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());
|
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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(<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
|
//! ### 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;
|
||||||
|
|
Loading…
Reference in New Issue