Merge pull request #35 from vberger/master

Migrate to wayland-server 0.10
This commit is contained in:
Victor Berger 2017-09-20 15:37:35 +02:00 committed by GitHub
commit 24ea2a066a
16 changed files with 1795 additions and 1842 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
target target
Cargo.lock Cargo.lock
*.bk *.bk
.vscode

View File

@ -5,7 +5,7 @@ authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
license = "MIT" license = "MIT"
[dependencies] [dependencies]
wayland-server = "0.9.9" wayland-server = "0.10.1"
nix = "0.7.0" nix = "0.7.0"
xkbcommon = "0.2.1" xkbcommon = "0.2.1"
tempfile = "2.1.5" tempfile = "2.1.5"
@ -18,7 +18,7 @@ glium = { version = "0.16.0", optional = true, default-features = false }
input = { version = "0.2.0", optional = true } input = { version = "0.2.0", optional = true }
clippy = { version = "*", optional = true } clippy = { version = "*", optional = true }
rental = "0.4.11" rental = "0.4.11"
wayland-protocols = { version = "0.9.9", features = ["unstable_protocols", "server"] } wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] }
[build-dependencies] [build-dependencies]
gl_generator = "0.5" gl_generator = "0.5"

View File

@ -0,0 +1,95 @@
use rand;
use smithay::compositor::{CompositorToken, SurfaceUserImplementation};
use smithay::shell::{PopupConfigure, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure};
use smithay::shm::with_buffer_contents;
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
#[derive(Default)]
pub struct SurfaceData {
pub buffer: Option<(Vec<u8>, (u32, u32))>,
pub location: Option<(i32, i32)>,
}
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
SurfaceUserImplementation {
commit: |_, _, surface, token| {
// we retrieve the contents of the associated buffer and copy it
token.with_surface_data(surface, |attributes| {
match attributes.buffer.take() {
Some(Some((buffer, (_x, _y)))) => {
// we ignore hotspot coordinates in this simple example
with_buffer_contents(&buffer, |slice, data| {
let offset = data.offset as usize;
let stride = data.stride as usize;
let width = data.width as usize;
let height = data.height as usize;
let mut new_vec = Vec::with_capacity(width * height * 4);
for i in 0..height {
new_vec.extend(
&slice[(offset + i * stride)..(offset + i * stride + width * 4)],
);
}
attributes.user_data.buffer =
Some((new_vec, (data.width as u32, data.height as u32)));
}).unwrap();
buffer.release();
}
Some(None) => {
// erase the contents
attributes.user_data.buffer = None;
}
None => {}
}
});
},
frame: |_, _, _, callback, _| {
callback.done(0);
},
}
}
pub fn shell_implementation(
)
-> ShellSurfaceUserImplementation<SurfaceData, Roles, (), CompositorToken<SurfaceData, Roles, ()>, ()>
{
ShellSurfaceUserImplementation {
new_client: |_, _, _| {},
client_pong: |_, _, _| {},
new_toplevel: |_, token, toplevel| {
let wl_surface = toplevel.get_surface().unwrap();
token.with_surface_data(wl_surface, |data| {
// 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);
data.user_data.location = Some((x, y))
});
ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
}
},
new_popup: |_, _, _| {
PopupConfigure {
size: (10, 10),
position: (10, 10),
serial: 42,
}
},
move_: |_, _, _, _, _| {},
resize: |_, _, _, _, _, _| {},
grab: |_, _, _, _, _| {},
change_display_state: |_, _, _, _, _, _, _| {
ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
}
},
show_window_menu: |_, _, _, _, _, _, _| {},
}
}

View File

@ -1,3 +1,5 @@
mod glium; mod glium;
mod implementations;
pub use self::glium::GliumDrawer; pub use self::glium::GliumDrawer;
pub use self::implementations::*;

View File

@ -13,151 +13,15 @@ extern crate wayland_server;
mod helpers; mod helpers;
use glium::Surface; use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer};
use helpers::GliumDrawer;
use slog::{Drain, Logger}; use slog::{Drain, Logger};
use smithay::backend::graphics::glium::IntoGlium; use smithay::backend::graphics::glium::IntoGlium;
use smithay::backend::input::InputBackend; use smithay::backend::input::InputBackend;
use smithay::backend::winit; use smithay::backend::winit;
use smithay::compositor::{self, CompositorHandler, CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
use smithay::compositor::roles::Role; use smithay::compositor::roles::Role;
use smithay::shell::{self, PopupConfigure, PopupSurface, ShellClient, ShellHandler, ShellSurfaceRole, use smithay::shell::shell_init;
ToplevelConfigure, ToplevelSurface}; use smithay::shm::init_shm_global;
use smithay::shm::{ShmGlobal, ShmToken};
use wayland_protocols::unstable::xdg_shell::server::{zxdg_shell_v6, zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle};
use wayland_server::protocol::{wl_callback, wl_compositor, wl_output, wl_seat, wl_shell, wl_shm,
wl_subcompositor, wl_surface};
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
struct SurfaceHandler {
shm_token: ShmToken,
}
#[derive(Default)]
struct SurfaceData {
buffer: Option<(Vec<u8>, (u32, u32))>,
location: Option<(i32, i32)>,
}
impl compositor::Handler<SurfaceData, Roles> for SurfaceHandler {
fn commit(&mut self, _evlh: &mut EventLoopHandle, _client: &Client, surface: &wl_surface::WlSurface,
token: CompositorToken<SurfaceData, Roles, SurfaceHandler>) {
// we retrieve the contents of the associated buffer and copy it
token.with_surface_data(surface, |attributes| {
match attributes.buffer.take() {
Some(Some((buffer, (_x, _y)))) => {
// we ignore hotspot coordinates in this simple example
self.shm_token
.with_buffer_contents(&buffer, |slice, data| {
let offset = data.offset as usize;
let stride = data.stride as usize;
let width = data.width as usize;
let height = data.height as usize;
let mut new_vec = Vec::with_capacity(width * height * 4);
for i in 0..height {
new_vec.extend(
&slice[(offset + i * stride)..(offset + i * stride + width * 4)],
);
}
attributes.user_data.buffer =
Some((new_vec, (data.width as u32, data.height as u32)));
})
.unwrap();
buffer.release();
}
Some(None) => {
// erase the contents
attributes.user_data.buffer = None;
}
None => {}
}
});
}
fn frame(&mut self, _evlh: &mut EventLoopHandle, _client: &Client, surface: &wl_surface::WlSurface,
callback: wl_callback::WlCallback, _token: CompositorToken<SurfaceData, Roles, SurfaceHandler>) {
callback.done(0);
}
}
struct ShellSurfaceHandler {
token: CompositorToken<SurfaceData, Roles, SurfaceHandler>,
}
impl ShellSurfaceHandler {
fn new(token: CompositorToken<SurfaceData, Roles, SurfaceHandler>) -> ShellSurfaceHandler {
ShellSurfaceHandler { token }
}
}
impl shell::Handler<SurfaceData, Roles, SurfaceHandler, ()> for ShellSurfaceHandler {
fn new_client(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {}
fn client_pong(&mut self, _evlh: &mut EventLoopHandle, _client: ShellClient<()>) {}
fn new_toplevel(&mut self, _evlh: &mut EventLoopHandle,
surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>)
-> ToplevelConfigure {
let wl_surface = surface.get_surface().unwrap();
self.token.with_surface_data(wl_surface, |data| {
// 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);
data.user_data.location = Some((x, y))
});
ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
}
}
fn new_popup(&mut self, _evlh: &mut EventLoopHandle,
_surface: PopupSurface<SurfaceData, Roles, SurfaceHandler, ()>)
-> PopupConfigure {
PopupConfigure {
size: (10, 10),
position: (10, 10),
serial: 42,
}
}
fn move_(&mut self, _evlh: &mut EventLoopHandle,
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>, _seat: &wl_seat::WlSeat,
_serial: u32) {
}
fn resize(&mut self, _evlh: &mut EventLoopHandle,
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>, _seat: &wl_seat::WlSeat,
_serial: u32, _edges: zxdg_toplevel_v6::ResizeEdge) {
}
fn grab(&mut self, _evlh: &mut EventLoopHandle,
_surface: PopupSurface<SurfaceData, Roles, SurfaceHandler, ()>, _seat: &wl_seat::WlSeat,
_serial: u32) {
}
fn change_display_state(&mut self, _evlh: &mut EventLoopHandle,
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>,
_maximized: Option<bool>, _minimized: Option<bool>, _fullscreen: Option<bool>,
_output: Option<&wl_output::WlOutput>)
-> ToplevelConfigure {
ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
}
}
fn show_window_menu(&mut self, _evlh: &mut EventLoopHandle,
_surface: ToplevelSurface<SurfaceData, Roles, SurfaceHandler, ()>,
_seat: &wl_seat::WlSeat, _serial: u32, _x: i32, _y: i32) {
}
}
type MyCompositorHandler = CompositorHandler<SurfaceData, Roles, SurfaceHandler>;
type MyShellHandler = ShellHandler<SurfaceData, Roles, SurfaceHandler, ShellSurfaceHandler, ()>;
fn main() { fn main() {
// A logger facility, here we use the terminal for this example // A logger facility, here we use the terminal for this example
@ -172,51 +36,21 @@ fn main() {
let (mut display, mut event_loop) = wayland_server::create_display(); let (mut display, mut event_loop) = wayland_server::create_display();
/* /*
* Initialize wl_shm global * Initialize the globals
*/ */
// Insert the ShmGlobal as a handler to your event loop
// Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported.
let shm_handler_id = event_loop.add_handler_with_init(ShmGlobal::new(vec![], log.clone()));
// Register this handler to advertise a wl_shm global of version 1
event_loop.register_global::<wl_shm::WlShm, ShmGlobal>(shm_handler_id, 1);
// retreive the token
let shm_token = {
let state = event_loop.state();
state.get_handler::<ShmGlobal>(shm_handler_id).get_token()
};
init_shm_global(&mut event_loop, vec![], log.clone());
/* let (compositor_token, _, _) =
* Initialize the compositor global compositor_init(&mut event_loop, surface_implementation(), (), log.clone());
*/
let compositor_handler_id = event_loop.add_handler_with_init(MyCompositorHandler::new(
SurfaceHandler {
shm_token: shm_token.clone(),
},
log.clone(),
));
// register it to handle wl_compositor and wl_subcompositor
event_loop.register_global::<wl_compositor::WlCompositor, MyCompositorHandler>(compositor_handler_id, 4);
event_loop
.register_global::<wl_subcompositor::WlSubcompositor, MyCompositorHandler>(compositor_handler_id, 1);
// retrieve the tokens
let compositor_token = {
let state = event_loop.state();
state
.get_handler::<MyCompositorHandler>(compositor_handler_id)
.get_token()
};
/* let (shell_state_token, _, _) = shell_init(
* Initialize the shell global &mut event_loop,
*/ compositor_token,
let shell_handler_id = event_loop.add_handler_with_init(MyShellHandler::new( shell_implementation(),
ShellSurfaceHandler::new(compositor_token),
compositor_token, compositor_token,
log.clone(), log.clone(),
)); );
event_loop.register_global::<wl_shell::WlShell, MyShellHandler>(shell_handler_id, 1);
event_loop.register_global::<zxdg_shell_v6::ZxdgShellV6, MyShellHandler>(shell_handler_id, 1);
/* /*
* Initialize glium * Initialize glium
@ -240,10 +74,7 @@ fn main() {
{ {
let screen_dimensions = context.get_framebuffer_dimensions(); let screen_dimensions = context.get_framebuffer_dimensions();
let state = event_loop.state(); let state = event_loop.state();
for toplevel_surface in state for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() {
.get_handler::<MyShellHandler>(shell_handler_id)
.toplevel_surfaces()
{
if let Some(wl_surface) = toplevel_surface.get_surface() { if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn // this surface is a root of a subsurface tree that needs to be drawn
let initial_place = compositor_token let initial_place = compositor_token

View File

@ -1,28 +0,0 @@
use super::{CompositorHandler, Handler as UserHandler, Role, RoleType, SubsurfaceRole};
use wayland_server::{Client, EventLoopHandle, GlobalHandler};
use wayland_server::protocol::{wl_compositor, wl_subcompositor};
impl<U, R, H> GlobalHandler<wl_compositor::WlCompositor> for CompositorHandler<U, R, H>
where
U: Default + Send + 'static,
R: Default + Send + 'static,
H: UserHandler<U, R> + Send + 'static,
{
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_compositor::WlCompositor) {
debug!(self.log, "New compositor global binded.");
evlh.register::<_, CompositorHandler<U, R, H>>(&global, self.my_id);
}
}
impl<U, R, H> GlobalHandler<wl_subcompositor::WlSubcompositor> for CompositorHandler<U, R, H>
where
U: Send + 'static,
R: RoleType + Role<SubsurfaceRole> + Send + 'static,
H: Send + 'static,
{
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_subcompositor::WlSubcompositor) {
debug!(self.log, "New subcompositor global binded.");
evlh.register::<_, CompositorHandler<U, R, H>>(&global, self.my_id);
}
}

View File

@ -1,66 +1,101 @@
use super::{CompositorHandler, Damage, Handler as UserHandler, Rectangle, RectangleKind, Role, RoleType, use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole,
SubsurfaceRole}; SurfaceUserImplementation};
use super::region::RegionData; use super::region::RegionData;
use super::tree::{Location, SurfaceData}; use super::tree::{Location, SurfaceData};
use wayland_server::{Client, Destroy, EventLoopHandle, Liveness, Resource}; use std::cell::RefCell;
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, use std::rc::Rc;
wl_subcompositor, wl_subsurface, wl_surface}; use wayland_server::{Client, EventLoopHandle, Liveness, Resource};
use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface};
struct CompositorDestructor<U, R> {
_t: ::std::marker::PhantomData<U>,
_r: ::std::marker::PhantomData<R>,
}
/* /*
* wl_compositor * wl_compositor
*/ */
impl<U, R, H> wl_compositor::Handler for CompositorHandler<U, R, H> pub(crate) fn compositor_bind<U, R, ID>(evlh: &mut EventLoopHandle, idata: &mut SurfaceIData<U, R, ID>,
_: &Client, compositor: wl_compositor::WlCompositor)
where where
U: Default + Send + 'static, U: Default + 'static,
R: Default + Send + 'static, R: Default + 'static,
H: UserHandler<U, R> + Send + 'static, ID: 'static,
{ {
fn create_surface(&mut self, evqh: &mut EventLoopHandle, _: &Client, _: &wl_compositor::WlCompositor, trace!(idata.log, "Binding a new wl_compositor.");
id: wl_surface::WlSurface) { evlh.register(
trace!(self.log, "New surface created."); &compositor,
unsafe { SurfaceData::<U, R>::init(&id) }; compositor_implementation::<U, R, ID>(),
evqh.register_with_destructor::<_, CompositorHandler<U, R, H>, CompositorDestructor<U, R>>( idata.clone(),
&id, None,
self.my_id, );
);
}
fn create_region(&mut self, evqh: &mut EventLoopHandle, _: &Client, _: &wl_compositor::WlCompositor,
id: wl_region::WlRegion) {
trace!(self.log, "New region created.");
unsafe { RegionData::init(&id) };
evqh.register_with_destructor::<_, CompositorHandler<U, R, H>, CompositorDestructor<U, R>>(
&id,
self.my_id,
);
}
} }
server_declare_handler!(CompositorHandler<U: [Default, Send], R: [Default, Send], H: [UserHandler<U, R>, Send]>, wl_compositor::Handler, wl_compositor::WlCompositor); fn compositor_implementation<U, R, ID>() -> wl_compositor::Implementation<SurfaceIData<U, R, ID>>
where
U: Default + 'static,
R: Default + 'static,
ID: 'static,
{
wl_compositor::Implementation {
create_surface: |evlh, idata, _, _, surface| {
unsafe { SurfaceData::<U, R>::init(&surface) };
evlh.register(
&surface,
surface_implementation::<U, R, ID>(),
idata.clone(),
Some(destroy_surface::<U, R>),
);
},
create_region: |evlh, _, _, _, region| {
unsafe { RegionData::init(&region) };
evlh.register(&region, region_implementation(), (), Some(destroy_region));
},
}
}
/* /*
* wl_surface * wl_surface
*/ */
impl<U, R, H: UserHandler<U, R>> wl_surface::Handler for CompositorHandler<U, R, H> { /// Internal implementation data of surfaces
fn attach(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, ///
buffer: Option<&wl_buffer::WlBuffer>, x: i32, y: i32) { /// This type is only visible as type parameter of
trace!(self.log, "Attaching buffer to surface."); /// the `Global` handle you are provided.
unsafe { pub struct SurfaceIData<U, R, ID> {
log: ::slog::Logger,
implem: SurfaceUserImplementation<U, R, ID>,
idata: Rc<RefCell<ID>>,
}
impl<U, R, ID> SurfaceIData<U, R, ID> {
pub(crate) fn make(log: ::slog::Logger, implem: SurfaceUserImplementation<U, R, ID>, idata: ID)
-> SurfaceIData<U, R, ID> {
SurfaceIData {
log: log,
implem: implem,
idata: Rc::new(RefCell::new(idata)),
}
}
}
impl<U, R, ID> Clone for SurfaceIData<U, R, ID> {
fn clone(&self) -> SurfaceIData<U, R, ID> {
SurfaceIData {
log: self.log.clone(),
implem: self.implem.clone(),
idata: self.idata.clone(),
}
}
}
pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>(
)
-> wl_surface::Implementation<SurfaceIData<U, R, ID>>
{
wl_surface::Implementation {
attach: |_, _, _, surface, buffer, x, y| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| { SurfaceData::<U, R>::with_data(surface, |d| {
d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y)))) d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y))))
}); });
} },
} damage: |_, _, _, surface, x, y, width, height| unsafe {
fn damage(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, x: i32,
y: i32, width: i32, height: i32) {
trace!(self.log, "Registering damage to surface.");
unsafe {
SurfaceData::<U, R>::with_data(surface, |d| { SurfaceData::<U, R>::with_data(surface, |d| {
d.damage = Damage::Surface(Rectangle { d.damage = Damage::Surface(Rectangle {
x, x,
@ -69,56 +104,38 @@ impl<U, R, H: UserHandler<U, R>> wl_surface::Handler for CompositorHandler<U, R,
height, height,
}) })
}); });
} },
} frame: |evlh, idata, _, surface, callback| {
fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, let mut user_idata = idata.idata.borrow_mut();
callback: wl_callback::WlCallback) { trace!(idata.log, "Calling user callback for wl_surface.frame");
trace!(self.log, "Frame surface callback."); (idata.implem.frame)(
let token = self.get_token(); evlh,
UserHandler::frame(&mut self.handler, evlh, client, surface, callback, token); &mut *user_idata,
} surface,
fn set_opaque_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface, callback,
region: Option<&wl_region::WlRegion>) { CompositorToken::make(),
trace!(self.log, "Setting surface opaque region."); )
unsafe { },
set_opaque_region: |_, _, _, surface, region| unsafe {
let attributes = region.map(|r| RegionData::get_attributes(r)); let attributes = region.map(|r| RegionData::get_attributes(r));
SurfaceData::<U, R>::with_data(surface, |d| d.opaque_region = attributes); SurfaceData::<U, R>::with_data(surface, |d| d.opaque_region = attributes);
} },
} set_input_region: |_, _, _, surface, region| unsafe {
fn set_input_region(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface,
region: Option<&wl_region::WlRegion>) {
trace!(self.log, "Setting surface input region.");
unsafe {
let attributes = region.map(|r| RegionData::get_attributes(r)); let attributes = region.map(|r| RegionData::get_attributes(r));
SurfaceData::<U, R>::with_data(surface, |d| d.input_region = attributes); SurfaceData::<U, R>::with_data(surface, |d| d.input_region = attributes);
} },
} commit: |evlh, idata, _, surface| {
fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface) { let mut user_idata = idata.idata.borrow_mut();
trace!(self.log, "Commit surface callback."); trace!(idata.log, "Calling user callback for wl_surface.commit");
let token = self.get_token(); (idata.implem.commit)(evlh, &mut *user_idata, surface, CompositorToken::make())
UserHandler::commit(&mut self.handler, evlh, client, surface, token); },
} set_buffer_transform: |_, _, _, surface, transform| unsafe {
fn set_buffer_transform(&mut self, _: &mut EventLoopHandle, _: &Client,
surface: &wl_surface::WlSurface, transform: wl_output::Transform) {
trace!(self.log, "Setting surface's buffer transform.");
unsafe {
SurfaceData::<U, R>::with_data(surface, |d| d.buffer_transform = transform); SurfaceData::<U, R>::with_data(surface, |d| d.buffer_transform = transform);
} },
} set_buffer_scale: |_, _, _, surface, scale| unsafe {
fn set_buffer_scale(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface,
scale: i32) {
trace!(self.log, "Setting surface's buffer scale.");
unsafe {
SurfaceData::<U, R>::with_data(surface, |d| d.buffer_scale = scale); SurfaceData::<U, R>::with_data(surface, |d| d.buffer_scale = scale);
} },
} damage_buffer: |_, _, _, surface, x, y, width, height| unsafe {
fn damage_buffer(&mut self, _: &mut EventLoopHandle, _: &Client, surface: &wl_surface::WlSurface,
x: i32, y: i32, width: i32, height: i32) {
trace!(
self.log,
"Registering damage to surface (buffer coordinates)."
);
unsafe {
SurfaceData::<U, R>::with_data(surface, |d| { SurfaceData::<U, R>::with_data(surface, |d| {
d.damage = Damage::Buffer(Rectangle { d.damage = Damage::Buffer(Rectangle {
x, x,
@ -127,97 +144,102 @@ impl<U, R, H: UserHandler<U, R>> wl_surface::Handler for CompositorHandler<U, R,
height, height,
}) })
}); });
} },
destroy: |_, _, _, _| {},
} }
} }
server_declare_handler!(CompositorHandler<U:[], R: [], H: [UserHandler<U, R>]>, wl_surface::Handler, wl_surface::WlSurface); fn destroy_surface<U: 'static, R: 'static>(surface: &wl_surface::WlSurface) {
unsafe { SurfaceData::<U, R>::cleanup(surface) }
impl<U, R> Destroy<wl_surface::WlSurface> for CompositorDestructor<U, R> {
fn destroy(surface: &wl_surface::WlSurface) {
unsafe { SurfaceData::<U, R>::cleanup(surface) }
}
} }
/* /*
* wl_region * wl_region
*/ */
impl<U, R, H> wl_region::Handler for CompositorHandler<U, R, H> { pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
fn add(&mut self, _: &mut EventLoopHandle, _: &Client, region: &wl_region::WlRegion, x: i32, y: i32, wl_region::Implementation {
width: i32, height: i32) { add: |_, _, _, region, x, y, width, height| {
trace!(self.log, "Adding rectangle to a region."); unsafe {
unsafe { RegionData::add_rectangle(
RegionData::add_rectangle( region,
region, RectangleKind::Add,
RectangleKind::Add, Rectangle {
Rectangle { x,
x, y,
y, width,
width, height,
height, },
}, )
) };
}; },
} subtract: |_, _, _, region, x, y, width, height| {
fn subtract(&mut self, _: &mut EventLoopHandle, _: &Client, region: &wl_region::WlRegion, x: i32, unsafe {
y: i32, width: i32, height: i32) { RegionData::add_rectangle(
trace!(self.log, "Subtracting rectangle to a region."); region,
unsafe { RectangleKind::Subtract,
RegionData::add_rectangle( Rectangle {
region, x,
RectangleKind::Subtract, y,
Rectangle { width,
x, height,
y, },
width, )
height, };
}, },
) destroy: |_, _, _, _| {},
};
} }
} }
server_declare_handler!(CompositorHandler<U: [], R: [], H: []>, wl_region::Handler, wl_region::WlRegion); fn destroy_region(region: &wl_region::WlRegion) {
unsafe { RegionData::cleanup(region) };
impl<U, R> Destroy<wl_region::WlRegion> for CompositorDestructor<U, R> {
fn destroy(region: &wl_region::WlRegion) {
unsafe { RegionData::cleanup(region) };
}
} }
/* /*
* wl_subcompositor * wl_subcompositor
*/ */
impl<U, R, H> wl_subcompositor::Handler for CompositorHandler<U, R, H> pub(crate) fn subcompositor_bind<U, R>(evlh: &mut EventLoopHandle, _: &mut (), _: &Client,
subcompositor: wl_subcompositor::WlSubcompositor)
where where
U: Send + 'static, R: RoleType + Role<SubsurfaceRole> + 'static,
R: RoleType + Role<SubsurfaceRole> + Send + 'static, U: 'static,
H: Send + 'static,
{ {
fn get_subsurface(&mut self, evqh: &mut EventLoopHandle, _: &Client, evlh.register(
resource: &wl_subcompositor::WlSubcompositor, id: wl_subsurface::WlSubsurface, &subcompositor,
surface: &wl_surface::WlSurface, parent: &wl_surface::WlSurface) { subcompositor_implementation::<U, R>(),
trace!(self.log, "Creating new subsurface."); (),
if let Err(()) = unsafe { SurfaceData::<U, R>::set_parent(surface, parent) } { None,
resource.post_error( );
wl_subcompositor::Error::BadSurface as u32,
"Surface already has a role.".into(),
);
return;
}
id.set_user_data(
Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _,
);
evqh.register_with_destructor::<_, CompositorHandler<U, R, H>, CompositorDestructor<U, R>>(
&id,
self.my_id,
);
}
} }
server_declare_handler!(CompositorHandler<U: [Send], R: [RoleType, Role<SubsurfaceRole>, Send], H: [Send]>, wl_subcompositor::Handler, wl_subcompositor::WlSubcompositor); fn subcompositor_implementation<U, R>() -> wl_subcompositor::Implementation<()>
where
R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static,
{
wl_subcompositor::Implementation {
get_subsurface: |evlh, _, _, subcompositor, subsurface, surface, parent| {
if let Err(()) = unsafe { SurfaceData::<U, R>::set_parent(surface, parent) } {
subcompositor.post_error(
wl_subcompositor::Error::BadSurface as u32,
"Surface already has a role.".into(),
);
return;
}
subsurface.set_user_data(
Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _,
);
evlh.register(
&subsurface,
subsurface_implementation::<U, R>(),
(),
Some(destroy_subsurface::<U, R>),
);
},
destroy: |_, _, _, _| {},
}
}
/* /*
* wl_subsurface * wl_subsurface
@ -226,7 +248,8 @@ server_declare_handler!(CompositorHandler<U: [Send], R: [RoleType, Role<Subsurfa
unsafe fn with_subsurface_attributes<U, R, F>(subsurface: &wl_subsurface::WlSubsurface, f: F) unsafe fn with_subsurface_attributes<U, R, F>(subsurface: &wl_subsurface::WlSubsurface, f: F)
where where
F: FnOnce(&mut SubsurfaceRole), F: FnOnce(&mut SubsurfaceRole),
R: RoleType + Role<SubsurfaceRole>, U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
{ {
let ptr = subsurface.get_user_data(); let ptr = subsurface.get_user_data();
let surface = &*(ptr as *mut wl_surface::WlSurface); let surface = &*(ptr as *mut wl_surface::WlSurface);
@ -235,24 +258,19 @@ where
); );
} }
impl<U, R, H> wl_subsurface::Handler for CompositorHandler<U, R, H> fn subsurface_implementation<U, R>() -> wl_subsurface::Implementation<()>
where where
R: RoleType + Role<SubsurfaceRole>, R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static,
{ {
fn set_position(&mut self, _: &mut EventLoopHandle, _: &Client, wl_subsurface::Implementation {
subsurface: &wl_subsurface::WlSubsurface, x: i32, y: i32) { set_position: |_, _, _, subsurface, x, y| unsafe {
trace!(self.log, "Setting subsurface position.");
unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| { with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
attrs.x = x; attrs.x = x;
attrs.y = y; attrs.y = y;
}); });
} },
} place_above: |_, _, _, subsurface, sibling| unsafe {
fn place_above(&mut self, _: &mut EventLoopHandle, _: &Client,
subsurface: &wl_subsurface::WlSubsurface, sibling: &wl_surface::WlSurface) {
trace!(self.log, "Setting subsurface above an other.");
unsafe {
let ptr = subsurface.get_user_data(); let ptr = subsurface.get_user_data();
let surface = &*(ptr as *mut wl_surface::WlSurface); let surface = &*(ptr as *mut wl_surface::WlSurface);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::After, sibling) { if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::After, sibling) {
@ -261,12 +279,8 @@ where
"Provided surface is not a sibling or parent.".into(), "Provided surface is not a sibling or parent.".into(),
); );
} }
} },
} place_below: |_, _, _, subsurface, sibling| unsafe {
fn place_below(&mut self, _: &mut EventLoopHandle, _: &Client,
subsurface: &wl_subsurface::WlSubsurface, sibling: &wl_surface::WlSurface) {
trace!(self.log, "Setting subsurface below an other.");
unsafe {
let ptr = subsurface.get_user_data(); let ptr = subsurface.get_user_data();
let surface = &*(ptr as *mut wl_surface::WlSurface); let surface = &*(ptr as *mut wl_surface::WlSurface);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, sibling) { if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, sibling) {
@ -275,36 +289,32 @@ where
"Provided surface is not a sibling or parent.".into(), "Provided surface is not a sibling or parent.".into(),
); );
} }
} },
} set_sync: |_, _, _, subsurface| unsafe {
fn set_sync(&mut self, _: &mut EventLoopHandle, _: &Client, subsurface: &wl_subsurface::WlSubsurface) { with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
trace!(self.log, "Setting subsurface sync."; "sync_status" => true); attrs.sync = true;
unsafe { });
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| { attrs.sync = true; }); },
} set_desync: |_, _, _, subsurface| unsafe {
} with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
fn set_desync(&mut self, _: &mut EventLoopHandle, _: &Client, subsurface: &wl_subsurface::WlSubsurface) { attrs.sync = false;
trace!(self.log, "Setting subsurface sync."; "sync_status" => false); });
unsafe { },
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| { attrs.sync = false; }); destroy: |_, _, _, _| {},
}
} }
} }
server_declare_handler!(CompositorHandler<U: [], R: [RoleType, Role<SubsurfaceRole>], H: []>, wl_subsurface::Handler, wl_subsurface::WlSubsurface); fn destroy_subsurface<U, R>(subsurface: &wl_subsurface::WlSubsurface)
impl<U, R> Destroy<wl_subsurface::WlSubsurface> for CompositorDestructor<U, R>
where where
R: RoleType + Role<SubsurfaceRole>, U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
{ {
fn destroy(subsurface: &wl_subsurface::WlSubsurface) { let ptr = subsurface.get_user_data();
let ptr = subsurface.get_user_data(); subsurface.set_user_data(::std::ptr::null_mut());
subsurface.set_user_data(::std::ptr::null_mut()); unsafe {
unsafe { let surface = Box::from_raw(ptr as *mut wl_surface::WlSurface);
let surface = Box::from_raw(ptr as *mut wl_surface::WlSurface); if surface.status() == Liveness::Alive {
if surface.status() == Liveness::Alive { SurfaceData::<U, R>::unset_parent(&surface);
SurfaceData::<U, R>::unset_parent(&surface);
}
} }
} }
} }

View File

@ -1,13 +1,12 @@
//! Utilities for handling surfaces, subsurfaces and regions //! Utilities for handling surfaces, subsurfaces and regions
//! //!
//! This module provides the `CompositorHandler<U,H>` type, with implements //! This module provides automatic handling of sufaces, subsurfaces
//! automatic handling of sufaces, subsurfaces and region wayland objects, //! and region wayland objects, by registering an implementation for
//! by being registered as a global handler for `wl_compositor` and //! for the `wl_compositor` and `wl_subcompositor` globals.
//! `wl_subcompositor`.
//! //!
//! ## Why use this handler //! ## Why use this implementation
//! //!
//! This handler does a simple job: it stores in a coherent way the state of //! This implementation does a simple job: it stores in a coherent way the state of
//! surface trees with subsurfaces, to provide you a direct access to the tree //! surface trees with subsurfaces, to provide you a direct access to the tree
//! structure and all surface metadata. //! structure and all surface metadata.
//! //!
@ -15,7 +14,7 @@
//! you can iterate over the whole tree of subsurfaces to recover all the metadata you //! you can iterate over the whole tree of subsurfaces to recover all the metadata you
//! need to display the subsurface tree. //! need to display the subsurface tree.
//! //!
//! This handler will not do anything more than present you the metadata specified by the //! This implementation will not do anything more than present you the metadata specified by the
//! client in a coherent and practical way. All the logic regarding to drawing itself, and //! client in a coherent and practical way. All the logic regarding to drawing itself, and
//! the positionning of windows (surface trees) one relative to another is out of its scope. //! the positionning of windows (surface trees) one relative to another is out of its scope.
//! //!
@ -23,15 +22,16 @@
//! //!
//! ### Initialization //! ### Initialization
//! //!
//! To initialize this handler, simply instanciate it and register it to the event loop //! To initialize this implementation, use the `compositor_init` method provided
//! as a global handler for wl_compositor and wl_subcompositor: //! by this module. It'll require you to first define as few things, as shown in
//! this example:
//! //!
//! ``` //! ```
//! # extern crate wayland_server; //! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay; //! # #[macro_use] extern crate smithay;
//! use wayland_server::protocol::wl_compositor::WlCompositor; //! use wayland_server::protocol::wl_compositor::WlCompositor;
//! use wayland_server::protocol::wl_subcompositor::WlSubcompositor; //! use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
//! use smithay::compositor; //! use smithay::compositor::{compositor_init, SurfaceUserImplementation};
//! //!
//! // Define some user data to be associated with the surfaces. //! // Define some user data to be associated with the surfaces.
//! // It must implement the Default trait, which will represent the state of a surface which //! // It must implement the Default trait, which will represent the state of a surface which
@ -44,39 +44,25 @@
//! // Declare the roles enum //! // Declare the roles enum
//! define_roles!(MyRoles); //! define_roles!(MyRoles);
//! //!
//! // Define a sub-handler to take care of the events the CompositorHandler does not rack for you
//! struct MyHandler {
//! // whatever you need
//! }
//!
//! // Implement the handler trait for this sub-handler
//! impl compositor::Handler<MyData, MyRoles> for MyHandler {
//! // See the trait documentation for its implementation
//! // A default implementation for each method is provided, that does nothing
//! }
//!
//! // A type alias to shorten things:
//! type MyCompositorHandler = compositor::CompositorHandler<MyData, MyRoles, MyHandler>;
//!
//! # fn main() { //! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display(); //! # let (_display, mut event_loop) = wayland_server::create_display();
//! // define your implementation for surface
//! let my_implementation = SurfaceUserImplementation {
//! commit: |evlh, idata, surface, token| { /* ... */ },
//! frame: |evlh, idata, surface, callback, token| { /* ... */ }
//! };
//! // define your implementation data
//! let my_implementation_data = ();
//! //!
//! // Instanciate the CompositorHandler and give it to the event loop //! // Call the init function:
//! let compositor_hid = event_loop.add_handler_with_init( //! let (token, _, _) = compositor_init::<MyData, MyRoles, _, _>(
//! MyCompositorHandler::new(MyHandler{ /* ... */ }, None /* put a logger here */) //! &mut event_loop,
//! my_implementation, // instance of compositor::SurfaceUserImplementation
//! my_implementation_data, // whatever implementation data you need
//! None // put a logger here
//! ); //! );
//! //!
//! // Register it as a handler for wl_compositor //! // this `token` is what you'll use to access the surface associated data
//! event_loop.register_global::<WlCompositor, MyCompositorHandler>(compositor_hid, 4);
//!
//! // Register it as a handler for wl_subcompositor
//! event_loop.register_global::<WlSubcompositor, MyCompositorHandler>(compositor_hid, 1);
//!
//! // retrieve the token needed to access the surfaces' metadata
//! let compositor_token = {
//! let state = event_loop.state();
//! state.get_handler::<MyCompositorHandler>(compositor_hid).get_token()
//! };
//! //!
//! // You're now ready to go! //! // You're now ready to go!
//! # } //! # }
@ -85,8 +71,9 @@
//! ### Use the surface metadata //! ### Use the surface metadata
//! //!
//! As you can see in the previous example, in the end we are retrieving a token from //! As you can see in the previous example, in the end we are retrieving a token from
//! the `CompositorHandler`. This token is necessary to retrieve the metadata associated with //! the init function. This token is necessary to retrieve the metadata associated with
//! a surface. It can be cloned, and is sendable accross threads. See `CompositorToken` for //! a surface. It can be cloned, and is sendable accross threads (as long as your
//! `Mydata` and `MyRoles` are `Send` too). See `CompositorToken` for
//! the details of what it enables you. //! the details of what it enables you.
//! //!
//! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered //! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered
@ -97,19 +84,19 @@
//! This `CompositorToken` also provides access to the metadata associated with the role of the //! This `CompositorToken` also provides access to the metadata associated with the role of the
//! surfaces. See the documentation of the `roles` submodule for a detailed explanation. //! surfaces. See the documentation of the `roles` submodule for a detailed explanation.
mod global;
mod handlers; mod handlers;
mod tree; mod tree;
mod region; mod region;
pub mod roles; pub mod roles;
pub use self::handlers::SurfaceIData;
use self::region::RegionData; use self::region::RegionData;
use self::roles::{Role, RoleType, WrongRole}; use self::roles::{Role, RoleType, WrongRole};
use self::tree::SurfaceData; use self::tree::SurfaceData;
pub use self::tree::TraversalAction; pub use self::tree::TraversalAction;
use wayland_server::{resource_is_registered, Client, EventLoopHandle, Init}; use wayland_server::{resource_is_registered, EventLoop, EventLoopHandle, Global};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region,
use wayland_server::protocol::{wl_buffer, wl_callback, wl_output, wl_region, wl_surface}; wl_subcompositor, wl_surface};
/// Description of which part of a surface /// Description of which part of a surface
/// should be considered damaged and needs to be redrawn /// should be considered damaged and needs to be redrawn
@ -124,6 +111,12 @@ pub enum Damage {
Buffer(Rectangle), Buffer(Rectangle),
} }
#[derive(Copy, Clone, Default)]
struct Marker<U, R> {
_u: ::std::marker::PhantomData<U>,
_r: ::std::marker::PhantomData<R>,
}
/// Data associated with a surface, aggreged by the handlers /// Data associated with a surface, aggreged by the handlers
/// ///
/// Most of the fields of this struct represent a double-buffered state, which /// Most of the fields of this struct represent a double-buffered state, which
@ -261,26 +254,35 @@ impl Default for RegionAttributes {
/// This token can be cloned at will, and is the entry-point to /// This token can be cloned at will, and is the entry-point to
/// access data associated with the wl_surface and wl_region managed /// access data associated with the wl_surface and wl_region managed
/// by the `CompositorGlobal` that provided it. /// by the `CompositorGlobal` that provided it.
pub struct CompositorToken<U, R, H> { pub struct CompositorToken<U, R, ID> {
hid: usize,
_data: ::std::marker::PhantomData<*mut U>, _data: ::std::marker::PhantomData<*mut U>,
_role: ::std::marker::PhantomData<*mut R>, _role: ::std::marker::PhantomData<*mut R>,
_handler: ::std::marker::PhantomData<*mut H>, _idata: ::std::marker::PhantomData<*mut ID>,
} }
unsafe impl<U: Send, R: Send, H: Send> Send for CompositorToken<U, R, H> {} unsafe impl<U: Send, R: Send, ID> Send for CompositorToken<U, R, ID> {}
unsafe impl<U: Send, R: Send, H: Send> Sync for CompositorToken<U, R, H> {} unsafe impl<U: Send, R: Send, ID> Sync for CompositorToken<U, R, ID> {}
// we implement them manually because #[derive(..)] would require // we implement them manually because #[derive(..)] would require
// U: Clone and H: Clone ... // U: Clone and R: Clone
impl<U, R, H> Copy for CompositorToken<U, R, H> {} impl<U, R, ID> Copy for CompositorToken<U, R, ID> {}
impl<U, R, H> Clone for CompositorToken<U, R, H> { impl<U, R, ID> Clone for CompositorToken<U, R, ID> {
fn clone(&self) -> CompositorToken<U, R, H> { fn clone(&self) -> CompositorToken<U, R, ID> {
*self *self
} }
} }
impl<U: Send + 'static, R: Send + 'static, H: Handler<U, R> + Send + 'static> CompositorToken<U, R, H> { impl<U, R, ID> CompositorToken<U, R, ID> {
pub(crate) fn make() -> CompositorToken<U, R, ID> {
CompositorToken {
_data: ::std::marker::PhantomData,
_role: ::std::marker::PhantomData,
_idata: ::std::marker::PhantomData,
}
}
}
impl<U: Send + 'static, R: Send + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// Access the data of a surface /// Access the data of a surface
/// ///
/// The closure will be called with the contents of the data associated with this surface. /// The closure will be called with the contents of the data associated with this surface.
@ -292,18 +294,21 @@ impl<U: Send + 'static, R: Send + 'static, H: Handler<U, R> + Send + 'static> Co
F: FnOnce(&mut SurfaceAttributes<U>) -> T, F: FnOnce(&mut SurfaceAttributes<U>) -> T,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::with_data(surface, f) } unsafe { SurfaceData::<U, R>::with_data(surface, f) }
} }
} }
impl<U, R, H> CompositorToken<U, R, H> impl<U, R, ID> CompositorToken<U, R, ID>
where where
U: Send + 'static, U: 'static,
R: RoleType + Role<SubsurfaceRole> + Send + 'static, R: RoleType + Role<SubsurfaceRole> + 'static,
H: Handler<U, R> + Send + 'static, ID: 'static,
{ {
/// Access the data of a surface tree /// Access the data of a surface tree
/// ///
@ -327,7 +332,10 @@ where
-> TraversalAction<T>, -> TraversalAction<T>,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { unsafe {
@ -344,7 +352,10 @@ where
/// will panic (having more than one compositor is not supported). /// will panic (having more than one compositor is not supported).
pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> { pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::get_parent(surface) } unsafe { SurfaceData::<U, R>::get_parent(surface) }
@ -356,22 +367,27 @@ where
/// will panic (having more than one compositor is not supported). /// will panic (having more than one compositor is not supported).
pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> { pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::get_children(surface) } unsafe { SurfaceData::<U, R>::get_children(surface) }
} }
} }
impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send + 'static> impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
CompositorToken<U, R, H> {
/// Check wether this surface as a role or not /// Check wether this surface as a role or not
/// ///
/// If the surface is not managed by the CompositorGlobal that provided this token, this /// If the surface is not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported). /// will panic (having more than one compositor is not supported).
pub fn has_a_role(&self, surface: &wl_surface::WlSurface) -> bool { pub fn has_a_role(&self, surface: &wl_surface::WlSurface) -> bool {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::has_a_role(surface) } unsafe { SurfaceData::<U, R>::has_a_role(surface) }
@ -386,7 +402,10 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
R: Role<RoleData>, R: Role<RoleData>,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::has_role::<RoleData>(surface) } unsafe { SurfaceData::<U, R>::has_role::<RoleData>(surface) }
@ -405,7 +424,10 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
RoleData: Default, RoleData: Default,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::give_role::<RoleData>(surface) } unsafe { SurfaceData::<U, R>::give_role::<RoleData>(surface) }
@ -423,7 +445,10 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
R: Role<RoleData>, R: Role<RoleData>,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::give_role_with::<RoleData>(surface, data) } unsafe { SurfaceData::<U, R>::give_role_with::<RoleData>(surface, data) }
@ -442,7 +467,10 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
F: FnOnce(&mut RoleData) -> T, F: FnOnce(&mut RoleData) -> T,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::with_role_data::<RoleData, _, _>(surface, f) } unsafe { SurfaceData::<U, R>::with_role_data::<RoleData, _, _>(surface, f) }
@ -459,7 +487,10 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
R: Role<RoleData>, R: Role<RoleData>,
{ {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(surface, self.hid), resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { SurfaceData::<U, R>::remove_role::<RoleData>(surface) } unsafe { SurfaceData::<U, R>::remove_role::<RoleData>(surface) }
@ -471,98 +502,93 @@ impl<U: Send + 'static, R: Send + RoleType + 'static, H: Handler<U, R> + Send +
/// will panic (having more than one compositor is not supported). /// will panic (having more than one compositor is not supported).
pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes { pub fn get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes {
assert!( assert!(
resource_is_registered::<_, CompositorHandler<U, R, H>>(region, self.hid), resource_is_registered(region, &self::handlers::region_implementation()),
"Accessing the data of foreign regions is not supported." "Accessing the data of foreign surfaces is not supported."
); );
unsafe { RegionData::get_attributes(region) } unsafe { RegionData::get_attributes(region) }
} }
} }
/// A struct handling the `wl_compositor` and `wl_subcompositor` globals /// Create new wl_compositor and wl_subcompositor globals.
/// ///
/// It allows you to choose a custom `U` type to store data you want /// The globals are directly registered into the eventloop, and this function
/// associated with the surfaces in their metadata, as well a providing /// returns a `CompositorToken` which you'll need access the data associated to
/// a sub-handler to handle the events defined by the `Handler` trait /// the `wl_surface`s.
/// defined in this module.
/// ///
/// See the module-level documentation for instructions and examples of use. /// It also returns the two global handles, in case you whish to remove these
pub struct CompositorHandler<U, R, H> { /// globals from the event loop in the future.
my_id: usize, pub fn compositor_init<U, R, ID, L>(
log: ::slog::Logger, evl: &mut EventLoop, implem: SurfaceUserImplementation<U, R, ID>, idata: ID, logger: L)
handler: H, -> (
_role: ::std::marker::PhantomData<R>, CompositorToken<U, R, ID>,
_data: ::std::marker::PhantomData<U>, Global<wl_compositor::WlCompositor, self::handlers::SurfaceIData<U, R, ID>>,
Global<wl_subcompositor::WlSubcompositor, ()>,
)
where
L: Into<Option<::slog::Logger>>,
U: Default + 'static,
R: Default + RoleType + Role<SubsurfaceRole> + 'static,
ID: 'static,
{
let log = ::slog_or_stdlog(logger);
let idata = self::handlers::SurfaceIData::make(
log.new(o!("smithay_module" => "compositor_handler")),
implem,
idata,
);
let compositor_global_token =
evl.register_global::<wl_compositor::WlCompositor, _>(4, self::handlers::compositor_bind, idata);
let subcompositor_global_token = evl.register_global::<wl_subcompositor::WlSubcompositor, _>(
1,
self::handlers::subcompositor_bind::<U, R>,
(),
);
(
CompositorToken::make(),
compositor_global_token,
subcompositor_global_token,
)
} }
impl<U, R, H> Init for CompositorHandler<U, R, H> { /// Sub-implementation for surface event handling
fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
self.my_id = index;
debug!(self.log, "Init finished")
}
}
impl<U, R, H> CompositorHandler<U, R, H> {
/// Create a new CompositorHandler
pub fn new<L>(handler: H, logger: L) -> CompositorHandler<U, R, H>
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
CompositorHandler {
my_id: ::std::usize::MAX,
log: log.new(o!("smithay_module" => "compositor_handler")),
handler: handler,
_role: ::std::marker::PhantomData,
_data: ::std::marker::PhantomData,
}
}
/// Create a token to access the data associated to the objects managed by this handler.
pub fn get_token(&self) -> CompositorToken<U, R, H> {
assert!(
self.my_id != ::std::usize::MAX,
"CompositorHandler is not initialized yet."
);
trace!(self.log, "Creating a compositor token.");
CompositorToken {
hid: self.my_id,
_data: ::std::marker::PhantomData,
_role: ::std::marker::PhantomData,
_handler: ::std::marker::PhantomData,
}
}
/// Access the underlying sub-handler
pub fn get_handler(&mut self) -> &mut H {
&mut self.handler
}
}
/// Sub-handler trait for surface event handling
/// ///
/// The global provided by Smithay cannot process these events for you, so they /// The global provided by Smithay cannot process these events for you, so they
/// are forwarded directly to a handler implementing this trait that you must provide /// are forwarded directly to this implementation that you must provide
/// at creation of the `CompositorHandler`. /// at creation of the compositor global.
#[allow(unused_variables)] pub struct SurfaceUserImplementation<U, R, ID> {
pub trait Handler<U, R>: Sized {
/// The double-buffered state has been validated by the client /// The double-buffered state has been validated by the client
/// ///
/// At this point, the pending state that has been accumulated in the `SurfaceAttributes` associated /// At this point, the pending state that has been accumulated in the `SurfaceAttributes` associated
/// to this surface should be integrated into the current state of the surface. /// to this surface should be integrated into the current state of the surface.
/// ///
/// See [`wayland_server::protocol::wl_surface::Handler::commit`](https://docs.rs/wayland-server/*/wayland_server/protocol/wl_surface/trait.Handler.html#method.commit) /// See [`wayland_server::protocol::wl_surface::Implementation::commit`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.commit)
/// for more details /// for more details
fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, pub commit: fn(
token: CompositorToken<U, R, Self>) { evlh: &mut EventLoopHandle,
} idata: &mut ID,
surface: &wl_surface::WlSurface,
token: CompositorToken<U, R, ID>,
),
/// The client asks to be notified when would be a good time to update the contents of this surface /// The client asks to be notified when would be a good time to update the contents of this surface
/// ///
/// You must keep the provided `WlCallback` and trigger it at the appropriate time by calling /// You must keep the provided `WlCallback` and trigger it at the appropriate time by calling
/// its `done()` method. /// its `done()` method.
/// ///
/// See [`wayland_server::protocol::wl_surface::Handler::frame`](https://docs.rs/wayland-server/*/wayland_server/protocol/wl_surface/trait.Handler.html#method.frame) /// See [`wayland_server::protocol::wl_surface::Implementation::frame`](https://docs.rs/wayland-server/0.10.1/wayland_server/protocol/wl_surface/struct.Implementation.html#structfield.frame)
/// for more details /// for more details
fn frame(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, pub frame: fn(
callback: wl_callback::WlCallback, token: CompositorToken<U, R, Self>) { evlh: &mut EventLoopHandle,
idata: &mut ID,
surface: &wl_surface::WlSurface,
callback: wl_callback::WlCallback,
token: CompositorToken<U, R, ID>,
),
}
impl<U, R, ID> Copy for SurfaceUserImplementation<U, R, ID> {}
impl<U, R, ID> Clone for SurfaceUserImplementation<U, R, ID> {
fn clone(&self) -> SurfaceUserImplementation<U, R, ID> {
*self
} }
} }

View File

@ -1,8 +1,6 @@
use super::{Rectangle, RectangleKind, RegionAttributes}; use super::{Rectangle, RectangleKind, RegionAttributes};
use std::sync::Mutex; use std::sync::Mutex;
use wayland_server::Resource; use wayland_server::Resource;
use wayland_server::protocol::wl_region; use wayland_server::protocol::wl_region;
#[derive(Default)] #[derive(Default)]
@ -13,9 +11,8 @@ pub struct RegionData {
impl RegionData { impl RegionData {
/// Initialize the user_data of a region, must be called right when the surface is created /// Initialize the user_data of a region, must be called right when the surface is created
pub unsafe fn init(region: &wl_region::WlRegion) { pub unsafe fn init(region: &wl_region::WlRegion) {
region.set_user_data( region.set_user_data(Box::into_raw(Box::new(Mutex::new(RegionData::default())))
Box::into_raw(Box::new(Mutex::new(RegionData::default()))) as *mut _, as *mut _)
)
} }
/// Cleans the user_data of that surface, must be called when it is destroyed /// Cleans the user_data of that surface, must be called when it is destroyed

View File

@ -1,7 +1,6 @@
use super::{SubsurfaceRole, SurfaceAttributes}; use super::{SubsurfaceRole, SurfaceAttributes};
use super::roles::*; use super::roles::*;
use std::sync::Mutex; use std::sync::Mutex;
use wayland_server::{Liveness, Resource}; use wayland_server::{Liveness, Resource};
use wayland_server::protocol::wl_surface; use wayland_server::protocol::wl_surface;
@ -64,7 +63,11 @@ impl<U: Default, R: Default> SurfaceData<U, R> {
} }
} }
impl<U, R> SurfaceData<U, R> { impl<U, R> SurfaceData<U, R>
where
U: 'static,
R: 'static,
{
unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex<SurfaceData<U, R>> { unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex<SurfaceData<U, R>> {
let ptr = surface.get_user_data(); let ptr = surface.get_user_data();
&*(ptr as *mut _) &*(ptr as *mut _)
@ -97,7 +100,7 @@ impl<U, R> SurfaceData<U, R> {
} }
} }
impl<U, R: RoleType> SurfaceData<U, R> { impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
pub unsafe fn has_a_role(surface: &wl_surface::WlSurface) -> bool { pub unsafe fn has_a_role(surface: &wl_surface::WlSurface) -> bool {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.status() == Liveness::Alive);
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
@ -171,7 +174,7 @@ impl<U, R: RoleType> SurfaceData<U, R> {
} }
} }
impl<U, R: RoleType + Role<SubsurfaceRole>> SurfaceData<U, R> { impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R> {
/// Sets the parent of a surface /// Sets the parent of a surface
/// ///
/// if this surface already has a role, does nothing and fails, otherwise /// if this surface already has a role, does nothing and fails, otherwise
@ -291,7 +294,7 @@ impl<U, R: RoleType + Role<SubsurfaceRole>> SurfaceData<U, R> {
} }
} }
impl<U, R> SurfaceData<U, R> { impl<U: 'static, R: 'static> SurfaceData<U, R> {
/// Access the attributes associated with a surface /// Access the attributes associated with a surface
/// ///
/// Note that an internal lock is taken during access of this data, /// Note that an internal lock is taken during access of this data,
@ -319,9 +322,9 @@ impl<U, R> SurfaceData<U, R> {
-> TraversalAction<T>, -> TraversalAction<T>,
{ {
// helper function for recursion // helper function for recursion
unsafe fn map<U, R, F, T>(surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, unsafe fn map<U: 'static, R: 'static, F, T>(surface: &wl_surface::WlSurface,
initial: &T, f: &mut F) root: &wl_surface::WlSurface, initial: &T, f: &mut F)
-> bool -> bool
where where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
-> TraversalAction<T>, -> TraversalAction<T>,

View File

@ -12,7 +12,6 @@ extern crate nix;
extern crate rental; extern crate rental;
extern crate tempfile; extern crate tempfile;
extern crate wayland_protocols; extern crate wayland_protocols;
#[macro_use]
extern crate wayland_server; extern crate wayland_server;
extern crate xkbcommon; extern crate xkbcommon;

View File

@ -1,54 +0,0 @@
use super::{Handler as UserHandler, ShellClientData, ShellHandler, ShellSurfaceRole};
use super::wl_handlers::WlShellDestructor;
use super::xdg_handlers::XdgShellDestructor;
use compositor::Handler as CompositorHandler;
use compositor::roles::*;
use std::sync::Mutex;
use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6;
use wayland_server::{Client, EventLoopHandle, GlobalHandler, Resource};
use wayland_server::protocol::{wl_shell, wl_shell_surface};
fn shell_client_data<SD: Default>() -> ShellClientData<SD> {
ShellClientData {
pending_ping: 0,
data: Default::default(),
}
}
impl<U, R, H, SH, SD> GlobalHandler<wl_shell::WlShell> for ShellHandler<U, R, H, SH, SD>
where
U: Send + 'static,
R: Role<ShellSurfaceRole> + Send + 'static,
H: CompositorHandler<U, R> + Send + 'static,
SH: UserHandler<U, R, H, SD> + Send + 'static,
SD: Default + Send + 'static,
{
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: wl_shell::WlShell) {
debug!(self.log, "New wl_shell global binded.");
global.set_user_data(Box::into_raw(Box::new(Mutex::new((
shell_client_data::<SD>(),
Vec::<wl_shell_surface::WlShellSurface>::new(),
)))) as *mut _);
evlh.register_with_destructor::<_, Self, WlShellDestructor<SD>>(&global, self.my_id);
}
}
impl<U, R, H, SH, SD> GlobalHandler<zxdg_shell_v6::ZxdgShellV6> for ShellHandler<U, R, H, SH, SD>
where
U: Send + 'static,
R: Role<ShellSurfaceRole> + Send + 'static,
H: CompositorHandler<U, R> + Send + 'static,
SH: UserHandler<U, R, H, SD> + Send + 'static,
SD: Default + Send + 'static,
{
fn bind(&mut self, evlh: &mut EventLoopHandle, _: &Client, global: zxdg_shell_v6::ZxdgShellV6) {
debug!(self.log, "New xdg_shell global binded.");
global.set_user_data(
Box::into_raw(Box::new(Mutex::new(shell_client_data::<SD>()))) as *mut _,
);
evlh.register_with_destructor::<_, Self, XdgShellDestructor<SD>>(&global, self.my_id);
}
}

View File

@ -1,14 +1,13 @@
//! Utilities for handling shell surfaces, toplevel and popups //! Utilities for handling shell surfaces, toplevel and popups
//! //!
//! This module provides the `ShellHandler` type, which implements automatic handling of //! This module provides automatic handling of shell surfaces objects, by being registered
//! shell surfaces objects, by being registered as a global handler for `wl_shell` and //! as a global handler for `wl_shell` and `xdg_shell`.
//! `xdg_shell`.
//! //!
//! ## Why use this handler //! ## Why use this implementation
//! //!
//! This handler can track for you the various shell surfaces defined by the clients by //! This implementation can track for you the various shell surfaces defined by the
//! handling the `xdg_shell` protocol. It also includes a compatibility layer for the //! clients by handling the `xdg_shell` protocol. It also includes a compatibility
//! deprecated `wl_shell` global. //! layer for the deprecated `wl_shell` global.
//! //!
//! It allows you to easily access a list of all shell surfaces defined by your clients //! 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. //! access their associated metadata and underlying `wl_surface`s.
@ -21,28 +20,24 @@
//! //!
//! ### Initialization //! ### Initialization
//! //!
//! To initialize this handler, simply instanciate it and register it to the event loop //! To initialize this handler, simple use the `shell_init` function provided in this
//! as a global handler for xdg_shell and wl_shell. You will need to provide it the //! module. You will need to provide it the `CompositorToken` you retrieved from an
//! `CompositorToken` you retrieved from an instanciation of the `CompositorHandler` //! instanciation of the `CompositorHandler` provided by smithay.
//! provided by smithay.
//! //!
//! ``` //! ```no_run
//! # extern crate wayland_server; //! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay; //! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols; //! # extern crate wayland_protocols;
//! # //! #
//! use smithay::compositor::roles::*; //! use smithay::compositor::roles::*;
//! use smithay::compositor::CompositorToken; //! use smithay::compositor::CompositorToken;
//! use smithay::shell::{ShellHandler, Handler as ShellHandlerTrait, ShellSurfaceRole}; //! use smithay::shell::{shell_init, ShellSurfaceRole, ShellSurfaceUserImplementation};
//! use wayland_server::protocol::wl_shell::WlShell; //! use wayland_server::protocol::wl_shell::WlShell;
//! use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6::ZxdgShellV6; //! use wayland_protocols::unstable::xdg_shell::server::zxdg_shell_v6::ZxdgShellV6;
//! use wayland_server::{EventLoop, EventLoopHandle}; //! use wayland_server::{EventLoop, EventLoopHandle};
//! # use smithay::shell::*;
//! # use wayland_server::protocol::{wl_seat, wl_output}; //! # use wayland_server::protocol::{wl_seat, wl_output};
//! # use wayland_protocols::unstable::xdg_shell::server::zxdg_toplevel_v6; //! # use wayland_protocols::unstable::xdg_shell::server::zxdg_toplevel_v6;
//! # #[derive(Default)] struct MySurfaceData; //! # #[derive(Default)] struct MySurfaceData;
//! # struct MyHandlerForCompositor;
//! # impl ::smithay::compositor::Handler<MySurfaceData, MyRoles> for MyHandlerForCompositor {}
//! //!
//! // define the roles type. You need to integrate the ShellSurface role: //! // define the roles type. You need to integrate the ShellSurface role:
//! define_roles!(MyRoles => //! define_roles!(MyRoles =>
@ -55,100 +50,70 @@
//! /* ... */ //! /* ... */
//! } //! }
//! //!
//! // define a sub-handler for the shell::Handler trait
//! struct MyHandlerForShell {
//! /* ... */
//! }
//!
//! # type MyToplevelSurface = ToplevelSurface<MySurfaceData, MyRoles, MyHandlerForCompositor,
//! # MyShellData>;
//! # type MyPopupSurface = PopupSurface<MySurfaceData, MyRoles, MyHandlerForCompositor,
//! # MyShellData>;
//!
//! impl ShellHandlerTrait<MySurfaceData, MyRoles, MyHandlerForCompositor, MyShellData> for MyHandlerForShell {
//! /* ... a few methods to implement, see shell::Handler
//! documentation for details ... */
//! # fn new_client(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<MyShellData>) { unimplemented!() }
//! # fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<MyShellData>) { unimplemented!() }
//! # fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface)
//! # -> ToplevelConfigure { unimplemented!() }
//! # fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: MyPopupSurface)
//! # -> PopupConfigure { unimplemented!() }
//! # fn move_(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
//! # seat: &wl_seat::WlSeat, serial: u32) { unimplemented!() }
//! # fn resize(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
//! # seat: &wl_seat::WlSeat, serial: u32, edges: zxdg_toplevel_v6::ResizeEdge) { unimplemented!() }
//! # fn grab(&mut self, evlh: &mut EventLoopHandle, surface: MyPopupSurface,
//! # seat: &wl_seat::WlSeat, serial: u32) { unimplemented!() }
//! # fn change_display_state(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
//! # maximized: Option<bool>, minimized: Option<bool>, fullscreen: Option<bool>,
//! # output: Option<&wl_output::WlOutput>)
//! # -> ToplevelConfigure { unimplemented!() }
//! # fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, surface: MyToplevelSurface,
//! # seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32) { unimplemented!() }
//! }
//!
//! # type MyCompositorHandler = smithay::compositor::CompositorHandler<MySurfaceData, MyRoles,
//! # MyHandlerForCompositor>;
//! // A type alias for brevety. ShellHandler has many type parameters:
//! type MyShellHandler = ShellHandler<
//! MySurfaceData, // the surface data you defined for the CompositorHandler
//! MyRoles, // the roles type
//! MyHandlerForCompositor, // the sub-handler type you defined for the CompositorHandler
//! MyHandlerForShell, // the sub-handler type you defined for this ShellHandler
//! MyShellData // the client data you defined for this ShellHandler
//! >;
//! # fn main() { //! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display(); //! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let compositor_hid = event_loop.add_handler_with_init( //! # let (compositor_token, _, _) = smithay::compositor::compositor_init::<(), MyRoles, _, _>(
//! # MyCompositorHandler::new(MyHandlerForCompositor{ /* ... */ }, None /* put a logger here */) //! # &mut event_loop,
//! # unimplemented!(),
//! # (),
//! # None
//! # ); //! # );
//! # let compositor_token = { //! // define your implementation for shell
//! # let state = event_loop.state(); //! let my_shell_implementation = ShellSurfaceUserImplementation {
//! # state.get_handler::<MyCompositorHandler>(compositor_hid).get_token() //! new_client: |evlh, idata, client| { unimplemented!() },
//! # }; //! client_pong: |evlh, idata, client| { unimplemented!() },
//! new_toplevel: |evlh, idata, toplevel| { unimplemented!() },
//! new_popup: |evlh, idata, popup| { unimplemented!() },
//! move_: |evlh, idata, toplevel, seat, serial| { unimplemented!() },
//! resize: |evlh, idata, toplevel, seat, serial, edges| { unimplemented!() },
//! grab: |evlh, idata, popup, seat, serial| { unimplemented!() },
//! change_display_state: |evlh, idata, toplevel, maximized, minimized, fullscreen, output| {
//! unimplemented!()
//! },
//! show_window_menu: |evlh, idata, toplevel, seat, serial, x, y| { unimplemented!() },
//! };
//! //!
//! let shell_hid = event_loop.add_handler_with_init( //! // define your implementation data
//! MyShellHandler::new( //! let my_shell_implementation_data = ();
//! MyHandlerForShell{ /* ... */ }, //!
//! compositor_token, // the composior token you retrieved from the CompositorHandler //! let (shell_state_token, _, _) = shell_init::<_, _, _, _, MyShellData, _>(
//! None /* put a logger here */ //! &mut event_loop,
//! ) //! compositor_token, // token from the compositor implementation
//! my_shell_implementation, // instance of shell::ShellSurfaceUserImplementation
//! my_shell_implementation_data, // whatever data you need here
//! None // put a logger if you want
//! ); //! );
//! //!
//! event_loop.register_global::<WlShell, MyShellHandler>(shell_hid, 1);
//! event_loop.register_global::<ZxdgShellV6, MyShellHandler>(shell_hid, 1);
//!
//! // You're now ready to go! //! // You're now ready to go!
//! # } //! # }
//! ``` //! ```
//! //!
//! ### Access to shell surface and clients data //! ### Access to shell surface and clients data
//! //!
//! There are mainly 3 kind of objects that you'll manipulate from this handler: //! There are mainly 3 kind of objects that you'll manipulate from this implementation:
//! //!
//! - `ShellClient`: This is a handle representing an isntanciation of a shell global //! - `ShellClient`: This is a handle representing an isntanciation of a shell global
//! you can associate client-wise metadata to it (this is the `MyShellData` type in //! you can associate client-wise metadata to it (this is the `MyShellData` type in
//! the example above). //! the example above).
//! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can //! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can
//! retrive a list of all currently alive toplevel surface from the `Shellhandler`. //! retrive a list of all currently alive toplevel surface from the `ShellState`.
//! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly, //! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly,
//! you can get a list of all currently alive popup surface from the `ShellHandler`. //! you can get a list of all currently alive popup surface from the `ShellState`.
//! //!
//! You'll obtain these objects though two means: either via the callback methods of //! You'll obtain these objects though two means: either via the callback methods of
//! the subhandler you provided, or via methods on the `ShellHandler` that you can //! the subhandler you provided, or via methods on the `ShellState` that you can
//! access from the `state()` of the event loop. //! access from the `state()` of the event loop and the token returned by the init
//! function.
use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle}; use compositor::{CompositorToken, Rectangle};
use compositor::roles::Role; use compositor::roles::Role;
use std::cell::RefCell;
use std::rc::Rc;
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6 as xdg_positioner, use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6 as xdg_positioner,
zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6}; zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{EventLoop, EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken};
use wayland_server::{EventLoopHandle, EventResult, Init, Liveness, Resource};
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface}; use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
mod global;
mod wl_handlers; mod wl_handlers;
mod xdg_handlers; mod xdg_handlers;
@ -301,54 +266,100 @@ impl Default for ShellSurfacePendingState {
} }
} }
/// The handler for the shell globals /// Internal implementation data of shell surfaces
/// ///
/// See module-level documentation for its use. /// This type is only visible as type parameter of
pub struct ShellHandler<U, R, H, SH, SD> { /// the `Global` handle you are provided.
my_id: usize, pub struct ShellSurfaceIData<U, R, CID, SID, SD> {
log: ::slog::Logger, log: ::slog::Logger,
token: CompositorToken<U, R, H>, compositor_token: CompositorToken<U, R, CID>,
handler: SH, implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>,
known_toplevels: Vec<ToplevelSurface<U, R, H, SD>>, idata: Rc<RefCell<SID>>,
known_popups: Vec<PopupSurface<U, R, H, SD>>, state_token: StateToken<ShellState<U, R, CID, SD>>,
_shell_data: ::std::marker::PhantomData<SD>,
} }
impl<U, R, H, SH, SD> Init for ShellHandler<U, R, H, SH, SD> { impl<U, R, CID, SID, SD> Clone for ShellSurfaceIData<U, R, CID, SID, SD> {
fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) { fn clone(&self) -> ShellSurfaceIData<U, R, CID, SID, SD> {
self.my_id = index; ShellSurfaceIData {
debug!(self.log, "Init finished") log: self.log.clone(),
} compositor_token: self.compositor_token.clone(),
} implementation: self.implementation.clone(),
idata: self.idata.clone(),
impl<U, R, H, SH, SD> ShellHandler<U, R, H, SH, SD> state_token: self.state_token.clone(),
where
U: Send + 'static,
R: Role<ShellSurfaceRole> + Send + 'static,
H: CompositorHandler<U, R> + Send + 'static,
{
/// Create a new CompositorHandler
pub fn new<L>(handler: SH, token: CompositorToken<U, R, H>, logger: L) -> ShellHandler<U, R, H, SH, SD>
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
ShellHandler {
my_id: ::std::usize::MAX,
log: log.new(o!("smithay_module" => "shell_handler")),
token: token,
handler: handler,
known_toplevels: Vec::new(),
known_popups: Vec::new(),
_shell_data: ::std::marker::PhantomData,
} }
} }
}
/// Access the inner handler of this CompositorHandler /// Create new xdg_shell and wl_shell globals.
pub fn get_handler(&mut self) -> &mut SH { ///
&mut self.handler /// The globals are directly registered into the eventloop, and this function
} /// returns a `StateToken<_>` which you'll need access the list of shell
/// surfaces created by your clients.
///
/// It also returns the two global handles, in case you whish to remove these
/// globals from the event loop in the future.
pub fn shell_init<U, R, CID, SID, SD, L>(
evl: &mut EventLoop, token: CompositorToken<U, R, CID>,
implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>, idata: SID, logger: L)
-> (
StateToken<ShellState<U, R, CID, SD>>,
Global<wl_shell::WlShell, ShellSurfaceIData<U, R, CID, SID, SD>>,
Global<zxdg_shell_v6::ZxdgShellV6, ShellSurfaceIData<U, R, CID, SID, SD>>,
)
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: Default + 'static,
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
let shell_state = ShellState {
known_toplevels: Vec::new(),
known_popups: Vec::new(),
};
let shell_state_token = evl.state().insert(shell_state);
let shell_surface_idata = ShellSurfaceIData {
log: log.new(o!("smithay_module" => "shell_handler")),
compositor_token: token,
implementation: implementation,
idata: Rc::new(RefCell::new(idata)),
state_token: shell_state_token.clone(),
};
// TODO: init globals
let wl_shell_global = evl.register_global(
1,
self::wl_handlers::wl_shell_bind::<U, R, CID, SID, SD>,
shell_surface_idata.clone(),
);
let xdg_shell_global = evl.register_global(
1,
self::xdg_handlers::xdg_shell_bind::<U, R, CID, SID, SD>,
shell_surface_idata.clone(),
);
(shell_state_token, wl_shell_global, xdg_shell_global)
}
/// Shell global state
///
/// This state allows you to retrieve a list of surfaces
/// currently known to the shell global.
pub struct ShellState<U, R, CID, SD> {
known_toplevels: Vec<ToplevelSurface<U, R, CID, SD>>,
known_popups: Vec<PopupSurface<U, R, CID, SD>>,
}
impl<U, R, CID, SD> ShellState<U, R, CID, SD>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
/// Cleans the internal surface storage by removing all dead surfaces /// Cleans the internal surface storage by removing all dead surfaces
pub fn cleanup_surfaces(&mut self) { pub fn cleanup_surfaces(&mut self) {
self.known_toplevels.retain(|s| s.alive()); self.known_toplevels.retain(|s| s.alive());
@ -356,12 +367,12 @@ where
} }
/// Access all the shell surfaces known by this handler /// Access all the shell surfaces known by this handler
pub fn toplevel_surfaces(&self) -> &[ToplevelSurface<U, R, H, SD>] { pub fn toplevel_surfaces(&self) -> &[ToplevelSurface<U, R, CID, SD>] {
&self.known_toplevels[..] &self.known_toplevels[..]
} }
/// Access all the popup surfaces known by this handler /// Access all the popup surfaces known by this handler
pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, H, SD>] { pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, CID, SD>] {
&self.known_popups[..] &self.known_popups[..]
} }
} }
@ -375,11 +386,18 @@ enum ShellClientKind {
Xdg(zxdg_shell_v6::ZxdgShellV6), Xdg(zxdg_shell_v6::ZxdgShellV6),
} }
struct ShellClientData<SD> { pub(crate) struct ShellClientData<SD> {
pending_ping: u32, pending_ping: u32,
data: SD, data: SD,
} }
fn make_shell_client_data<SD: Default>() -> ShellClientData<SD> {
ShellClientData {
pending_ping: 0,
data: Default::default(),
}
}
/// A shell client /// A shell client
/// ///
/// This represents an instanciation of a shell /// This represents an instanciation of a shell
@ -490,18 +508,19 @@ enum SurfaceKind {
/// ///
/// This is an unified abstraction over the toplevel surfaces /// This is an unified abstraction over the toplevel surfaces
/// of both `wl_shell` and `xdg_shell`. /// of both `wl_shell` and `xdg_shell`.
pub struct ToplevelSurface<U, R, H, SD> { pub struct ToplevelSurface<U, R, CID, SD> {
wl_surface: wl_surface::WlSurface, wl_surface: wl_surface::WlSurface,
shell_surface: SurfaceKind, shell_surface: SurfaceKind,
token: CompositorToken<U, R, H>, token: CompositorToken<U, R, CID>,
_shell_data: ::std::marker::PhantomData<SD>, _shell_data: ::std::marker::PhantomData<SD>,
} }
impl<U, R, H, SD> ToplevelSurface<U, R, H, SD> impl<U, R, CID, SD> ToplevelSurface<U, R, CID, SD>
where where
U: Send + 'static, U: 'static,
R: Role<ShellSurfaceRole> + Send + 'static, R: Role<ShellSurfaceRole> + 'static,
H: CompositorHandler<U, R> + Send + 'static, CID: 'static,
SD: 'static,
{ {
/// Is the toplevel surface refered by this handle still alive? /// Is the toplevel surface refered by this handle still alive?
pub fn alive(&self) -> bool { pub fn alive(&self) -> bool {
@ -628,18 +647,19 @@ where
/// ///
/// This is an unified abstraction over the popup surfaces /// This is an unified abstraction over the popup surfaces
/// of both `wl_shell` and `xdg_shell`. /// of both `wl_shell` and `xdg_shell`.
pub struct PopupSurface<U, R, H, SD> { pub struct PopupSurface<U, R, CID, SD> {
wl_surface: wl_surface::WlSurface, wl_surface: wl_surface::WlSurface,
shell_surface: SurfaceKind, shell_surface: SurfaceKind,
token: CompositorToken<U, R, H>, token: CompositorToken<U, R, CID>,
_shell_data: ::std::marker::PhantomData<SD>, _shell_data: ::std::marker::PhantomData<SD>,
} }
impl<U, R, H, SD> PopupSurface<U, R, H, SD> impl<U, R, CID, SD> PopupSurface<U, R, CID, SD>
where where
U: Send + 'static, U: 'static,
R: Role<ShellSurfaceRole> + Send + 'static, R: Role<ShellSurfaceRole> + 'static,
H: CompositorHandler<U, R> + Send + 'static, CID: 'static,
SD: 'static,
{ {
/// Is the popup surface refered by this handle still alive? /// Is the popup surface refered by this handle still alive?
pub fn alive(&self) -> bool { pub fn alive(&self) -> bool {
@ -807,47 +827,66 @@ pub struct PopupConfigure {
pub serial: u32, pub serial: u32,
} }
/// A trait for the sub-handler provided to the ShellHandler /// A sub-implementation for the shell
/// ///
/// You need to implement this trait to handle events that the ShellHandler /// You need to provide this to handle events that the provided implementation
/// cannot process for you directly. /// cannot process for you directly.
/// ///
/// Depending on what you want to do, you might implement some of these methods /// Depending on what you want to do, you might implement some of these functions
/// as doing nothing. /// as doing nothing.
pub trait Handler<U, R, H, SD> { pub struct ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
/// A new shell client was instanciated /// A new shell client was instanciated
fn new_client(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<SD>); pub new_client: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient<SD>),
/// The pong for a pending ping of this shell client was received /// The pong for a pending ping of this shell client was received
/// ///
/// The ShellHandler already checked for you that the serial matches the one /// The ShellHandler already checked for you that the serial matches the one
/// from the pending ping. /// from the pending ping.
fn client_pong(&mut self, evlh: &mut EventLoopHandle, client: ShellClient<SD>); pub client_pong: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient<SD>),
/// A new toplevel surface was created /// A new toplevel surface was created
/// ///
/// You need to return a `ToplevelConfigure` from this method, which will be sent /// You need to return a `ToplevelConfigure` from this function, which will be sent
/// to the client to configure this surface /// to the client to configure this surface
fn new_toplevel(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>) pub new_toplevel: fn(
-> ToplevelConfigure; evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
) -> ToplevelConfigure,
/// A new popup surface was created /// A new popup surface was created
/// ///
/// You need to return a `PopupConfigure` from this method, which will be sent /// You need to return a `PopupConfigure` from this function, which will be sent
/// to the client to configure this surface /// to the client to configure this surface
fn new_popup(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface<U, R, H, SD>) pub new_popup: fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: PopupSurface<U, R, CID, SD>)
-> PopupConfigure; -> PopupConfigure,
/// The client requested the start of an interactive move for this surface /// The client requested the start of an interactive move for this surface
fn move_(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>, pub move_: fn(
seat: &wl_seat::WlSeat, serial: u32); evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
),
/// The client requested the start of an interactive resize for this surface /// The client requested the start of an interactive resize for this surface
/// ///
/// The `edges` argument specifies which part of the window's border is being dragged. /// The `edges` argument specifies which part of the window's border is being dragged.
fn resize(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>, pub resize: fn(
seat: &wl_seat::WlSeat, serial: u32, edges: zxdg_toplevel_v6::ResizeEdge); evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
edges: zxdg_toplevel_v6::ResizeEdge,
),
/// This popup requests a grab of the pointer /// This popup requests a grab of the pointer
/// ///
/// This means it requests to be sent a `popup_done` event when the pointer leaves /// This means it requests to be sent a `popup_done` event when the pointer leaves
/// the grab area. /// the grab area.
fn grab(&mut self, evlh: &mut EventLoopHandle, surface: PopupSurface<U, R, H, SD>, pub grab: fn(
seat: &wl_seat::WlSeat, serial: u32); evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: PopupSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
),
/// A toplevel surface requested its display state to be changed /// A toplevel surface requested its display state to be changed
/// ///
/// Each field represents the request of the client for a specific property: /// Each field represents the request of the client for a specific property:
@ -861,14 +900,33 @@ pub trait Handler<U, R, H, SD> {
/// ///
/// You are to answer with a `ToplevelConfigure` that will be sent to the client in /// You are to answer with a `ToplevelConfigure` that will be sent to the client in
/// response. /// response.
fn change_display_state(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>, pub change_display_state: fn(
maximized: Option<bool>, minimized: Option<bool>, fullscreen: Option<bool>, evlh: &mut EventLoopHandle,
output: Option<&wl_output::WlOutput>) idata: &mut SID,
-> ToplevelConfigure; surface: ToplevelSurface<U, R, CID, SD>,
maximized: Option<bool>,
minimized: Option<bool>,
fullscreen: Option<bool>,
output: Option<&wl_output::WlOutput>,
) -> ToplevelConfigure,
/// The client requests the window menu to be displayed on this surface at this location /// The client requests the window menu to be displayed on this surface at this location
/// ///
/// This menu belongs to the compositor. It is typically expected to contain options for /// This menu belongs to the compositor. It is typically expected to contain options for
/// control of the window (maximize/minimize/close/move/etc...). /// control of the window (maximize/minimize/close/move/etc...).
fn show_window_menu(&mut self, evlh: &mut EventLoopHandle, surface: ToplevelSurface<U, R, H, SD>, pub show_window_menu: fn(
seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32); evlh: &mut EventLoopHandle,
idata: &mut SID,
surface: ToplevelSurface<U, R, CID, SD>,
seat: &wl_seat::WlSeat,
serial: u32,
x: i32,
y: i32,
),
}
impl<U, R, CID, SID, SD> Copy for ShellSurfaceUserImplementation<U, R, CID, SID, SD> {}
impl<U, R, CID, SID, SD> Clone for ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
fn clone(&self) -> ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
*self
}
} }

View File

@ -1,35 +1,49 @@
use super::{Handler as UserHandler, PopupConfigure, PopupState, PositionerState, ShellClient, use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellHandler, ShellSurfacePendingState, ShellSurfaceRole, ToplevelConfigure, ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelState}; ToplevelConfigure, ToplevelState};
use compositor::{CompositorToken, Rectangle};
use compositor::{CompositorToken, Handler as CompositorHandler, Rectangle};
use compositor::roles::*; use compositor::roles::*;
use std::sync::Mutex; use std::sync::Mutex;
use wayland_protocols::unstable::xdg_shell::server::{zxdg_positioner_v6 as xdg_positioner, zxdg_toplevel_v6}; use wayland_protocols::unstable::xdg_shell::server::{zxdg_positioner_v6 as xdg_positioner, zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle, Resource};
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};
use wayland_server::{Client, Destroy, EventLoopHandle, Resource}; pub(crate) fn wl_shell_bind<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface}; idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>,
_: &Client, shell: wl_shell::WlShell)
pub struct WlShellDestructor<SD> { where
_data: ::std::marker::PhantomData<SD>, U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: Default + 'static,
{
shell.set_user_data(Box::into_raw(Box::new(Mutex::new((
make_shell_client_data::<SD>(),
Vec::<wl_shell_surface::WlShellSurface>::new(),
)))) as *mut _);
evlh.register(
&shell,
shell_implementation(),
idata.clone(),
Some(destroy_shell::<SD>),
);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.new_client)(evlh, &mut *user_idata, make_shell_client(&shell));
} }
/* /*
* wl_shell * wl_shell
*/ */
pub type ShellUserData<SD> = Mutex<(ShellClientData<SD>, Vec<wl_shell_surface::WlShellSurface>)>; pub(crate) type ShellUserData<SD> = Mutex<(ShellClientData<SD>, Vec<wl_shell_surface::WlShellSurface>)>;
impl<SD> Destroy<wl_shell::WlShell> for WlShellDestructor<SD> { fn destroy_shell<SD>(shell: &wl_shell::WlShell) {
fn destroy(shell: &wl_shell::WlShell) { let ptr = shell.get_user_data();
let ptr = shell.get_user_data(); shell.set_user_data(::std::ptr::null_mut());
shell.set_user_data(::std::ptr::null_mut()); let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) }; // explicitly call drop to not forget what we're doing here
// explicitly call drop to not forget what we're doing here ::std::mem::drop(data);
::std::mem::drop(data);
}
} }
pub fn make_shell_client<SD>(resource: &wl_shell::WlShell) -> ShellClient<SD> { pub fn make_shell_client<SD>(resource: &wl_shell::WlShell) -> ShellClient<SD> {
@ -39,69 +53,68 @@ pub fn make_shell_client<SD>(resource: &wl_shell::WlShell) -> ShellClient<SD> {
} }
} }
impl<U, R, H, SH, SD> wl_shell::Handler for ShellHandler<U, R, H, SH, SD> fn shell_implementation<U, R, CID, SID, SD>(
)
-> wl_shell::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where where
U: Send + 'static, U: 'static,
R: Role<ShellSurfaceRole> + Send + 'static, R: Role<ShellSurfaceRole> + 'static,
H: CompositorHandler<U, R> + Send + 'static, CID: 'static,
SH: UserHandler<U, R, H, SD> + Send + 'static, SID: 'static,
SD: Send + 'static, SD: 'static,
{ {
fn get_shell_surface(&mut self, evlh: &mut EventLoopHandle, _: &Client, resource: &wl_shell::WlShell, wl_shell::Implementation {
id: wl_shell_surface::WlShellSurface, surface: &wl_surface::WlSurface) { get_shell_surface: |evlh, idata, _, shell, shell_surface, surface| {
trace!(self.log, "Creating new wl_shell_surface."); let role_data = ShellSurfaceRole {
let role_data = ShellSurfaceRole { pending_state: ShellSurfacePendingState::None,
pending_state: ShellSurfacePendingState::None, window_geometry: None,
window_geometry: None, pending_configures: Vec::new(),
pending_configures: Vec::new(), configured: true,
configured: true, };
}; if let Err(_) = idata.compositor_token.give_role_with(surface, role_data) {
if let Err(_) = self.token.give_role_with(surface, role_data) { shell.post_error(
resource.post_error( wl_shell::Error::Role as u32,
wl_shell::Error::Role as u32, "Surface already has a role.".into(),
"Surface already has a role.".into(), );
return;
}
shell_surface.set_user_data(
Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _,
);
evlh.register(
&shell_surface,
shell_surface_implementation(),
idata.clone(),
Some(destroy_shell_surface),
); );
return;
}
id.set_user_data(
Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _,
);
evlh.register_with_destructor::<_, Self, WlShellDestructor<SD>>(&id, self.my_id);
// register ourselves to the wl_shell for ping handling // register ourselves to the wl_shell for ping handling
let mutex = unsafe { &*(resource.get_user_data() as *mut ShellUserData<SD>) }; let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap(); let mut guard = mutex.lock().unwrap();
if guard.1.len() == 0 && guard.0.pending_ping != 0 { if guard.1.len() == 0 && guard.0.pending_ping != 0 {
// there is a pending ping that no surface could receive yet, send it // there is a pending ping that no surface could receive yet, send it
// note this is not possible that it was received and then a wl_shell_surface was // note this is not possible that it was received and then a wl_shell_surface was
// destroyed, because wl_shell_surface has no destructor! // destroyed, because wl_shell_surface has no destructor!
id.ping(guard.0.pending_ping); shell_surface.ping(guard.0.pending_ping);
} }
guard.1.push(id); guard.1.push(shell_surface);
},
} }
} }
server_declare_handler!(
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH:[UserHandler<U,R,H,SD>, Send], SD: [Send]>,
wl_shell::Handler,
wl_shell::WlShell
);
/* /*
* wl_shell_surface * wl_shell_surface
*/ */
pub type ShellSurfaceUserData = (wl_surface::WlSurface, wl_shell::WlShell); pub type ShellSurfaceUserData = (wl_surface::WlSurface, wl_shell::WlShell);
impl<SD> Destroy<wl_shell_surface::WlShellSurface> for WlShellDestructor<SD> { fn destroy_shell_surface(shell_surface: &wl_shell_surface::WlShellSurface) {
fn destroy(shell_surface: &wl_shell_surface::WlShellSurface) { let ptr = shell_surface.get_user_data();
let ptr = shell_surface.get_user_data(); shell_surface.set_user_data(::std::ptr::null_mut());
shell_surface.set_user_data(::std::ptr::null_mut()); // drop the WlSurface object
// drop the WlSurface object let surface = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
let surface = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) }; // explicitly call drop to not forget what we're doing here
// explicitly call drop to not forget what we're doing here ::std::mem::drop(surface);
::std::mem::drop(surface);
}
} }
fn make_toplevel_handle<U, R, H, SD>(token: CompositorToken<U, R, H>, fn make_toplevel_handle<U, R, H, SD>(token: CompositorToken<U, R, H>,
@ -140,231 +153,294 @@ pub fn send_popup_configure(resource: &wl_shell_surface::WlShellSurface, configu
resource.configure(wl_shell_surface::Resize::empty(), w, h); resource.configure(wl_shell_surface::Resize::empty(), w, h);
} }
impl<U, R, H, SH, SD> ShellHandler<U, R, H, SH, SD> fn wl_handle_display_state_change<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
where idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
U: Send + 'static, shell_surface: &wl_shell_surface::WlShellSurface,
R: Role<ShellSurfaceRole> + Send + 'static, maximized: Option<bool>, minimized: Option<bool>,
H: CompositorHandler<U, R> + Send + 'static, fullscreen: Option<bool>,
SH: UserHandler<U, R, H, SD> + Send + 'static, output: Option<&wl_output::WlOutput>) {
SD: Send + 'static, let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
{ // handler callback
fn wl_handle_display_state_change(&mut self, evlh: &mut EventLoopHandle, let mut user_idata = idata.idata.borrow_mut();
resource: &wl_shell_surface::WlShellSurface, let configure = (idata.implementation.change_display_state)(
maximized: Option<bool>, minimized: Option<bool>, evlh,
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>) { &mut *user_idata,
let handle = make_toplevel_handle(self.token, resource); handle,
// handler callback maximized,
let configure = minimized,
self.handler fullscreen,
.change_display_state(evlh, handle, maximized, minimized, fullscreen, output); output,
// send the configure response to client );
let (w, h) = configure.size.unwrap_or((0, 0)); // send the configure response to client
resource.configure(wl_shell_surface::None, w, h); let (w, h) = configure.size.unwrap_or((0, 0));
} shell_surface.configure(wl_shell_surface::None, w, h);
}
fn wl_ensure_toplevel(&mut self, evlh: &mut EventLoopHandle, fn wl_set_parent<U, R, CID, SID, SD>(idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
resource: &wl_shell_surface::WlShellSurface) { shell_surface: &wl_shell_surface::WlShellSurface,
let ptr = resource.get_user_data(); parent: Option<wl_surface::WlSurface>)
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) }; where
// copy token to make borrow checker happy U: 'static,
let token = self.token; R: Role<ShellSurfaceRole> + 'static,
let need_send = token CID: 'static,
.with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| { SID: 'static,
match data.pending_state { SD: 'static,
ShellSurfacePendingState::Toplevel(_) => { {
return false; let ptr = shell_surface.get_user_data();
} let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
ShellSurfacePendingState::Popup(_) => { idata
// this is no longer a popup, deregister it .compositor_token
self.known_popups.retain(|other| { .with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.parent = parent;
}
_ => unreachable!(),
})
.unwrap();
}
fn wl_ensure_toplevel<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
shell_surface: &wl_shell_surface::WlShellSurface)
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
let ptr = shell_surface.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
// copy token to make borrow checker happy
let token = idata.compositor_token;
let need_send = token
.with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| {
match data.pending_state {
ShellSurfacePendingState::Toplevel(_) => {
return false;
}
ShellSurfacePendingState::Popup(_) => {
// this is no longer a popup, deregister it
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.retain(|other| {
other other
.get_surface() .get_surface()
.map(|s| !s.equals(wl_surface)) .map(|s| !s.equals(wl_surface))
.unwrap_or(false) .unwrap_or(false)
}); });
}
ShellSurfacePendingState::None => {}
} }
// This was not previously toplevel, need to make it toplevel ShellSurfacePendingState::None => {}
data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
return true;
})
.expect(
"xdg_surface exists but surface has not shell_surface role?!",
);
// we need to notify about this new toplevel surface
if need_send {
let handle = make_toplevel_handle(self.token, resource);
let configure = self.handler.new_toplevel(evlh, handle);
send_toplevel_configure(resource, configure);
}
}
}
impl<U, R, H, SH, SD> wl_shell_surface::Handler for ShellHandler<U, R, H, SH, SD>
where
U: Send + 'static,
R: Role<ShellSurfaceRole> + Send + 'static,
H: CompositorHandler<U, R> + Send + 'static,
SH: UserHandler<U, R, H, SD> + Send + 'static,
SD: Send + 'static,
{
fn pong(&mut self, evlh: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface, serial: u32) {
let &(_, ref shell) = unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.0.pending_ping == serial {
guard.0.pending_ping = 0;
true
} else {
false
} }
}; // This was not previously toplevel, need to make it toplevel
if valid { data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState {
self.handler.client_pong(evlh, make_shell_client(shell)); parent: None,
} title: String::new(),
} app_id: String::new(),
min_size: (0, 0),
fn move_(&mut self, evlh: &mut EventLoopHandle, _: &Client, max_size: (0, 0),
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32) { });
let handle = make_toplevel_handle(self.token, resource); return true;
self.handler.move_(evlh, handle, seat, serial); })
} .expect(
"xdg_surface exists but surface has not shell_surface role?!",
fn resize(&mut self, evlh: &mut EventLoopHandle, _: &Client, );
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32, // we need to notify about this new toplevel surface
edges: wl_shell_surface::Resize) { if need_send {
let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges.bits()) evlh.state()
.unwrap_or(zxdg_toplevel_v6::ResizeEdge::None); .get_mut(&idata.state_token)
let handle = make_toplevel_handle(self.token, resource); .known_toplevels
self.handler.resize(evlh, handle, seat, serial, edges); .push(make_toplevel_handle(idata.compositor_token, shell_surface));
} let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
fn set_toplevel(&mut self, evlh: &mut EventLoopHandle, _: &Client, let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle);
resource: &wl_shell_surface::WlShellSurface) { send_toplevel_configure(shell_surface, configure);
self.wl_ensure_toplevel(evlh, resource);
self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(false), None)
}
fn set_transient(&mut self, evlh: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface, parent: &wl_surface::WlSurface, _x: i32,
_y: i32, _flags: wl_shell_surface::Transient) {
self.wl_ensure_toplevel(evlh, resource);
// set the parent
let ptr = resource.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
self.token
.with_role_data::<ShellSurfaceRole, _, _>(wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.parent = Some(unsafe { parent.clone_unchecked() });
}
_ => unreachable!(),
})
.unwrap();
// set as regular surface
self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(false), None)
}
fn set_fullscreen(&mut self, evlh: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface,
_method: wl_shell_surface::FullscreenMethod, _framerate: u32,
output: Option<&wl_output::WlOutput>) {
self.wl_ensure_toplevel(evlh, resource);
self.wl_handle_display_state_change(evlh, resource, Some(false), Some(false), Some(true), output)
}
fn set_popup(&mut self, evlh: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface, seat: &wl_seat::WlSeat, serial: u32,
parent: &wl_surface::WlSurface, x: i32, y: i32, _: wl_shell_surface::Transient) {
let ptr = resource.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
// we are reseting the popup state, so remove this surface from everywhere
self.known_toplevels.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
self.known_popups.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
self.token
.with_role_data(wl_surface, |data| {
data.pending_state = ShellSurfacePendingState::Popup(PopupState {
parent: unsafe { parent.clone_unchecked() },
positioner: PositionerState {
rect_size: (1, 1),
anchor_rect: Rectangle {
x,
y,
width: 1,
height: 1,
},
anchor_edges: xdg_positioner::Anchor::empty(),
gravity: xdg_positioner::Gravity::empty(),
constraint_adjustment: xdg_positioner::ConstraintAdjustment::empty(),
offset: (0, 0),
},
});
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
// notify the handler about this new popup
let handle = make_popup_handle(self.token, resource);
let configure = self.handler.new_popup(evlh, handle);
send_popup_configure(resource, configure);
self.handler
.grab(evlh, make_popup_handle(self.token, resource), seat, serial);
}
fn set_maximized(&mut self, evlh: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface, output: Option<&wl_output::WlOutput>) {
self.wl_ensure_toplevel(evlh, resource);
self.wl_handle_display_state_change(evlh, resource, Some(true), Some(false), Some(false), output)
}
fn set_title(&mut self, _: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface, title: String) {
let ptr = resource.get_user_data();
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
self.token
.with_role_data(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.title = title;
}
_ => {}
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
}
fn set_class(&mut self, _: &mut EventLoopHandle, _: &Client,
resource: &wl_shell_surface::WlShellSurface, class_: String) {
let ptr = resource.get_user_data();
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
self.token
.with_role_data(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.app_id = class_;
}
_ => {}
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
} }
} }
server_declare_handler!( fn shell_surface_implementation<U, R, CID, SID, SD>(
ShellHandler<U: [Send], R: [Role<ShellSurfaceRole>, Send], H:[CompositorHandler<U, R>, Send], SH: [UserHandler<U,R,H,SD>, Send], SD: [Send]>, )
wl_shell_surface::Handler, -> wl_shell_surface::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
wl_shell_surface::WlShellSurface where
); U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
wl_shell_surface::Implementation {
pong: |evlh, idata, _, shell_surface, serial| {
let &(_, ref shell) = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) };
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.0.pending_ping == serial {
guard.0.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.client_pong)(evlh, &mut *user_idata, make_shell_client(shell));
}
},
move_: |evlh, idata, _, shell_surface, seat, serial| {
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.move_)(evlh, &mut *user_idata, handle, seat, serial);
},
resize: |evlh, idata, _, shell_surface, seat, serial, edges| {
let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges.bits())
.unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.resize)(evlh, &mut *user_idata, handle, seat, serial, edges);
},
set_toplevel: |evlh, idata, _, shell_surface| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(idata, shell_surface, None);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(false),
Some(false),
Some(false),
None,
)
},
set_transient: |evlh, idata, _, shell_surface, parent, _, _, _| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(
idata,
shell_surface,
Some(unsafe { parent.clone_unchecked() }),
);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(false),
Some(false),
Some(false),
None,
)
},
set_fullscreen: |evlh, idata, _, shell_surface, _, _, output| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(idata, shell_surface, None);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(false),
Some(false),
Some(true),
output,
)
},
set_popup: |evlh, idata, _, shell_surface, seat, serial, parent, x, y, _| {
let ptr = shell_surface.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
// we are reseting the popup state, so remove this surface from everywhere
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
idata
.compositor_token
.with_role_data(wl_surface, |data| {
data.pending_state = ShellSurfacePendingState::Popup(PopupState {
parent: unsafe { parent.clone_unchecked() },
positioner: PositionerState {
rect_size: (1, 1),
anchor_rect: Rectangle {
x,
y,
width: 1,
height: 1,
},
anchor_edges: xdg_positioner::Anchor::empty(),
gravity: xdg_positioner::Gravity::empty(),
constraint_adjustment: xdg_positioner::ConstraintAdjustment::empty(),
offset: (0, 0),
},
});
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
// notify the handler about this new popup
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.push(make_popup_handle(idata.compositor_token, shell_surface));
let handle = make_popup_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_popup)(evlh, &mut *user_idata, handle);
send_popup_configure(shell_surface, configure);
(idata.implementation.grab)(
evlh,
&mut *user_idata,
make_popup_handle(idata.compositor_token, shell_surface),
seat,
serial,
);
},
set_maximized: |evlh, idata, _, shell_surface, output| {
wl_ensure_toplevel(evlh, idata, shell_surface);
wl_set_parent(idata, shell_surface, None);
wl_handle_display_state_change(
evlh,
idata,
shell_surface,
Some(true),
Some(false),
Some(false),
output,
)
},
set_title: |_, idata, _, shell_surface, title| {
let ptr = shell_surface.get_user_data();
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.title = title;
}
_ => {}
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
},
set_class: |_, idata, _, shell_surface, class| {
let ptr = shell_surface.get_user_data();
let &(ref surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut state) => {
state.app_id = class;
}
_ => {}
})
.expect("wl_shell_surface exists but wl_surface has wrong role?!");
},
}
}

File diff suppressed because it is too large Load Diff

View File

@ -19,39 +19,39 @@
//! extern crate wayland_server; //! extern crate wayland_server;
//! extern crate smithay; //! extern crate smithay;
//! //!
//! use smithay::shm::ShmGlobal; //! use smithay::shm::init_shm_global;
//! use wayland_server::protocol::wl_shm; //! use wayland_server::protocol::wl_shm::Format;
//! //!
//! # fn main() { //! # fn main() {
//! # let (_, mut event_loop) = wayland_server::create_display(); //! # let (_, mut event_loop) = wayland_server::create_display();
//! //! // Insert the ShmGlobal into your event loop
//! // Insert the ShmGlobal as a handler to your event loop
//! // Here, we specify that Yuyv and C8 format are supported //! // Here, we specify that Yuyv and C8 format are supported
//! // additionnaly to the standart Argb8888 and Xrgb8888. //! // additionnaly to the standart Argb8888 and Xrgb8888.
//! let handler_id = event_loop.add_handler_with_init(ShmGlobal::new( //! let shm_global = init_shm_global(
//! vec![wl_shm::Format::Yuyv, wl_shm::Format::C8], //! &mut event_loop,
//! vec![Format::Yuyv, Format::C8],
//! None // we don't provide a logger here //! None // we don't provide a logger here
//! )); //! );
//! // Register this handler to advertise a wl_shm global of version 1
//! let shm_global = event_loop.register_global::<wl_shm::WlShm,ShmGlobal>(handler_id, 1);
//! // Retrieve the shm token for later use to access the buffers
//! let shm_token = {
//! let state = event_loop.state();
//! state.get_handler::<ShmGlobal>(handler_id).get_token()
//! };
//!
//! # } //! # }
//! ``` //! ```
//! //!
//! Then, when you have a `WlBuffer` and need to retrieve its contents, use the token method to //! Then, when you have a `WlBuffer` and need to retrieve its contents, use the token method to
//! do it: //! do it:
//! //!
//! ```ignore //! ```
//! shm_token.with_buffer_contents(&buffer, //! # extern crate wayland_server;
//! |slice: &[u8], buffer_metadata: &BufferData| { //! # extern crate smithay;
//! # use wayland_server::protocol::wl_buffer::WlBuffer;
//! # fn wrap(buffer: &WlBuffer) {
//! use smithay::shm::{with_buffer_contents, BufferData};
//!
//! with_buffer_contents(&buffer,
//! |slice: &[u8], buffer_metadata: BufferData| {
//! // do something to draw it on the screen //! // do something to draw it on the screen
//! } //! }
//! ); //! );
//! # }
//! # fn main() {}
//! ``` //! ```
//! //!
//! **Note** //! **Note**
@ -63,67 +63,50 @@
use self::pool::{Pool, ResizeError}; use self::pool::{Pool, ResizeError};
use std::rc::Rc;
use std::os::unix::io::RawFd;
use std::sync::Arc; use std::sync::Arc;
use wayland_server::{resource_is_registered, Client, EventLoop, EventLoopHandle, Global, Resource};
use wayland_server::{resource_is_registered, Client, Destroy, EventLoopHandle, GlobalHandler, Init, Resource};
use wayland_server::protocol::{wl_buffer, wl_shm, wl_shm_pool}; use wayland_server::protocol::{wl_buffer, wl_shm, wl_shm_pool};
mod pool; mod pool;
/// A global for handling SHM pool and buffers #[derive(Clone)]
/// Internal data storage of ShmGlobal
/// ///
/// You must register it to an event loop using `register_with_init`, or it will /// This type is only visible as type parameter of
/// quickly panic. /// the `Global` handle you are provided.
pub struct ShmGlobal { pub struct ShmGlobalData {
formats: Vec<wl_shm::Format>, formats: Rc<Vec<wl_shm::Format>>,
handler_id: Option<usize>,
log: ::slog::Logger, log: ::slog::Logger,
} }
impl ShmGlobal { /// Create a new SHM global advertizing given supported formats.
/// Create a new SHM global advertizing given supported formats.
///
/// This global will always advertize `ARGB8888` and `XRGB8888` format
/// as they are required by the protocol. Formats given as argument
/// as additionnaly advertized.
pub fn new<L>(mut formats: Vec<wl_shm::Format>, logger: L) -> ShmGlobal
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
// always add the mandatory formats
formats.push(wl_shm::Format::Argb8888);
formats.push(wl_shm::Format::Xrgb8888);
ShmGlobal {
formats: formats,
handler_id: None,
log: log.new(o!("smithay_module" => "shm_handler")),
}
}
/// Retreive a token from the SHM global.
///
/// This can only be called once the `ShmGlobal` has been added to and event loop
/// and has been initialized. If it is not the case, this method will panic.
///
/// This is needed to retrieve the contents of the shm pools and buffers.
pub fn get_token(&self) -> ShmToken {
ShmToken {
hid: self.handler_id.expect("ShmGlobal was not initialized."),
}
}
}
/// An SHM global token
/// ///
/// It is needed to access the contents of the buffers & pools managed by the /// This global will always advertize `ARGB8888` and `XRGB8888` format
/// associated `ShmGlobal`. /// as they are required by the protocol. Formats given as argument
#[derive(Clone)] /// as additionnaly advertized.
pub struct ShmToken { ///
hid: usize, /// The global is directly registered into the eventloop, and this function
/// returns the global handle, in case you whish to remove this global in
/// the future.
pub fn init_shm_global<L>(evl: &mut EventLoop, mut formats: Vec<wl_shm::Format>, logger: L)
-> Global<wl_shm::WlShm, ShmGlobalData>
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
// always add the mandatory formats
formats.push(wl_shm::Format::Argb8888);
formats.push(wl_shm::Format::Xrgb8888);
let data = ShmGlobalData {
formats: Rc::new(formats),
log: log.new(o!("smithay_module" => "shm_handler")),
};
let global = evl.register_global::<wl_shm::WlShm, _>(1, shm_global_bind, data);
global
} }
/// Error that can occur when accessing an SHM buffer /// Error that can occur when accessing an SHM buffer
@ -140,109 +123,82 @@ pub enum BufferAccessError {
BadMap, BadMap,
} }
impl ShmToken { /// Call given closure with the contents of the given buffer
/// Call given closure with the contents of the given buffer ///
/// /// If the buffer is managed by the provided ShmGlobal, its contents are
/// If the buffer is managed by the associated ShmGlobal, its contents are /// extracted and the closure is extracted with them:
/// extracted and the closure is extracted with them: ///
/// /// - The first argument is a data slice of the contents of the pool
/// - The first argument is a data slice of the contents of the pool /// - The second argument is the specification of this buffer is this pool
/// - The second argument is the specification of this buffer is this pool ///
/// /// If the buffer is not managed by the provided ShmGlobal, the closure is not called
/// If the buffer is not managed by the associated ShmGlobal, the closure is not called /// and this method will return `Err(())` (this will be the case for an EGL buffer for example).
/// and this method will return `Err(())` (this will be the case for an EGL buffer for example). pub fn with_buffer_contents<F>(buffer: &wl_buffer::WlBuffer, f: F) -> Result<(), BufferAccessError>
pub fn with_buffer_contents<F>(&self, buffer: &wl_buffer::WlBuffer, f: F) -> Result<(), BufferAccessError> where
where F: FnOnce(&[u8], BufferData),
F: FnOnce(&[u8], BufferData), {
if !resource_is_registered(buffer, &buffer_implementation()) {
return Err(BufferAccessError::NotManaged);
}
let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) };
if data.pool
.with_data_slice(|slice| f(slice, data.data))
.is_err()
{ {
if !resource_is_registered::<_, ShmHandler>(buffer, self.hid) { // SIGBUS error occured
return Err(BufferAccessError::NotManaged); buffer.post_error(wl_shm::Error::InvalidFd as u32, "Bad pool size.".into());
} return Err(BufferAccessError::BadMap);
let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) }; }
Ok(())
}
if data.pool fn shm_global_bind(evlh: &mut EventLoopHandle, data: &mut ShmGlobalData, _: &Client, global: wl_shm::WlShm) {
.with_data_slice(|slice| f(slice, data.data)) // register an handler for this shm
.is_err() evlh.register(&global, shm_implementation(), data.clone(), None);
{ // and then the custom formats
// SIGBUS error occured for f in &data.formats[..] {
buffer.post_error(wl_shm::Error::InvalidFd as u32, "Bad pool size.".into()); global.format(*f);
return Err(BufferAccessError::BadMap);
}
Ok(())
} }
} }
impl Init for ShmGlobal { fn shm_implementation() -> wl_shm::Implementation<ShmGlobalData> {
fn init(&mut self, evqh: &mut EventLoopHandle, _index: usize) { wl_shm::Implementation {
let id = evqh.add_handler_with_init(ShmHandler { create_pool: |evlh, data, _, shm, pool, fd, size| {
my_id: ::std::usize::MAX, if size <= 0 {
valid_formats: self.formats.clone(),
log: self.log.clone(),
});
self.handler_id = Some(id);
}
}
impl GlobalHandler<wl_shm::WlShm> for ShmGlobal {
fn bind(&mut self, evqh: &mut EventLoopHandle, _: &Client, global: wl_shm::WlShm) {
let hid = self.handler_id.expect("ShmGlobal was not initialized.");
// register an handler for this shm
evqh.register::<_, ShmHandler>(&global, hid);
// and then the custom formats
for f in &self.formats {
global.format(*f);
}
}
}
struct ShmHandler {
my_id: usize,
valid_formats: Vec<wl_shm::Format>,
log: ::slog::Logger,
}
impl Init for ShmHandler {
fn init(&mut self, _evqh: &mut EventLoopHandle, index: usize) {
self.my_id = index;
debug!(self.log, "Init finished")
}
}
impl wl_shm::Handler for ShmHandler {
fn create_pool(&mut self, evqh: &mut EventLoopHandle, _client: &Client, shm: &wl_shm::WlShm,
pool: wl_shm_pool::WlShmPool, fd: RawFd, size: i32) {
if size <= 0 {
shm.post_error(
wl_shm::Error::InvalidFd as u32,
"Invalid size for a new wl_shm_pool.".into(),
);
return;
}
let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) {
Ok(p) => p,
Err(()) => {
shm.post_error( shm.post_error(
wl_shm::Error::InvalidFd as u32, wl_shm::Error::InvalidFd as u32,
format!("Failed mmap of fd {}.", fd), "Invalid size for a new wl_shm_pool.".into(),
); );
return; return;
} }
}; let mmap_pool = match Pool::new(fd, size as usize, data.log.clone()) {
let arc_pool = Box::new(Arc::new(mmap_pool)); Ok(p) => p,
evqh.register_with_destructor::<_, ShmHandler, ShmHandler>(&pool, self.my_id); Err(()) => {
pool.set_user_data(Box::into_raw(arc_pool) as *mut ()); shm.post_error(
wl_shm::Error::InvalidFd as u32,
format!("Failed mmap of fd {}.", fd),
);
return;
}
};
let arc_pool = Box::new(Arc::new(mmap_pool));
evlh.register(
&pool,
shm_pool_implementation(),
data.clone(),
Some(destroy_shm_pool),
);
pool.set_user_data(Box::into_raw(arc_pool) as *mut ());
},
} }
} }
impl Destroy<wl_shm_pool::WlShmPool> for ShmHandler { fn destroy_shm_pool(pool: &wl_shm_pool::WlShmPool) {
fn destroy(pool: &wl_shm_pool::WlShmPool) { let arc_pool = unsafe { Box::from_raw(pool.get_user_data() as *mut Arc<Pool>) };
let arc_pool = unsafe { Box::from_raw(pool.get_user_data() as *mut Arc<Pool>) }; drop(arc_pool)
drop(arc_pool)
}
} }
declare_handler!(ShmHandler, wl_shm::Handler, wl_shm::WlShm);
/// Details of the contents of a buffer relative to its pool /// Details of the contents of a buffer relative to its pool
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct BufferData { pub struct BufferData {
@ -263,56 +219,53 @@ struct InternalBufferData {
data: BufferData, data: BufferData,
} }
impl wl_shm_pool::Handler for ShmHandler { fn shm_pool_implementation() -> wl_shm_pool::Implementation<ShmGlobalData> {
fn create_buffer(&mut self, evqh: &mut EventLoopHandle, _client: &Client, wl_shm_pool::Implementation {
pool: &wl_shm_pool::WlShmPool, buffer: wl_buffer::WlBuffer, offset: i32, width: i32, create_buffer: |evlh, data, _, pool, buffer, offset, width, height, stride, format| {
height: i32, stride: i32, format: wl_shm::Format) { if !data.formats.contains(&format) {
if !self.valid_formats.contains(&format) { buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new());
buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new()); return;
return;
}
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
let data = Box::into_raw(Box::new(InternalBufferData {
pool: arc_pool.clone(),
data: BufferData {
offset: offset,
width: width,
height: height,
stride: stride,
format: format,
},
}));
evqh.register_with_destructor::<_, ShmHandler, ShmHandler>(&buffer, self.my_id);
buffer.set_user_data(data as *mut ());
}
fn resize(&mut self, _evqh: &mut EventLoopHandle, _client: &Client, pool: &wl_shm_pool::WlShmPool,
size: i32) {
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
match arc_pool.resize(size) {
Ok(()) => {}
Err(ResizeError::InvalidSize) => {
pool.post_error(
wl_shm::Error::InvalidFd as u32,
"Invalid new size for a wl_shm_pool.".into(),
);
} }
Err(ResizeError::MremapFailed) => { let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into()); let data = Box::into_raw(Box::new(InternalBufferData {
pool: arc_pool.clone(),
data: BufferData {
offset: offset,
width: width,
height: height,
stride: stride,
format: format,
},
}));
evlh.register(&buffer, buffer_implementation(), (), Some(destroy_buffer));
buffer.set_user_data(data as *mut ());
},
resize: |_, _, _, pool, size| {
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
match arc_pool.resize(size) {
Ok(()) => {}
Err(ResizeError::InvalidSize) => {
pool.post_error(
wl_shm::Error::InvalidFd as u32,
"Invalid new size for a wl_shm_pool.".into(),
);
}
Err(ResizeError::MremapFailed) => {
pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into());
}
} }
} },
destroy: |_, _, _, _| {},
} }
} }
impl Destroy<wl_buffer::WlBuffer> for ShmHandler { fn destroy_buffer(buffer: &wl_buffer::WlBuffer) {
fn destroy(buffer: &wl_buffer::WlBuffer) { let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) };
let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) }; drop(buffer_data)
drop(buffer_data)
}
} }
declare_handler!(ShmHandler, wl_shm_pool::Handler, wl_shm_pool::WlShmPool); fn buffer_implementation() -> wl_buffer::Implementation<()> {
wl_buffer::Implementation {
impl wl_buffer::Handler for ShmHandler {} destroy: |_, _, _, _| {},
}
declare_handler!(ShmHandler, wl_buffer::Handler, wl_buffer::WlBuffer); }