wayland.shell: legacy module for wl_shell

This commit is contained in:
Victor Berger 2018-04-23 11:40:41 +02:00
parent 12e13f863b
commit 048dda59e3
9 changed files with 684 additions and 32 deletions

View File

@ -125,7 +125,7 @@ fn main() {
init_shm_global(&mut display, event_loop.token(), vec![], log.clone()); init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) = let (compositor_token, _, _, window_map) =
init_shell(&mut display, event_loop.token(), log.clone(), egl_display); init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
/* /*

View File

@ -1,19 +1,21 @@
use super::WindowMap; use super::{SurfaceKind, WindowMap};
use glium::texture::Texture2d; use glium::texture::Texture2d;
use rand; use rand;
use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format}; use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format};
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages}; use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent}; use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent};
use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState, ToplevelConfigure, use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState as XdgShellState,
XdgRequest, XdgSurfaceRole}; ToplevelConfigure, XdgRequest, XdgSurfaceRole};
use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState,
ShellSurfaceKind, ShellSurfaceRole};
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents; use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use wayland_server::{Display, LoopToken, Resource}; use wayland_server::{Display, LoopToken, Resource};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_surface}; use wayland_server::protocol::{wl_buffer, wl_callback, wl_shell_surface, wl_surface};
define_roles!(Roles => [ ShellSurface, XdgSurfaceRole ] ); define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] [ ShellSurface, ShellSurfaceRole<()>] );
#[derive(Default)] #[derive(Default)]
pub struct SurfaceData { pub struct SurfaceData {
@ -97,7 +99,7 @@ fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
} }
pub type MyWindowMap = pub type MyWindowMap =
WindowMap<SurfaceData, Roles, (), fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>>; WindowMap<SurfaceData, Roles, (), (), fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>>;
pub fn init_shell( pub fn init_shell(
display: &mut Display, display: &mut Display,
@ -106,7 +108,8 @@ pub fn init_shell(
egl_display: Rc<RefCell<Option<EGLDisplay>>>, egl_display: Rc<RefCell<Option<EGLDisplay>>>,
) -> ( ) -> (
CompositorToken<SurfaceData, Roles>, CompositorToken<SurfaceData, Roles>,
Arc<Mutex<ShellState<SurfaceData, Roles, ()>>>, Arc<Mutex<XdgShellState<SurfaceData, Roles, ()>>>,
Arc<Mutex<WlShellState<SurfaceData, Roles, ()>>>,
Rc<RefCell<MyWindowMap>>, Rc<RefCell<MyWindowMap>>,
) { ) {
// Create the compositor // Create the compositor
@ -125,16 +128,16 @@ pub fn init_shell(
); );
// Init a window map, to track the location of our windows // Init a window map, to track the location of our windows
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, (), _>::new( let window_map = Rc::new(RefCell::new(WindowMap::<_, _, (), (), _>::new(
compositor_token, compositor_token,
get_size as _, get_size as _,
))); )));
// init the xdg_shell // init the xdg_shell
let shell_window_map = window_map.clone(); let xdg_window_map = window_map.clone();
let (shell_state, _, _) = xdg_shell_init( let (xdg_shell_state, _, _) = xdg_shell_init(
display, display,
looptoken, looptoken.clone(),
compositor_token.clone(), compositor_token.clone(),
move |shell_event, ()| match shell_event { move |shell_event, ()| match shell_event {
XdgRequest::NewToplevel { surface } => { XdgRequest::NewToplevel { surface } => {
@ -149,7 +152,9 @@ pub fn init_shell(
states: vec![], states: vec![],
serial: 42, serial: 42,
}); });
shell_window_map.borrow_mut().insert(surface, (x, y)); xdg_window_map
.borrow_mut()
.insert(SurfaceKind::Xdg(surface), (x, y));
} }
XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure { XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
size: (10, 10), size: (10, 10),
@ -161,5 +166,37 @@ pub fn init_shell(
log.clone(), log.clone(),
); );
(compositor_token, shell_state, window_map) // init the wl_shell
let shell_window_map = window_map.clone();
let (wl_shell_state, _) = wl_shell_init(
display,
looptoken,
compositor_token.clone(),
move |req: ShellRequest<_, _, ()>, ()| match req {
ShellRequest::SetKind {
surface,
kind: ShellSurfaceKind::Toplevel,
} => {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
surface.send_configure((0, 0), wl_shell_surface::Resize::None);
shell_window_map
.borrow_mut()
.insert(SurfaceKind::Wl(surface), (x, y));
}
_ => (),
},
log.clone(),
);
(
compositor_token,
xdg_shell_state,
wl_shell_state,
window_map,
)
} }

View File

@ -4,4 +4,4 @@ mod window_map;
pub use self::glium::GliumDrawer; pub use self::glium::GliumDrawer;
pub use self::implementations::*; pub use self::implementations::*;
pub use self::window_map::WindowMap; pub use self::window_map::{Kind as SurfaceKind, WindowMap};

View File

@ -2,20 +2,48 @@ use smithay::utils::Rectangle;
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction};
use smithay::wayland::compositor::roles::Role; use smithay::wayland::compositor::roles::Role;
use smithay::wayland::shell::xdg::{ToplevelSurface, XdgSurfaceRole}; use smithay::wayland::shell::xdg::{ToplevelSurface, XdgSurfaceRole};
use smithay::wayland::shell::legacy::{ShellSurface, ShellSurfaceRole};
use wayland_server::Resource; use wayland_server::Resource;
use wayland_server::protocol::wl_surface; use wayland_server::protocol::wl_surface;
struct Window<U, R, SD> { pub enum Kind<U, R, SD, D> {
location: (i32, i32), Xdg(ToplevelSurface<U, R, SD>),
surface: Rectangle, Wl(ShellSurface<U, R, D>),
toplevel: ToplevelSurface<U, R, SD>,
} }
impl<U, R, SD> Window<U, R, SD> impl<U, R, SD, D> Kind<U, R, SD, D>
where where
U: 'static, U: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + 'static, R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static, SD: 'static,
D: 'static,
{
pub fn alive(&self) -> bool {
match *self {
Kind::Xdg(ref t) => t.alive(),
Kind::Wl(ref t) => t.alive(),
}
}
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
match *self {
Kind::Xdg(ref t) => t.get_surface(),
Kind::Wl(ref t) => t.get_surface(),
}
}
}
struct Window<U, R, SD, D> {
location: (i32, i32),
surface: Rectangle,
toplevel: Kind<U, R, SD, D>,
}
impl<U, R, SD, D> Window<U, R, SD, D>
where
U: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static,
D: 'static,
{ {
// Find the topmost surface under this point if any and the location of this point in the surface // Find the topmost surface under this point if any and the location of this point in the surface
fn matching<F>( fn matching<F>(
@ -111,20 +139,21 @@ where
} }
} }
pub struct WindowMap<U, R, SD, F> { pub struct WindowMap<U, R, SD, D, F> {
ctoken: CompositorToken<U, R>, ctoken: CompositorToken<U, R>,
windows: Vec<Window<U, R, SD>>, windows: Vec<Window<U, R, SD, D>>,
get_size: F, get_size: F,
} }
impl<U, R, SD, F> WindowMap<U, R, SD, F> impl<U, R, SD, D, F> WindowMap<U, R, SD, D, F>
where where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>, F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
U: 'static, U: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + 'static, R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static, SD: 'static,
D: 'static,
{ {
pub fn new(ctoken: CompositorToken<U, R>, get_size: F) -> WindowMap<U, R, SD, F> { pub fn new(ctoken: CompositorToken<U, R>, get_size: F) -> WindowMap<U, R, D, SD, F> {
WindowMap { WindowMap {
ctoken: ctoken, ctoken: ctoken,
windows: Vec::new(), windows: Vec::new(),
@ -132,7 +161,7 @@ where
} }
} }
pub fn insert(&mut self, toplevel: ToplevelSurface<U, R, SD>, location: (i32, i32)) { pub fn insert(&mut self, toplevel: Kind<U, R, SD, D>, location: (i32, i32)) {
let mut window = Window { let mut window = Window {
location: location, location: location,
surface: Rectangle { surface: Rectangle {
@ -181,7 +210,7 @@ where
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func) pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
where where
Func: FnMut(&ToplevelSurface<U, R, SD>, (i32, i32)), Func: FnMut(&Kind<U, R, SD, D>, (i32, i32)),
{ {
for w in self.windows.iter().rev() { for w in self.windows.iter().rev() {
f(&w.toplevel, w.location) f(&w.toplevel, w.location)

View File

@ -286,7 +286,7 @@ fn main() {
log.clone(), log.clone(),
); );
let (compositor_token, _shell_state_token, window_map) = init_shell( let (compositor_token, _, _, window_map) = init_shell(
&mut display.borrow_mut(), &mut display.borrow_mut(),
event_loop.token(), event_loop.token(),
log.clone(), log.clone(),

View File

@ -195,7 +195,7 @@ fn main() {
init_shm_global(&mut display, event_loop.token(), vec![], log.clone()); init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) = let (compositor_token, _, _, window_map) =
init_shell(&mut display, event_loop.token(), log.clone(), egl_display); init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
let (mut seat, _) = Seat::new( let (mut seat, _) = Seat::new(

View File

@ -1 +1,355 @@
//! Utilities for handling shell surfaces with the `wl_shell` protocol
//!
//! This module provides automatic handling of shell surfaces objects, by being registered
//! as a global handler for `wl_shell`. This protocol is deprecated in favor of `xdg_shell`,
//! thus this module is provided as a compatibility layer with older clients. As a consequence,
//! you can as a compositor-writer decide to only support its fonctionnality in a best-effort
//! maneer: as this global is part of the core protocol, you are still required to provide
//! some support for it.
//!
//! ## Why use this implementation
//!
//! This implementation can track for you the various shell surfaces defined by the
//! clients by handling the `wl_shell` protocol.
//!
//! 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.
//!
//! This handler only handles the protocol exchanges with the client to present you the
//! information in a coherent and relatively easy to use maneer. All the actual drawing
//! and positioning logic of windows is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simple use the `wl_shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the compositor handler provided by smithay.
//!
//! ```no_run
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols;
//! #
//! use smithay::wayland::compositor::roles::*;
//! use smithay::wayland::compositor::CompositorToken;
//! use smithay::wayland::shell::legacy::{wl_shell_init, ShellSurfaceRole, ShellRequest};
//! use wayland_server::{EventLoop, LoopToken};
//! # use wayland_server::protocol::{wl_seat, wl_output};
//! # #[derive(Default)] struct MySurfaceData;
//!
//! // define the metadata you want associated with the shell surfaces
//! #[derive(Default)]
//! pub struct MyShellSurfaceData {
//! /* ... */
//! }
//!
//! // define the roles type. You need to integrate the XdgSurface role:
//! define_roles!(MyRoles =>
//! [ShellSurface, ShellSurfaceRole<MyShellSurfaceData>]
//! );
//!
//! # fn main() {
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<(), MyRoles, _, _>(
//! # &mut display,
//! # event_loop.token(),
//! # |_, _| {},
//! # None
//! # );
//! let (shell_state, _) = wl_shell_init(
//! &mut display,
//! event_loop.token(),
//! // token from the compositor implementation
//! compositor_token,
//! // your implementation, can also be a strucy implementing the
//! // appropriate Implementation<(), ShellRequest<_, _, _>> trait
//! |event: ShellRequest<_, _, MyShellSurfaceData>, ()| { /* ... */ },
//! None // put a logger if you want
//! );
//!
//! // You're now ready to go!
//! # }
//! ```
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use wayland_server::{Display, Global, LoopToken, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
mod wl_handlers; mod wl_handlers;
/// Metadata associated with the `xdg_surface` role
pub struct ShellSurfaceRole<D: 'static> {
/// Title of the surface
pub title: String,
/// Class of the surface
pub class: String,
pending_ping: u32,
/// Some user data you may want to associate with the surface
pub user_data: D,
}
/// A handle to a shell surface
pub struct ShellSurface<U, R, D> {
wl_surface: Resource<wl_surface::WlSurface>,
shell_surface: Resource<wl_shell_surface::WlShellSurface>,
token: CompositorToken<U, R>,
_d: ::std::marker::PhantomData<D>,
}
impl<U, R, D> ShellSurface<U, R, D>
where
U: 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
D: 'static,
{
/// Is the shell surface refered by this handle still alive?
pub fn alive(&self) -> bool {
self.shell_surface.is_alive() && self.wl_surface.is_alive()
}
/// Do this handle and the other one actually refer to the same shell surface?
pub fn equals(&self, other: &Self) -> bool {
self.shell_surface.equals(&other.shell_surface)
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Send a ping request to this shell surface
///
/// You'll receive the reply as a `ShellRequest::Pong` request
///
/// A typical use is to start a timer at the same time you send this ping
/// request, and cancel it when you receive the pong. If the timer runs
/// down to 0 before a pong is received, mark the client as unresponsive.
///
/// Fails if this shell client already has a pending ping or is already dead.
pub fn send_ping(&self, serial: u32) -> Result<(), ()> {
if !self.alive() {
return Err(());
}
let ret = self.token.with_role_data(&self.wl_surface, |data| {
if data.pending_ping == 0 {
data.pending_ping = serial;
true
} else {
false
}
});
if let Ok(true) = ret {
self.shell_surface
.send(wl_shell_surface::Event::Ping { serial });
Ok(())
} else {
Err(())
}
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
pub fn send_configure(&self, size: (u32, u32), edges: wl_shell_surface::Resize) {
self.shell_surface.send(wl_shell_surface::Event::Configure {
edges,
width: size.0 as i32,
height: size.1 as i32,
})
}
/// Signal a popup surface that it has lost focus
pub fn send_popup_done(&self) {
self.shell_surface.send(wl_shell_surface::Event::PopupDone)
}
/// Access the user data you associated to this surface
pub fn with_user_data<F, T>(&self, f: F) -> Result<T, ()>
where
F: FnOnce(&mut D) -> T,
{
self.token
.with_role_data(&self.wl_surface, |data| f(&mut data.user_data))
.map_err(|_| ())
}
}
/// Possible kinds of shell surface of the `wl_shell` protocol
pub enum ShellSurfaceKind {
/// Toplevel, a regular window displayed somewhere in the compositor space
Toplevel,
/// Transient, this surface has a parent surface
///
/// These are sub-windows of an application (for example a configuration window),
/// and as such should only be visible in their parent window is, and on top of it.
Transient {
/// The surface considered as parent
parent: Resource<wl_surface::WlSurface>,
/// Location relative to the parent
location: (i32, i32),
/// Wether this window should be marked as inactive
inactive: bool,
},
/// Fullscreen surface, covering an entire output
Fullscreen {
/// Method used for fullscreen
method: wl_shell_surface::FullscreenMethod,
/// Framerate (relevant only for driver fullscreen)
framerate: u32,
/// Requested output if any
output: Option<Resource<wl_output::WlOutput>>,
},
/// A popup surface
///
/// Short-lived surface, typically refrered as "tooltips" in many
/// contexts.
Popup {
/// The parent surface of this popup
parent: Resource<wl_surface::WlSurface>,
/// The serial of the input event triggering the creation of this
/// popup
serial: u32,
/// Wether this popup should be marked as inactive
inactive: bool,
/// Location of the popup relative to its parent
location: (i32, i32),
/// Seat associated this the input that triggered the creation of the
/// popup. Used to define when the "popup done" event is sent.
seat: Resource<wl_seat::WlSeat>,
},
/// A maximized surface
///
/// Like a toplevel surface, but as big as possible on a single output
/// while keeping any relevant desktop-environment interface visible.
Maximized {
/// Requested output for maximization
output: Option<Resource<wl_output::WlOutput>>,
},
}
/// A request triggered by a wl_shell_surface
pub enum ShellRequest<U, R, D> {
/// A new shell surface was created
///
/// by default it has no kind and this should not be displayed
NewShellSurface {
/// The created surface
surface: ShellSurface<U, R, D>,
},
/// A pong event
///
/// The surface responded to its pending ping. If you receive this
/// event, smithay has already checked that the responded serial was valid.
Pong {
/// The surface that sent the pong
surface: ShellSurface<U, R, D>,
},
/// Start of an interactive move
///
/// The surface requests that an interactive move is started on it
Move {
/// The surface requesting the move
surface: ShellSurface<U, R, D>,
/// Serial of the implicit grab that initiated the move
serial: u32,
/// Seat associated with the move
seat: Resource<wl_seat::WlSeat>,
},
/// Start of an interactive resize
///
/// The surface requests that an interactive resize is started on it
Resize {
/// The surface requesting the resize
surface: ShellSurface<U, R, D>,
/// Serial of the implicit grab that initiated the resize
serial: u32,
/// Seat associated with the resize
seat: Resource<wl_seat::WlSeat>,
/// Direction of the resize
edges: wl_shell_surface::Resize,
},
/// The surface changed its kind
SetKind {
/// The surface
surface: ShellSurface<U, R, D>,
/// Its new kind
kind: ShellSurfaceKind,
},
}
/// Shell global state
///
/// This state allows you to retrieve a list of surfaces
/// currently known to the shell global.
pub struct ShellState<U, R, D> {
known_surfaces: Vec<ShellSurface<U, R, D>>,
}
impl<U, R, D> ShellState<U, R, D>
where
U: 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
D: 'static,
{
/// Cleans the internal surface storage by removing all dead surfaces
pub(crate) fn cleanup_surfaces(&mut self) {
self.known_surfaces.retain(|s| s.alive());
}
/// Access all the shell surfaces known by this handler
pub fn surfaces(&self) -> &[ShellSurface<U, R, D>] {
&self.known_surfaces[..]
}
}
/// Create a new `wl_shell` global
pub fn wl_shell_init<U, R, D, L, Impl>(
display: &mut Display,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
implementation: Impl,
logger: L,
) -> (Arc<Mutex<ShellState<U, R, D>>>, Global<wl_shell::WlShell>)
where
U: 'static,
D: Default + 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
L: Into<Option<::slog::Logger>>,
Impl: Implementation<(), ShellRequest<U, R, D>>,
{
let _log = ::slog_or_stdlog(logger);
let implementation = Rc::new(RefCell::new(implementation));
let ltoken2 = ltoken.clone();
let state = Arc::new(Mutex::new(ShellState {
known_surfaces: Vec::new(),
}));
let state2 = state.clone();
let global = display.create_global(&ltoken2, 1, move |_version, shell| {
self::wl_handlers::implement_shell(
shell,
ltoken.clone(),
ctoken.clone(),
implementation.clone(),
state2.clone(),
);
});
(state, global)
}

View File

@ -1 +1,233 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface};
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use super::{ShellRequest, ShellState, ShellSurface, ShellSurfaceKind, ShellSurfaceRole};
pub(crate) fn implement_shell<U, R, D, Impl>(
shell: NewResource<wl_shell::WlShell>,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
implementation: Rc<RefCell<Impl>>,
state: Arc<Mutex<ShellState<U, R, D>>>,
) where
U: 'static,
D: Default + 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
Impl: Implementation<(), ShellRequest<U, R, D>> + 'static,
{
let ltoken2 = ltoken.clone();
shell.implement_nonsend(
move |req, shell: Resource<_>| {
let wl_shell::Request::GetShellSurface { id, surface } = req;
let role_data = ShellSurfaceRole {
title: "".into(),
class: "".into(),
pending_ping: 0,
user_data: Default::default(),
};
if ctoken.give_role_with(&surface, role_data).is_err() {
shell.post_error(
wl_shell::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
let shell_surface = implement_shell_surface(
id,
surface,
implementation.clone(),
ltoken.clone(),
ctoken.clone(),
state.clone(),
);
state
.lock()
.unwrap()
.known_surfaces
.push(make_handle(&shell_surface, ctoken.clone()));
implementation.borrow_mut().receive(
ShellRequest::NewShellSurface {
surface: make_handle(&shell_surface, ctoken.clone()),
},
(),
);
},
None::<fn(_, _)>,
&ltoken2,
);
}
fn make_handle<U, R, D>(
shell_surface: &Resource<wl_shell_surface::WlShellSurface>,
token: CompositorToken<U, R>,
) -> ShellSurface<U, R, D> {
let data = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData<U, R, D>) };
ShellSurface {
wl_surface: data.surface.clone(),
shell_surface: shell_surface.clone(),
token,
_d: ::std::marker::PhantomData,
}
}
pub(crate) struct ShellSurfaceUserData<U, R, D> {
surface: Resource<wl_surface::WlSurface>,
state: Arc<Mutex<ShellState<U, R, D>>>,
}
fn implement_shell_surface<U, R, Impl, D>(
shell_surface: NewResource<wl_shell_surface::WlShellSurface>,
surface: Resource<wl_surface::WlSurface>,
implementation: Rc<RefCell<Impl>>,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
state: Arc<Mutex<ShellState<U, R, D>>>,
) -> Resource<wl_shell_surface::WlShellSurface>
where
U: 'static,
D: 'static,
R: Role<ShellSurfaceRole<D>> + 'static,
Impl: Implementation<(), ShellRequest<U, R, D>> + 'static,
{
use self::wl_shell_surface::Request;
let shell_surface = shell_surface.implement_nonsend(
move |req, shell_surface: Resource<_>| {
let data = unsafe { &mut *(shell_surface.get_user_data() as *mut ShellSurfaceUserData<U, R, D>) };
let mut user_impl = implementation.borrow_mut();
match req {
Request::Pong { serial } => {
let valid = ctoken
.with_role_data(&data.surface, |data| {
if data.pending_ping == serial {
data.pending_ping = 0;
true
} else {
false
}
})
.expect("wl_shell_surface exists but surface has not the right role?");
if valid {
user_impl.receive(
ShellRequest::Pong {
surface: make_handle(&shell_surface, ctoken.clone()),
},
(),
);
}
}
Request::Move { seat, serial } => user_impl.receive(
ShellRequest::Move {
surface: make_handle(&shell_surface, ctoken.clone()),
serial,
seat,
},
(),
),
Request::Resize {
seat,
serial,
edges,
} => user_impl.receive(
ShellRequest::Resize {
surface: make_handle(&shell_surface, ctoken.clone()),
serial,
seat,
edges,
},
(),
),
Request::SetToplevel => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Toplevel,
},
(),
),
Request::SetTransient {
parent,
x,
y,
flags,
} => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Transient {
parent,
location: (x, y),
inactive: flags.contains(wl_shell_surface::Transient::Inactive),
},
},
(),
),
Request::SetFullscreen {
method,
framerate,
output,
} => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Fullscreen {
method,
framerate,
output,
},
},
(),
),
Request::SetPopup {
seat,
serial,
parent,
x,
y,
flags,
} => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Popup {
parent,
serial,
seat,
location: (x, y),
inactive: flags.contains(wl_shell_surface::Transient::Inactive),
},
},
(),
),
Request::SetMaximized { output } => user_impl.receive(
ShellRequest::SetKind {
surface: make_handle(&shell_surface, ctoken.clone()),
kind: ShellSurfaceKind::Maximized { output },
},
(),
),
Request::SetTitle { title } => {
ctoken
.with_role_data(&data.surface, |data| data.title = title)
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
}
Request::SetClass { class_ } => {
ctoken
.with_role_data(&data.surface, |data| data.class = class_)
.expect("wl_shell_surface exists but surface has not shell_surface role?!");
}
}
},
Some(|shell_surface: Resource<_>, _| {
let data =
unsafe { Box::from_raw(shell_surface.get_user_data() as *mut ShellSurfaceUserData<U, R, D>) };
data.state.lock().unwrap().cleanup_surfaces();
}),
&ltoken,
);
shell_surface.set_user_data(Box::into_raw(Box::new(ShellSurfaceUserData { surface, state })) as *mut ());
shell_surface
}

View File

@ -21,9 +21,9 @@
//! //!
//! ### Initialization //! ### Initialization
//! //!
//! To initialize this handler, simple use the `shell_init` function provided in this //! To initialize this handler, simple use the `xdg_shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an //! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the `CompositorHandler` provided by smithay. //! instanciation of the compositor global provided by smithay.
//! //!
//! ```no_run //! ```no_run
//! # extern crate wayland_server; //! # extern crate wayland_server;