wayland.shell: legacy module for wl_shell
This commit is contained in:
parent
12e13f863b
commit
048dda59e3
|
@ -125,7 +125,7 @@ fn main() {
|
|||
|
||||
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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
use super::WindowMap;
|
||||
use super::{SurfaceKind, WindowMap};
|
||||
use glium::texture::Texture2d;
|
||||
use rand;
|
||||
use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format};
|
||||
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
|
||||
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent};
|
||||
use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState, ToplevelConfigure,
|
||||
XdgRequest, XdgSurfaceRole};
|
||||
use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState as XdgShellState,
|
||||
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 std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
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)]
|
||||
pub struct SurfaceData {
|
||||
|
@ -97,7 +99,7 @@ fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
|
|||
}
|
||||
|
||||
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(
|
||||
display: &mut Display,
|
||||
|
@ -106,7 +108,8 @@ pub fn init_shell(
|
|||
egl_display: Rc<RefCell<Option<EGLDisplay>>>,
|
||||
) -> (
|
||||
CompositorToken<SurfaceData, Roles>,
|
||||
Arc<Mutex<ShellState<SurfaceData, Roles, ()>>>,
|
||||
Arc<Mutex<XdgShellState<SurfaceData, Roles, ()>>>,
|
||||
Arc<Mutex<WlShellState<SurfaceData, Roles, ()>>>,
|
||||
Rc<RefCell<MyWindowMap>>,
|
||||
) {
|
||||
// Create the compositor
|
||||
|
@ -125,16 +128,16 @@ pub fn init_shell(
|
|||
);
|
||||
|
||||
// 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,
|
||||
get_size as _,
|
||||
)));
|
||||
|
||||
// init the xdg_shell
|
||||
let shell_window_map = window_map.clone();
|
||||
let (shell_state, _, _) = xdg_shell_init(
|
||||
let xdg_window_map = window_map.clone();
|
||||
let (xdg_shell_state, _, _) = xdg_shell_init(
|
||||
display,
|
||||
looptoken,
|
||||
looptoken.clone(),
|
||||
compositor_token.clone(),
|
||||
move |shell_event, ()| match shell_event {
|
||||
XdgRequest::NewToplevel { surface } => {
|
||||
|
@ -149,7 +152,9 @@ pub fn init_shell(
|
|||
states: vec![],
|
||||
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 {
|
||||
size: (10, 10),
|
||||
|
@ -161,5 +166,37 @@ pub fn init_shell(
|
|||
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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@ mod window_map;
|
|||
|
||||
pub use self::glium::GliumDrawer;
|
||||
pub use self::implementations::*;
|
||||
pub use self::window_map::WindowMap;
|
||||
pub use self::window_map::{Kind as SurfaceKind, WindowMap};
|
||||
|
|
|
@ -2,20 +2,48 @@ use smithay::utils::Rectangle;
|
|||
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction};
|
||||
use smithay::wayland::compositor::roles::Role;
|
||||
use smithay::wayland::shell::xdg::{ToplevelSurface, XdgSurfaceRole};
|
||||
use smithay::wayland::shell::legacy::{ShellSurface, ShellSurfaceRole};
|
||||
use wayland_server::Resource;
|
||||
use wayland_server::protocol::wl_surface;
|
||||
|
||||
struct Window<U, R, SD> {
|
||||
location: (i32, i32),
|
||||
surface: Rectangle,
|
||||
toplevel: ToplevelSurface<U, R, SD>,
|
||||
pub enum Kind<U, R, SD, D> {
|
||||
Xdg(ToplevelSurface<U, R, SD>),
|
||||
Wl(ShellSurface<U, R, D>),
|
||||
}
|
||||
|
||||
impl<U, R, SD> Window<U, R, SD>
|
||||
impl<U, R, SD, D> Kind<U, R, SD, D>
|
||||
where
|
||||
U: 'static,
|
||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + '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
|
||||
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>,
|
||||
windows: Vec<Window<U, R, SD>>,
|
||||
windows: Vec<Window<U, R, SD, D>>,
|
||||
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
|
||||
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
|
||||
U: 'static,
|
||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + 'static,
|
||||
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + '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 {
|
||||
ctoken: ctoken,
|
||||
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 {
|
||||
location: location,
|
||||
surface: Rectangle {
|
||||
|
@ -181,7 +210,7 @@ where
|
|||
|
||||
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
|
||||
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() {
|
||||
f(&w.toplevel, w.location)
|
||||
|
|
|
@ -286,7 +286,7 @@ fn main() {
|
|||
log.clone(),
|
||||
);
|
||||
|
||||
let (compositor_token, _shell_state_token, window_map) = init_shell(
|
||||
let (compositor_token, _, _, window_map) = init_shell(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
log.clone(),
|
||||
|
|
|
@ -195,7 +195,7 @@ fn main() {
|
|||
|
||||
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);
|
||||
|
||||
let (mut seat, _) = Seat::new(
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// 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(<oken2, 1, move |_version, shell| {
|
||||
self::wl_handlers::implement_shell(
|
||||
shell,
|
||||
ltoken.clone(),
|
||||
ctoken.clone(),
|
||||
implementation.clone(),
|
||||
state2.clone(),
|
||||
);
|
||||
});
|
||||
|
||||
(state, global)
|
||||
}
|
||||
|
|
|
@ -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(_, _)>,
|
||||
<oken2,
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
}),
|
||||
<oken,
|
||||
);
|
||||
shell_surface.set_user_data(Box::into_raw(Box::new(ShellSurfaceUserData { surface, state })) as *mut ());
|
||||
shell_surface
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
//!
|
||||
//! ### 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
|
||||
//! instanciation of the `CompositorHandler` provided by smithay.
|
||||
//! instanciation of the compositor global provided by smithay.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # extern crate wayland_server;
|
||||
|
|
Loading…
Reference in New Issue