Merge pull request #88 from Smithay/wayland_rs_20

Upgrade to wayland-rs 0.20
This commit is contained in:
Victor Berger 2018-04-24 14:45:17 +02:00 committed by GitHub
commit 60fc47917d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 4950 additions and 3745 deletions

View File

@ -1,10 +1 @@
error_on_line_overflow = false
fn_args_density = "Compressed"
fn_args_layout = "Visual"
fn_arg_intent = "Tabbed"
reorder_imports = true
reorder_imported_names = true
report_todo = "Never"
report_fixme = "Never"
use_try_shorthand = true
max_width = 110 max_width = 110

View File

@ -8,6 +8,10 @@ rust:
sudo: required sudo: required
branches:
only:
- master
dist: trusty dist: trusty
# We cannot cache .vagga, because we actually do not have read permissions # We cannot cache .vagga, because we actually do not have read permissions

View File

@ -7,15 +7,15 @@ description = "Smithay is a library for writing wayland compositors."
repository = "https://github.com/Smithay/smithay" repository = "https://github.com/Smithay/smithay"
[dependencies] [dependencies]
wayland-server = "0.14.0" wayland-server = "0.20.1"
wayland-sys = "0.14.0" wayland-sys = "0.20.1"
nix = "0.9.0" nix = "0.10.0"
xkbcommon = "0.2.1" xkbcommon = "0.2.1"
tempfile = "2.1.5" tempfile = "2.1.5"
slog = { version = "2.1.1" } slog = { version = "2.1.1" }
slog-stdlog = "3.0.2" slog-stdlog = "3.0.2"
libloading = "0.4.0" libloading = "0.4.0"
wayland-client = { version = "0.12.5", optional = true } wayland-client = { version = "0.20.1", optional = true }
winit = { version = "0.10.0", optional = true } winit = { version = "0.10.0", optional = true }
drm = { version = "^0.3.1", optional = true } drm = { version = "^0.3.1", optional = true }
gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] } gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] }
@ -24,7 +24,7 @@ input = { version = "0.4.0", optional = true }
udev = { version = "0.2.0", optional = true } udev = { version = "0.2.0", optional = true }
dbus = { version = "0.6.1", optional = true } dbus = { version = "0.6.1", optional = true }
systemd = { version = "^0.2.0", optional = true } systemd = { version = "^0.2.0", optional = true }
wayland-protocols = { version = "0.14.0", features = ["unstable_protocols", "server"] } wayland-protocols = { version = "0.20.1", features = ["unstable_protocols", "server"] }
image = "0.17.0" image = "0.17.0"
error-chain = "0.11.0" error-chain = "0.11.0"
lazy_static = "1.0.0" lazy_static = "1.0.0"
@ -38,7 +38,7 @@ slog-async = "2.2"
rand = "0.3" rand = "0.3"
[features] [features]
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium", "backend_session_logind"] default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"]
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"] backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
backend_drm = ["drm", "gbm"] backend_drm = ["drm", "gbm"]
backend_libinput = ["input"] backend_libinput = ["input"]
@ -47,3 +47,4 @@ backend_session_udev = ["udev", "backend_session"]
backend_session_logind = ["dbus", "systemd", "backend_session"] backend_session_logind = ["dbus", "systemd", "backend_session"]
backend_udev = ["udev", "backend_drm", "backend_session_udev"] backend_udev = ["udev", "backend_drm", "backend_session_udev"]
renderer_glium = ["glium"] renderer_glium = ["glium"]

View File

@ -34,7 +34,6 @@ use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use wayland_server::EventLoopHandle;
#[derive(Debug)] #[derive(Debug)]
pub struct Card(File); pub struct Card(File);
@ -56,7 +55,7 @@ fn main() {
); );
// Initialize the wayland server // Initialize the wayland server
let (mut display, mut event_loop) = wayland_server::create_display(); let (mut display, mut event_loop) = wayland_server::Display::new();
/* /*
* Initialize the drm backend * Initialize the drm backend
@ -124,10 +123,10 @@ fn main() {
* Initialize the globals * Initialize the globals
*/ */
init_shm_global(&mut event_loop, vec![], log.clone()); init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) = let (compositor_token, _, _, window_map) =
init_shell(&mut event_loop, log.clone(), egl_display.clone()); init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
/* /*
* Add a listening socket: * Add a listening socket:
@ -139,7 +138,7 @@ fn main() {
* Register the DrmDevice on the EventLoop * Register the DrmDevice on the EventLoop
*/ */
let _source = drm_device_bind( let _source = drm_device_bind(
&mut event_loop, &event_loop.token(),
device, device,
DrmHandlerImpl { DrmHandlerImpl {
compositor_token, compositor_token,
@ -159,7 +158,7 @@ fn main() {
} }
pub struct DrmHandlerImpl { pub struct DrmHandlerImpl {
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>, compositor_token: CompositorToken<SurfaceData, Roles>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
drawer: GliumDrawer<DrmBackend<Card>>, drawer: GliumDrawer<DrmBackend<Card>>,
logger: ::slog::Logger, logger: ::slog::Logger,
@ -167,8 +166,11 @@ pub struct DrmHandlerImpl {
impl DrmHandler<Card> for DrmHandlerImpl { impl DrmHandler<Card> for DrmHandlerImpl {
fn ready( fn ready(
&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<Card>, _crtc: crtc::Handle, &mut self,
_frame: u32, _duration: Duration, _device: &mut DrmDevice<Card>,
_crtc: crtc::Handle,
_frame: u32,
_duration: Duration,
) { ) {
let mut frame = self.drawer.draw(); let mut frame = self.drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0); frame.clear_color(0.8, 0.8, 0.9, 1.0);
@ -215,8 +217,8 @@ impl DrmHandler<Card> for DrmHandlerImpl {
if let Some(ref texture) = attributes.user_data.texture { if let Some(ref texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) { if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x; x += subdata.location.0;
y += subdata.y; y += subdata.location.1;
} }
info!(self.logger, "Render window"); info!(self.logger, "Render window");
self.drawer.render_texture( self.drawer.render_texture(
@ -248,7 +250,7 @@ impl DrmHandler<Card> for DrmHandlerImpl {
frame.finish().unwrap(); frame.finish().unwrap();
} }
fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<Card>, error: DrmError) { fn error(&mut self, _device: &mut DrmDevice<Card>, error: DrmError) {
panic!("{:?}", error); panic!("{:?}", error);
} }
} }

View File

@ -135,8 +135,13 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
} }
pub fn render_texture( pub fn render_texture(
&self, target: &mut glium::Frame, texture: &Texture2d, y_inverted: bool, &self,
surface_dimensions: (u32, u32), surface_location: (i32, i32), screen_size: (u32, u32), target: &mut glium::Frame,
texture: &Texture2d,
y_inverted: bool,
surface_dimensions: (u32, u32),
surface_location: (i32, i32),
screen_size: (u32, u32),
blending: glium::Blend, blending: glium::Blend,
) { ) {
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32); let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);

View File

@ -1,18 +1,21 @@
use super::WindowMap; use super::{SurfaceKind, WindowMap};
use glium::texture::Texture2d; use glium::texture::Texture2d;
use rand; use rand;
use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format}; use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format};
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages}; use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent};
SurfaceUserImplementation}; use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState as XdgShellState,
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, ToplevelConfigure, XdgRequest, XdgSurfaceRole};
ShellSurfaceUserImplementation, ToplevelConfigure}; use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState,
ShellSurfaceKind, ShellSurfaceRole};
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents; use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use wayland_server::{EventLoop, StateToken}; use std::sync::{Arc, Mutex};
use wayland_server::{Display, LoopToken, Resource};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_shell_surface, wl_surface};
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] [ ShellSurface, ShellSurfaceRole<()>] );
#[derive(Default)] #[derive(Default)]
pub struct SurfaceData { pub struct SurfaceData {
@ -25,110 +28,62 @@ pub enum Buffer {
Shm { data: Vec<u8>, size: (u32, u32) }, Shm { data: Vec<u8>, size: (u32, u32) },
} }
pub fn surface_implementation( fn surface_commit(
) -> SurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>> { surface: &Resource<wl_surface::WlSurface>,
SurfaceUserImplementation { token: CompositorToken<SurfaceData, Roles>,
commit: |_, display, surface, token| { display: &RefCell<Option<EGLDisplay>>,
// we retrieve the contents of the associated buffer and copy it ) {
token.with_surface_data(surface, |attributes| { // we retrieve the contents of the associated buffer and copy it
match attributes.buffer.take() { token.with_surface_data(surface, |attributes| {
Some(Some((buffer, (_x, _y)))) => { match attributes.buffer.take() {
// we ignore hotspot coordinates in this simple example Some(Some((buffer, (_x, _y)))) => {
match if let Some(display) = display.borrow().as_ref() { // we ignore hotspot coordinates in this simple example
display.egl_buffer_contents(buffer) match if let Some(display) = display.borrow().as_ref() {
} else { display.egl_buffer_contents(buffer)
Err(BufferAccessError::NotManaged(buffer)) } else {
} { Err(BufferAccessError::NotManaged(buffer))
Ok(images) => { } {
match images.format { Ok(images) => {
Format::RGB => {} match images.format {
Format::RGBA => {} Format::RGB => {}
_ => { Format::RGBA => {}
// we don't handle the more complex formats here. _ => {
attributes.user_data.buffer = None; // we don't handle the more complex formats here.
attributes.user_data.texture = None; attributes.user_data.buffer = None;
return;
}
};
attributes.user_data.texture = None; attributes.user_data.texture = None;
attributes.user_data.buffer = Some(Buffer::Egl { images }); return;
} }
Err(BufferAccessError::NotManaged(buffer)) => { };
shm_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.texture = None;
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
buffer.release();
}
Err(err) => panic!("EGL error: {}", err),
}
}
Some(None) => {
// erase the contents
attributes.user_data.buffer = None;
attributes.user_data.texture = None; attributes.user_data.texture = None;
attributes.user_data.buffer = Some(Buffer::Egl { images });
} }
None => {} Err(BufferAccessError::NotManaged(buffer)) => {
shm_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.texture = None;
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
buffer.send(wl_buffer::Event::Release);
}
Err(err) => panic!("EGL error: {}", err),
} }
});
},
frame: |_, _, _, callback, _| {
callback.done(0);
},
}
}
pub struct ShellIData<F> {
pub token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>,
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, (), F>>>,
}
pub fn shell_implementation<F>(
) -> ShellSurfaceUserImplementation<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ShellIData<F>, ()>
where
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
{
ShellSurfaceUserImplementation {
new_client: |_, _, _| {},
client_pong: |_, _, _| {},
new_toplevel: |_, idata, toplevel| {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
idata.window_map.borrow_mut().insert(toplevel, (x, y));
ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
} }
}, Some(None) => {
new_popup: |_, _, _| PopupConfigure { // erase the contents
size: (10, 10), attributes.user_data.buffer = None;
position: (10, 10), attributes.user_data.texture = None;
serial: 42, }
}, None => {}
move_: |_, _, _, _, _| {}, }
resize: |_, _, _, _, _, _| {}, });
grab: |_, _, _, _, _| {},
change_display_state: |_, _, _, _, _, _, _| ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
},
show_window_menu: |_, _, _, _, _, _, _| {},
}
} }
fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> { fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
@ -143,38 +98,104 @@ fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
.map(|(x, y)| (x as i32, y as i32)) .map(|(x, y)| (x as i32, y as i32))
} }
pub type MyWindowMap = WindowMap< pub type MyWindowMap =
SurfaceData, WindowMap<SurfaceData, Roles, (), (), fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>>;
Roles,
Rc<RefCell<Option<EGLDisplay>>>,
(),
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
>;
pub fn init_shell( pub fn init_shell(
evl: &mut EventLoop, log: ::slog::Logger, data: Rc<RefCell<Option<EGLDisplay>>> display: &mut Display,
looptoken: LoopToken,
log: ::slog::Logger,
egl_display: Rc<RefCell<Option<EGLDisplay>>>,
) -> ( ) -> (
CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>, CompositorToken<SurfaceData, Roles>,
StateToken<ShellState<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>, ()>>, Arc<Mutex<XdgShellState<SurfaceData, Roles, ()>>>,
Arc<Mutex<WlShellState<SurfaceData, Roles, ()>>>,
Rc<RefCell<MyWindowMap>>, Rc<RefCell<MyWindowMap>>,
) { ) {
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone()); // Create the compositor
let c_egl_display = egl_display.clone();
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new( let (compositor_token, _, _) = compositor_init(
compositor_token, display,
get_size as _, looptoken.clone(),
))); move |request, (surface, ctoken)| match request {
SurfaceEvent::Commit => surface_commit(&surface, ctoken, &*c_egl_display),
let (shell_state_token, _, _) = shell_init( SurfaceEvent::Frame { callback } => callback
evl, .implement(|e, _| match e {}, None::<fn(_, _)>)
compositor_token, .send(wl_callback::Event::Done { callback_data: 0 }),
shell_implementation(),
ShellIData {
token: compositor_token,
window_map: window_map.clone(),
}, },
log.clone(), log.clone(),
); );
(compositor_token, shell_state_token, window_map) // Init a window map, to track the location of our windows
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, (), (), _>::new(
compositor_token,
get_size as _,
)));
// init the xdg_shell
let xdg_window_map = window_map.clone();
let (xdg_shell_state, _, _) = xdg_shell_init(
display,
looptoken.clone(),
compositor_token.clone(),
move |shell_event, ()| match shell_event {
XdgRequest::NewToplevel { surface } => {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
surface.send_configure(ToplevelConfigure {
size: None,
states: vec![],
serial: 42,
});
xdg_window_map
.borrow_mut()
.insert(SurfaceKind::Xdg(surface), (x, y));
}
XdgRequest::NewPopup { surface } => surface.send_configure(PopupConfigure {
size: (10, 10),
position: (10, 10),
serial: 42,
}),
_ => (),
},
log.clone(),
);
// init the wl_shell
let shell_window_map = window_map.clone();
let (wl_shell_state, _) = wl_shell_init(
display,
looptoken,
compositor_token.clone(),
move |req: ShellRequest<_, _, ()>, ()| match req {
ShellRequest::SetKind {
surface,
kind: ShellSurfaceKind::Toplevel,
} => {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
surface.send_configure((0, 0), wl_shell_surface::Resize::None);
shell_window_map
.borrow_mut()
.insert(SurfaceKind::Wl(surface), (x, y));
}
_ => (),
},
log.clone(),
);
(
compositor_token,
xdg_shell_state,
wl_shell_state,
window_map,
)
} }

View File

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

View File

@ -1,27 +1,57 @@
use smithay::utils::Rectangle; use smithay::utils::Rectangle;
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction};
use smithay::wayland::compositor::roles::Role; use smithay::wayland::compositor::roles::Role;
use smithay::wayland::shell::{ShellSurfaceRole, ToplevelSurface}; use smithay::wayland::shell::xdg::{ToplevelSurface, XdgSurfaceRole};
use smithay::wayland::shell::legacy::{ShellSurface, ShellSurfaceRole};
use wayland_server::Resource; use wayland_server::Resource;
use wayland_server::protocol::wl_surface; use wayland_server::protocol::wl_surface;
struct Window<U, R, CID, SD> { pub enum Kind<U, R, SD, D> {
location: (i32, i32), Xdg(ToplevelSurface<U, R, SD>),
surface: Rectangle, Wl(ShellSurface<U, R, D>),
toplevel: ToplevelSurface<U, R, CID, SD>,
} }
impl<U, R, CID, SD> Window<U, R, CID, SD> impl<U, R, SD, D> Kind<U, R, SD, D>
where where
U: 'static, U: 'static,
R: Role<SubsurfaceRole> + Role<ShellSurfaceRole> + 'static, R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
CID: 'static,
SD: 'static, SD: 'static,
D: 'static,
{
pub fn alive(&self) -> bool {
match *self {
Kind::Xdg(ref t) => t.alive(),
Kind::Wl(ref t) => t.alive(),
}
}
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
match *self {
Kind::Xdg(ref t) => t.get_surface(),
Kind::Wl(ref t) => t.get_surface(),
}
}
}
struct Window<U, R, SD, D> {
location: (i32, i32),
surface: Rectangle,
toplevel: Kind<U, R, SD, D>,
}
impl<U, R, SD, D> Window<U, R, SD, D>
where
U: 'static,
R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
SD: 'static,
D: 'static,
{ {
// Find the topmost surface under this point if any and the location of this point in the surface // Find the topmost surface under this point if any and the location of this point in the surface
fn matching<F>( fn matching<F>(
&self, point: (f64, f64), ctoken: CompositorToken<U, R, CID>, get_size: F &self,
) -> Option<(wl_surface::WlSurface, (f64, f64))> point: (f64, f64),
ctoken: CompositorToken<U, R>,
get_size: F,
) -> Option<(Resource<wl_surface::WlSurface>, (f64, f64))>
where where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>, F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
{ {
@ -37,8 +67,8 @@ where
|wl_surface, attributes, role, &(mut x, mut y)| { |wl_surface, attributes, role, &(mut x, mut y)| {
if let Some((w, h)) = get_size(attributes) { if let Some((w, h)) = get_size(attributes) {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) { if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x; x += subdata.location.0;
y += subdata.y; y += subdata.location.1;
} }
let my_rect = Rectangle { let my_rect = Rectangle {
x, x,
@ -47,9 +77,10 @@ where
height: h, height: h,
}; };
if my_rect.contains((point.0 as i32, point.1 as i32)) { if my_rect.contains((point.0 as i32, point.1 as i32)) {
found = wl_surface found = Some((
.clone() wl_surface.clone(),
.map(|s| (s, (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64))); (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64),
));
TraversalAction::Break TraversalAction::Break
} else { } else {
TraversalAction::DoChildren((x, y)) TraversalAction::DoChildren((x, y))
@ -63,7 +94,7 @@ where
found found
} }
fn self_update<F>(&mut self, ctoken: CompositorToken<U, R, CID>, get_size: F) fn self_update<F>(&mut self, ctoken: CompositorToken<U, R>, get_size: F)
where where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>, F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
{ {
@ -76,8 +107,8 @@ where
|_, attributes, role, &(mut x, mut y)| { |_, attributes, role, &(mut x, mut y)| {
if let Some((w, h)) = get_size(attributes) { if let Some((w, h)) = get_size(attributes) {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) { if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x; x += subdata.location.0;
y += subdata.y; y += subdata.location.1;
} }
// update the bounding box // update the bounding box
if x < min_x { if x < min_x {
@ -108,21 +139,21 @@ where
} }
} }
pub struct WindowMap<U, R, CID, SD, F> { pub struct WindowMap<U, R, SD, D, F> {
ctoken: CompositorToken<U, R, CID>, ctoken: CompositorToken<U, R>,
windows: Vec<Window<U, R, CID, SD>>, windows: Vec<Window<U, R, SD, D>>,
get_size: F, get_size: F,
} }
impl<U, R, CID, SD, F> WindowMap<U, R, CID, SD, F> impl<U, R, SD, D, F> WindowMap<U, R, SD, D, F>
where where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>, F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
U: 'static, U: 'static,
R: Role<SubsurfaceRole> + Role<ShellSurfaceRole> + 'static, R: Role<SubsurfaceRole> + Role<XdgSurfaceRole> + Role<ShellSurfaceRole<D>> + 'static,
CID: 'static,
SD: 'static, SD: 'static,
D: 'static,
{ {
pub fn new(ctoken: CompositorToken<U, R, CID>, get_size: F) -> WindowMap<U, R, CID, SD, F> { pub fn new(ctoken: CompositorToken<U, R>, get_size: F) -> WindowMap<U, R, D, SD, F> {
WindowMap { WindowMap {
ctoken: ctoken, ctoken: ctoken,
windows: Vec::new(), windows: Vec::new(),
@ -130,7 +161,7 @@ where
} }
} }
pub fn insert(&mut self, toplevel: ToplevelSurface<U, R, CID, SD>, location: (i32, i32)) { pub fn insert(&mut self, toplevel: Kind<U, R, SD, D>, location: (i32, i32)) {
let mut window = Window { let mut window = Window {
location: location, location: location,
surface: Rectangle { surface: Rectangle {
@ -145,7 +176,10 @@ where
self.windows.insert(0, window); self.windows.insert(0, window);
} }
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> { pub fn get_surface_under(
&self,
point: (f64, f64),
) -> Option<(Resource<wl_surface::WlSurface>, (f64, f64))> {
for w in &self.windows { for w in &self.windows {
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) { if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
return Some(surface); return Some(surface);
@ -155,8 +189,9 @@ where
} }
pub fn get_surface_and_bring_to_top( pub fn get_surface_and_bring_to_top(
&mut self, point: (f64, f64) &mut self,
) -> Option<(wl_surface::WlSurface, (f64, f64))> { point: (f64, f64),
) -> Option<(Resource<wl_surface::WlSurface>, (f64, f64))> {
let mut found = None; let mut found = None;
for (i, w) in self.windows.iter().enumerate() { for (i, w) in self.windows.iter().enumerate() {
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) { if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
@ -175,7 +210,7 @@ where
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func) pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
where where
Func: FnMut(&ToplevelSurface<U, R, CID, SD>, (i32, i32)), Func: FnMut(&Kind<U, R, SD, D>, (i32, i32)),
{ {
for w in self.windows.iter().rev() { for w in self.windows.iter().rev() {
f(&w.toplevel, w.location) f(&w.toplevel, w.location)

View File

@ -53,9 +53,9 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration; use std::time::Duration;
use wayland_server::{Display, EventLoopHandle}; use wayland_server::Display;
use wayland_server::commons::downcast_impl;
use wayland_server::protocol::{wl_output, wl_pointer}; use wayland_server::protocol::{wl_output, wl_pointer};
use wayland_server::sources::EventSource;
use xkbcommon::xkb::keysyms as xkb; use xkbcommon::xkb::keysyms as xkb;
struct LibinputInputHandler { struct LibinputInputHandler {
@ -78,18 +78,16 @@ impl LibinputInputHandler {
} }
impl InputHandler<LibinputInputBackend> for LibinputInputHandler { impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
fn on_seat_created(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { fn on_seat_created(&mut self, _: &input::Seat) {
/* we just create a single static one */ /* we just create a single static one */
} }
fn on_seat_destroyed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { fn on_seat_destroyed(&mut self, _: &input::Seat) {
/* we just create a single static one */ /* we just create a single static one */
} }
fn on_seat_changed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { fn on_seat_changed(&mut self, _: &input::Seat) {
/* we just create a single static one */ /* we just create a single static one */
} }
fn on_keyboard_key( fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent
) {
let keycode = evt.key(); let keycode = evt.key();
let state = evt.state(); let state = evt.state();
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
@ -100,8 +98,9 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
let running = &self.running; let running = &self.running;
let mut session = &mut self.session; let mut session = &mut self.session;
let log = &self.log; let log = &self.log;
let time = Event::time(&evt);
self.keyboard self.keyboard
.input(keycode, state, serial, move |modifiers, keysym| { .input(keycode, state, serial, time, move |modifiers, keysym| {
debug!(log, "keysym"; "state" => format!("{:?}", state), "mods" => format!("{:?}", modifiers), "keysym" => xkbcommon::xkb::keysym_get_name(keysym)); debug!(log, "keysym"; "state" => format!("{:?}", state), "mods" => format!("{:?}", modifiers), "keysym" => xkbcommon::xkb::keysym_get_name(keysym));
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
&& state == KeyState::Pressed && state == KeyState::Pressed
@ -132,9 +131,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
} }
}); });
} }
fn on_pointer_move( fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerMotionEvent
) {
let (x, y) = (evt.dx(), evt.dy()); let (x, y) = (evt.dx(), evt.dy());
let serial = self.next_serial(); let serial = self.next_serial();
let mut location = self.pointer_location.borrow_mut(); let mut location = self.pointer_location.borrow_mut();
@ -149,10 +146,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
evt.time(), evt.time(),
); );
} }
fn on_pointer_move_absolute( fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat,
evt: event::pointer::PointerMotionAbsoluteEvent,
) {
let (x, y) = ( let (x, y) = (
evt.absolute_x_transformed(self.screen_size.0), evt.absolute_x_transformed(self.screen_size.0),
evt.absolute_y_transformed(self.screen_size.1), evt.absolute_y_transformed(self.screen_size.1),
@ -166,9 +160,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
evt.time(), evt.time(),
); );
} }
fn on_pointer_button( fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerButtonEvent
) {
let serial = self.next_serial(); let serial = self.next_serial();
let button = evt.button(); let button = evt.button();
let state = match evt.state() { let state = match evt.state() {
@ -185,9 +177,7 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
}; };
self.pointer.button(button, state, serial, evt.time()); self.pointer.button(button, state, serial, evt.time());
} }
fn on_pointer_axis( fn on_pointer_axis(&mut self, _: &input::Seat, evt: event::pointer::PointerAxisEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerAxisEvent
) {
let source = match evt.source() { let source = match evt.source() {
input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous, input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous,
input::AxisSource::Finger => wayland_server::protocol::wl_pointer::AxisSource::Finger, input::AxisSource::Finger => wayland_server::protocol::wl_pointer::AxisSource::Finger,
@ -244,30 +234,22 @@ impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
event.done(); event.done();
} }
} }
fn on_touch_down( fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchDownEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_motion( fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchMotionEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_up(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchUpEvent) { fn on_touch_up(&mut self, _: &input::Seat, _: event::touch::TouchUpEvent) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_cancel( fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchCancelEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_frame( fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchFrameEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_input_config_changed(&mut self, _evlh: &mut EventLoopHandle, _: &mut [LibinputDevice]) { fn on_input_config_changed(&mut self, _: &mut [LibinputDevice]) {
/* not done in this example */ /* not done in this example */
} }
} }
@ -284,7 +266,7 @@ fn main() {
); );
// Initialize the wayland server // Initialize the wayland server
let (mut display, mut event_loop) = wayland_server::create_display(); let (mut display, mut event_loop) = wayland_server::Display::new();
/* /*
* Add a listening socket * Add a listening socket
@ -292,15 +274,24 @@ fn main() {
let name = display.add_socket_auto().unwrap().into_string().unwrap(); let name = display.add_socket_auto().unwrap().into_string().unwrap();
println!("Listening on socket: {}", name); println!("Listening on socket: {}", name);
env::set_var("WAYLAND_DISPLAY", name); env::set_var("WAYLAND_DISPLAY", name);
let display = Rc::new(display); let display = Rc::new(RefCell::new(display));
/* /*
* Initialize the compositor * Initialize the compositor
*/ */
init_shm_global(&mut event_loop, vec![], log.clone()); init_shm_global(
&mut display.borrow_mut(),
event_loop.token(),
vec![],
log.clone(),
);
let (compositor_token, _shell_state_token, window_map) = let (compositor_token, _, _, window_map) = init_shell(
init_shell(&mut event_loop, log.clone(), active_egl_context.clone()); &mut display.borrow_mut(),
event_loop.token(),
log.clone(),
active_egl_context.clone(),
);
/* /*
* Initialize session * Initialize session
@ -321,7 +312,7 @@ fn main() {
let bytes = include_bytes!("resources/cursor2.rgba"); let bytes = include_bytes!("resources/cursor2.rgba");
let mut udev_backend = UdevBackend::new( let mut udev_backend = UdevBackend::new(
&mut event_loop, event_loop.token(),
&context, &context,
session.clone(), session.clone(),
UdevHandlerImpl { UdevHandlerImpl {
@ -340,17 +331,21 @@ fn main() {
let udev_session_id = notifier.register(&mut udev_backend); let udev_session_id = notifier.register(&mut udev_backend);
let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone()); let (mut w_seat, _) = Seat::new(
&mut display.borrow_mut(),
event_loop.token(),
session.seat().into(),
log.clone(),
);
let pointer = event_loop.state().get_mut(&seat_token).add_pointer(); let pointer = w_seat.add_pointer();
let keyboard = event_loop let keyboard = w_seat
.state()
.get_mut(&seat_token)
.add_keyboard("", "", "", None, 1000, 500) .add_keyboard("", "", "", None, 1000, 500)
.expect("Failed to initialize the keyboard"); .expect("Failed to initialize the keyboard");
let (output_token, _output_global) = Output::new( let (output, _output_global) = Output::new(
&mut event_loop, &mut display.borrow_mut(),
event_loop.token(),
"Drm".into(), "Drm".into(),
PhysicalProperties { PhysicalProperties {
width: 0, width: 0,
@ -363,26 +358,20 @@ fn main() {
); );
let (w, h) = (1920, 1080); // Hardcode full-hd res let (w, h) = (1920, 1080); // Hardcode full-hd res
event_loop output.change_current_state(
.state() Some(Mode {
.get_mut(&output_token)
.change_current_state(
Some(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
}),
None,
None,
);
event_loop
.state()
.get_mut(&output_token)
.set_preferred(Mode {
width: w as i32, width: w as i32,
height: h as i32, height: h as i32,
refresh: 60_000, refresh: 60_000,
}); }),
None,
None,
);
output.set_preferred(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
});
/* /*
* Initialize libinput backend * Initialize libinput backend
@ -392,28 +381,25 @@ fn main() {
let libinput_session_id = notifier.register(&mut libinput_context); let libinput_session_id = notifier.register(&mut libinput_context);
libinput_context.udev_assign_seat(&seat).unwrap(); libinput_context.udev_assign_seat(&seat).unwrap();
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
libinput_backend.set_handler( libinput_backend.set_handler(LibinputInputHandler {
&mut event_loop, log: log.clone(),
LibinputInputHandler { pointer,
log: log.clone(), keyboard,
pointer, window_map: window_map.clone(),
keyboard, pointer_location,
window_map: window_map.clone(), screen_size: (w, h),
pointer_location, serial: 0,
screen_size: (w, h), session: session,
serial: 0, running: running.clone(),
session: session, });
running: running.clone(), let libinput_event_source = libinput_bind(libinput_backend, event_loop.token())
},
);
let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop)
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.unwrap(); .unwrap();
let session_event_source = auto_session_bind(notifier, &mut event_loop) let session_event_source = auto_session_bind(notifier, &event_loop.token())
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.unwrap(); .unwrap();
let udev_event_source = udev_backend_bind(&mut event_loop, udev_backend) let udev_event_source = udev_backend_bind(&event_loop.token(), udev_backend)
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.unwrap(); .unwrap();
@ -421,7 +407,7 @@ fn main() {
if let Err(_) = event_loop.dispatch(Some(16)) { if let Err(_) = event_loop.dispatch(Some(16)) {
running.store(false, Ordering::SeqCst); running.store(false, Ordering::SeqCst);
} else { } else {
display.flush_clients(); display.borrow_mut().flush_clients();
window_map.borrow_mut().refresh(); window_map.borrow_mut().refresh();
} }
} }
@ -435,14 +421,19 @@ fn main() {
libinput_event_source.remove(); libinput_event_source.remove();
// destroy the udev backend freeing the drm devices // destroy the udev backend freeing the drm devices
udev_event_source.remove().close(&mut event_loop) //
// udev_event_source.remove() returns a Box<Implementation<..>>, downcast_impl
// allows us to cast it back to its original type, storing it back into its original
// variable to simplify type inference.
udev_backend = *(downcast_impl(udev_event_source.remove()).unwrap_or_else(|_| unreachable!()));
udev_backend.close();
} }
struct UdevHandlerImpl { struct UdevHandlerImpl {
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>, compositor_token: CompositorToken<SurfaceData, Roles>,
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>, active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>, backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
display: Rc<Display>, display: Rc<RefCell<Display>>,
primary_gpu: Option<PathBuf>, primary_gpu: Option<PathBuf>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>, pointer_location: Rc<RefCell<(f64, f64)>>,
@ -452,7 +443,8 @@ struct UdevHandlerImpl {
impl UdevHandlerImpl { impl UdevHandlerImpl {
pub fn scan_connectors( pub fn scan_connectors(
&self, device: &mut DrmDevice<SessionFdDrmDevice> &self,
device: &mut DrmDevice<SessionFdDrmDevice>,
) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> { ) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
// Get a set of all modesetting resource handles (excluding planes): // Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap(); let res_handles = device.resource_handles().unwrap();
@ -511,12 +503,10 @@ impl UdevHandlerImpl {
} }
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl { impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
fn device_added( fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<DrmHandlerImpl> {
&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>
) -> Option<DrmHandlerImpl> {
// init hardware acceleration on the primary gpu. // init hardware acceleration on the primary gpu.
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu { if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display).ok(); *self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok();
} }
let backends = Rc::new(RefCell::new(self.scan_connectors(device))); let backends = Rc::new(RefCell::new(self.scan_connectors(device)));
@ -531,13 +521,13 @@ impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
}) })
} }
fn device_changed(&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>) { fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
//quick and dirt, just re-init all backends //quick and dirt, just re-init all backends
let backends = self.backends.get(&device.device_id()).unwrap(); let backends = self.backends.get(&device.device_id()).unwrap();
*backends.borrow_mut() = self.scan_connectors(device); *backends.borrow_mut() = self.scan_connectors(device);
} }
fn device_removed(&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>) { fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
// drop the backends on this side // drop the backends on this side
self.backends.remove(&device.device_id()); self.backends.remove(&device.device_id());
@ -547,13 +537,13 @@ impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
} }
} }
fn error(&mut self, _evlh: &mut EventLoopHandle, error: IoError) { fn error(&mut self, error: IoError) {
error!(self.logger, "{:?}", error); error!(self.logger, "{:?}", error);
} }
} }
pub struct DrmHandlerImpl { pub struct DrmHandlerImpl {
compositor_token: CompositorToken<SurfaceData, Roles, Rc<RefCell<Option<EGLDisplay>>>>, compositor_token: CompositorToken<SurfaceData, Roles>,
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>, pointer_location: Rc<RefCell<(f64, f64)>>,
@ -562,8 +552,11 @@ pub struct DrmHandlerImpl {
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl { impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
fn ready( fn ready(
&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<SessionFdDrmDevice>, &mut self,
crtc: crtc::Handle, _frame: u32, _duration: Duration, _device: &mut DrmDevice<SessionFdDrmDevice>,
crtc: crtc::Handle,
_frame: u32,
_duration: Duration,
) { ) {
if let Some(drawer) = self.backends.borrow().get(&crtc) { if let Some(drawer) = self.backends.borrow().get(&crtc) {
{ {
@ -616,8 +609,8 @@ impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
if let Some(ref texture) = attributes.user_data.texture { if let Some(ref texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) { if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x; x += subdata.location.0;
y += subdata.y; y += subdata.location.1;
} }
info!(self.logger, "Render window"); info!(self.logger, "Render window");
drawer.render_texture( drawer.render_texture(
@ -655,9 +648,7 @@ impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
} }
} }
fn error( fn error(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError) {
&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError
) {
error!(self.logger, "{:?}", error); error!(self.logger, "{:?}", error);
} }
} }

View File

@ -26,7 +26,7 @@ use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
use smithay::wayland::shm::init_shm_global; use smithay::wayland::shm::init_shm_global;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use wayland_server::EventLoopHandle; use wayland_server::Display;
use wayland_server::protocol::{wl_output, wl_pointer}; use wayland_server::protocol::{wl_output, wl_pointer};
struct WinitInputHandler { struct WinitInputHandler {
@ -46,30 +46,27 @@ impl WinitInputHandler {
} }
impl InputHandler<winit::WinitInputBackend> for WinitInputHandler { impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
fn on_seat_created(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { fn on_seat_created(&mut self, _: &input::Seat) {
/* never happens with winit */ /* never happens with winit */
} }
fn on_seat_destroyed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { fn on_seat_destroyed(&mut self, _: &input::Seat) {
/* never happens with winit */ /* never happens with winit */
} }
fn on_seat_changed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { fn on_seat_changed(&mut self, _: &input::Seat) {
/* never happens with winit */ /* never happens with winit */
} }
fn on_keyboard_key( fn on_keyboard_key(&mut self, _: &input::Seat, evt: winit::WinitKeyboardInputEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitKeyboardInputEvent
) {
let keycode = evt.key_code(); let keycode = evt.key_code();
let state = evt.state(); let state = evt.state();
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
let serial = self.next_serial(); let serial = self.next_serial();
self.keyboard.input(keycode, state, serial, |_, _| true); self.keyboard
.input(keycode, state, serial, evt.time(), |_, _| true);
} }
fn on_pointer_move(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: input::UnusedEvent) { fn on_pointer_move(&mut self, _: &input::Seat, _: input::UnusedEvent) {
/* never happens with winit */ /* never happens with winit */
} }
fn on_pointer_move_absolute( fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: winit::WinitMouseMovedEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseMovedEvent
) {
// on winit, mouse events are already in pixel coordinates // on winit, mouse events are already in pixel coordinates
let (x, y) = evt.position(); let (x, y) = evt.position();
self.pointer_location = (x, y); self.pointer_location = (x, y);
@ -81,9 +78,7 @@ impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
evt.time(), evt.time(),
); );
} }
fn on_pointer_button( fn on_pointer_button(&mut self, _: &input::Seat, evt: winit::WinitMouseInputEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseInputEvent
) {
let serial = self.next_serial(); let serial = self.next_serial();
let button = match evt.button() { let button = match evt.button() {
input::MouseButton::Left => 0x110, input::MouseButton::Left => 0x110,
@ -105,9 +100,7 @@ impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
}; };
self.pointer.button(button, state, serial, evt.time()); self.pointer.button(button, state, serial, evt.time());
} }
fn on_pointer_axis( fn on_pointer_axis(&mut self, _: &input::Seat, evt: winit::WinitMouseWheelEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseWheelEvent
) {
let source = match evt.source() { let source = match evt.source() {
input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous, input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous,
input::AxisSource::Wheel => wayland_server::protocol::wl_pointer::AxisSource::Wheel, input::AxisSource::Wheel => wayland_server::protocol::wl_pointer::AxisSource::Wheel,
@ -152,28 +145,22 @@ impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
event.done(); event.done();
} }
} }
fn on_touch_down( fn on_touch_down(&mut self, _: &input::Seat, _: winit::WinitTouchStartedEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchStartedEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_motion( fn on_touch_motion(&mut self, _: &input::Seat, _: winit::WinitTouchMovedEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchMovedEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_up(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchEndedEvent) { fn on_touch_up(&mut self, _: &input::Seat, _: winit::WinitTouchEndedEvent) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_cancel( fn on_touch_cancel(&mut self, _: &input::Seat, _: winit::WinitTouchCancelledEvent) {
&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchCancelledEvent
) {
/* not done in this example */ /* not done in this example */
} }
fn on_touch_frame(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: input::UnusedEvent) { fn on_touch_frame(&mut self, _: &input::Seat, _: input::UnusedEvent) {
/* never happens with winit */ /* never happens with winit */
} }
fn on_input_config_changed(&mut self, _evlh: &mut EventLoopHandle, _: &mut ()) { fn on_input_config_changed(&mut self, _: &mut ()) {
/* never happens with winit */ /* never happens with winit */
} }
} }
@ -188,7 +175,7 @@ fn main() {
// Initialize a simple backend for testing // Initialize a simple backend for testing
let (renderer, mut input) = winit::init(log.clone()).unwrap(); let (renderer, mut input) = winit::init(log.clone()).unwrap();
let (mut display, mut event_loop) = wayland_server::create_display(); let (mut display, mut event_loop) = wayland_server::Display::new();
let egl_display = Rc::new(RefCell::new( let egl_display = Rc::new(RefCell::new(
if let Ok(egl_display) = renderer.bind_wl_display(&display) { if let Ok(egl_display) = renderer.bind_wl_display(&display) {
@ -206,22 +193,25 @@ fn main() {
* Initialize the globals * Initialize the globals
*/ */
init_shm_global(&mut event_loop, vec![], log.clone()); init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) = let (compositor_token, _, _, window_map) =
init_shell(&mut event_loop, log.clone(), egl_display); init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone()); let (mut seat, _) = Seat::new(
&mut display,
event_loop.token(),
"winit".into(),
log.clone(),
);
let pointer = event_loop.state().get_mut(&seat_token).add_pointer(); let pointer = seat.add_pointer();
let keyboard = event_loop let keyboard = seat.add_keyboard("", "fr", "oss", None, 1000, 500)
.state()
.get_mut(&seat_token)
.add_keyboard("", "fr", "oss", None, 1000, 500)
.expect("Failed to initialize the keyboard"); .expect("Failed to initialize the keyboard");
let (output_token, _output_global) = Output::new( let (output, _) = Output::new(
&mut event_loop, &mut display,
event_loop.token(),
"Winit".into(), "Winit".into(),
PhysicalProperties { PhysicalProperties {
width: 0, width: 0,
@ -233,38 +223,29 @@ fn main() {
log.clone(), log.clone(),
); );
event_loop output.change_current_state(
.state() Some(Mode {
.get_mut(&output_token)
.change_current_state(
Some(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
}),
None,
None,
);
event_loop
.state()
.get_mut(&output_token)
.set_preferred(Mode {
width: w as i32, width: w as i32,
height: h as i32, height: h as i32,
refresh: 60_000, refresh: 60_000,
}); }),
None,
input.set_handler( None,
&mut event_loop,
WinitInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location: (0.0, 0.0),
serial: 0,
},
); );
output.set_preferred(Mode {
width: w as i32,
height: h as i32,
refresh: 60_000,
});
input.set_handler(WinitInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location: (0.0, 0.0),
serial: 0,
});
/* /*
* Add a listening socket: * Add a listening socket:
@ -273,7 +254,7 @@ fn main() {
println!("Listening on socket: {}", name); println!("Listening on socket: {}", name);
loop { loop {
input.dispatch_new_events(&mut event_loop).unwrap(); input.dispatch_new_events().unwrap();
let mut frame = drawer.draw(); let mut frame = drawer.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
@ -320,8 +301,8 @@ fn main() {
if let Some(ref texture) = attributes.user_data.texture { if let Some(ref texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) { if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x; x += subdata.location.0;
y += subdata.y; y += subdata.location.1;
} }
drawer.render_texture( drawer.render_texture(
&mut frame, &mut frame,

View File

@ -37,8 +37,11 @@ pub(crate) struct DrmBackendInternal<A: Device + 'static> {
impl<A: Device + 'static> DrmBackend<A> { impl<A: Device + 'static> DrmBackend<A> {
pub(crate) fn new( pub(crate) fn new(
context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>, crtc: crtc::Handle, mode: Mode, context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
connectors: Vec<connector::Handle>, log: ::slog::Logger, crtc: crtc::Handle,
mode: Mode,
connectors: Vec<connector::Handle>,
log: ::slog::Logger,
) -> Result<Self> { ) -> Result<Self> {
// logger already initialized by the DrmDevice // logger already initialized by the DrmDevice
info!(log, "Initializing DrmBackend"); info!(log, "Initializing DrmBackend");
@ -351,7 +354,8 @@ impl<A: Device + 'static> DrmBackendInternal<A> {
} }
pub(crate) fn page_flip( pub(crate) fn page_flip(
&self, fb: Option<&framebuffer::Info> &self,
fb: Option<&framebuffer::Info>,
) -> ::std::result::Result<(), SwapBuffersError> { ) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Queueing Page flip"); trace!(self.logger, "Queueing Page flip");
@ -421,7 +425,9 @@ impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
} }
fn set_cursor_representation( fn set_cursor_representation(
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32) &self,
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
hotspot: (u32, u32),
) -> Result<()> { ) -> Result<()> {
let (w, h) = buffer.dimensions(); let (w, h) = buffer.dimensions();
debug!(self.backend.logger, "Importing cursor"); debug!(self.backend.logger, "Importing cursor");

View File

@ -66,8 +66,6 @@
//! impl ControlDevice for Card {} //! impl ControlDevice for Card {}
//! //!
//! # fn main() { //! # fn main() {
//! let (_display, mut event_loop) = wayland_server::create_display();
//!
//! // Open the drm device //! // Open the drm device
//! let mut options = OpenOptions::new(); //! let mut options = OpenOptions::new();
//! options.read(true); //! options.read(true);
@ -139,7 +137,6 @@
//! # use std::time::Duration; //! # use std::time::Duration;
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind}; //! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
//! use smithay::backend::graphics::egl::EGLGraphicsBackend; //! use smithay::backend::graphics::egl::EGLGraphicsBackend;
//! use wayland_server::EventLoopHandle;
//! # //! #
//! # #[derive(Debug)] //! # #[derive(Debug)]
//! # pub struct Card(File); //! # pub struct Card(File);
@ -153,7 +150,7 @@
//! # //! #
//! # fn main() { //! # fn main() {
//! # //! #
//! # let (_display, mut event_loop) = wayland_server::create_display(); //! # let (_display, mut event_loop) = wayland_server::Display::new();
//! # //! #
//! # let mut options = OpenOptions::new(); //! # let mut options = OpenOptions::new();
//! # options.read(true); //! # options.read(true);
@ -181,7 +178,6 @@
//! impl DrmHandler<Card> for MyDrmHandler { //! impl DrmHandler<Card> for MyDrmHandler {
//! fn ready( //! fn ready(
//! &mut self, //! &mut self,
//! _evlh: &mut EventLoopHandle,
//! _device: &mut DrmDevice<Card>, //! _device: &mut DrmDevice<Card>,
//! _crtc: CrtcHandle, //! _crtc: CrtcHandle,
//! _frame: u32, //! _frame: u32,
@ -192,7 +188,6 @@
//! } //! }
//! fn error( //! fn error(
//! &mut self, //! &mut self,
//! _evlh: &mut EventLoopHandle,
//! device: &mut DrmDevice<Card>, //! device: &mut DrmDevice<Card>,
//! error: DrmError) //! error: DrmError)
//! { //! {
@ -203,7 +198,11 @@
//! // render something (like clear_color) //! // render something (like clear_color)
//! backend.swap_buffers().unwrap(); //! backend.swap_buffers().unwrap();
//! //!
//! let _source = drm_device_bind(&mut event_loop, device, MyDrmHandler(backend)).map_err(|(err, _)| err).unwrap(); //! let (_source, _device_rc) = drm_device_bind(
//! &event_loop.token(),
//! device,
//! MyDrmHandler(backend)
//! ).map_err(|(err, _)| err).unwrap();
//! //!
//! event_loop.run().unwrap(); //! event_loop.run().unwrap();
//! # } //! # }
@ -233,8 +232,9 @@ use std::rc::{Rc, Weak};
use std::sync::{Arc, Once, ONCE_INIT}; use std::sync::{Arc, Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration; use std::time::Duration;
use wayland_server::{Display, EventLoopHandle}; use wayland_server::{Display, LoopToken};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
mod backend; mod backend;
pub mod error; pub mod error;
@ -378,7 +378,10 @@ impl<A: ControlDevice + 'static> DrmDevice<A> {
/// Errors if initialization fails or the mode is not available on all given /// Errors if initialization fails or the mode is not available on all given
/// connectors. /// connectors.
pub fn create_backend<I>( pub fn create_backend<I>(
&mut self, crtc: crtc::Handle, mode: Mode, connectors: I &mut self,
crtc: crtc::Handle,
mode: Mode,
connectors: I,
) -> Result<DrmBackend<A>> ) -> Result<DrmBackend<A>>
where where
I: Into<Vec<connector::Handle>>, I: Into<Vec<connector::Handle>>,
@ -532,44 +535,66 @@ impl<A: ControlDevice + 'static> Drop for DrmDevice<A> {
pub trait DrmHandler<A: ControlDevice + 'static> { pub trait DrmHandler<A: ControlDevice + 'static> {
/// The `DrmBackend` of crtc has finished swapping buffers and new frame can now /// The `DrmBackend` of crtc has finished swapping buffers and new frame can now
/// (and should be immediately) be rendered. /// (and should be immediately) be rendered.
fn ready( fn ready(&mut self, device: &mut DrmDevice<A>, crtc: crtc::Handle, frame: u32, duration: Duration);
&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<A>, crtc: crtc::Handle, frame: u32,
duration: Duration,
);
/// The `DrmDevice` has thrown an error. /// The `DrmDevice` has thrown an error.
/// ///
/// The related backends are most likely *not* usable anymore and /// The related backends are most likely *not* usable anymore and
/// the whole stack has to be recreated.. /// the whole stack has to be recreated..
fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<A>, error: DrmError); fn error(&mut self, device: &mut DrmDevice<A>, error: DrmError);
} }
/// Bind a `DrmDevice` to an `EventLoop`, /// Bind a `DrmDevice` to an `EventLoop`,
/// ///
/// This will cause it to recieve events and feed them into an `DrmHandler` /// This will cause it to recieve events and feed them into an `DrmHandler`
pub fn drm_device_bind<A, H>( pub fn drm_device_bind<A, H>(
evlh: &mut EventLoopHandle, device: DrmDevice<A>, handler: H token: &LoopToken,
) -> ::std::result::Result<FdEventSource<(DrmDevice<A>, H)>, (IoError, (DrmDevice<A>, H))> device: DrmDevice<A>,
handler: H,
) -> ::std::result::Result<(Source<FdEvent>, Rc<RefCell<DrmDevice<A>>>), (IoError, (DrmDevice<A>, H))>
where where
A: ControlDevice + 'static, A: ControlDevice + 'static,
H: DrmHandler<A> + 'static, H: DrmHandler<A> + 'static,
{ {
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
evlh.add_fd_event_source( let device = Rc::new(RefCell::new(device));
match token.add_fd_event_source(
fd, fd,
fd_event_source_implementation(),
(device, handler),
FdInterest::READ, FdInterest::READ,
) DrmFdImpl {
device: device.clone(),
handler,
},
) {
Ok(source) => Ok((source, device)),
Err((
ioerror,
DrmFdImpl {
device: device2,
handler,
},
)) => {
// make the Rc unique again
::std::mem::drop(device2);
let device = Rc::try_unwrap(device).unwrap_or_else(|_| unreachable!());
Err((ioerror, (device.into_inner(), handler)))
}
}
} }
fn fd_event_source_implementation<A, H>() -> FdEventSourceImpl<(DrmDevice<A>, H)> struct DrmFdImpl<A: ControlDevice + 'static, H> {
device: Rc<RefCell<DrmDevice<A>>>,
handler: H,
}
impl<A, H> Implementation<(), FdEvent> for DrmFdImpl<A, H>
where where
A: ControlDevice + 'static, A: ControlDevice + 'static,
H: DrmHandler<A> + 'static, H: DrmHandler<A> + 'static,
{ {
FdEventSourceImpl { fn receive(&mut self, event: FdEvent, (): ()) {
ready: |evlh, &mut (ref mut device, ref mut handler), _, _| { let mut device = self.device.borrow_mut();
match crtc::receive_events(device) { match event {
FdEvent::Ready { .. } => match crtc::receive_events(&mut *device) {
Ok(events) => for event in events { Ok(events) => for event in events {
if let crtc::Event::PageFlip(event) = event { if let crtc::Event::PageFlip(event) = event {
if device.active.load(Ordering::SeqCst) { if device.active.load(Ordering::SeqCst) {
@ -584,20 +609,21 @@ where
backend.unlock_buffer(); backend.unlock_buffer();
trace!(device.logger, "Handling event for backend {:?}", event.crtc); trace!(device.logger, "Handling event for backend {:?}", event.crtc);
// and then call the user to render the next frame // and then call the user to render the next frame
handler.ready(evlh, device, event.crtc, event.frame, event.duration); self.handler
.ready(&mut device, event.crtc, event.frame, event.duration);
} else { } else {
device.backends.borrow_mut().remove(&event.crtc); device.backends.borrow_mut().remove(&event.crtc);
} }
} }
} }
}, },
Err(err) => handler.error(evlh, device, err), Err(err) => self.handler.error(&mut device, err),
}; },
}, FdEvent::Error { error, .. } => {
error: |evlh, &mut (ref mut device, ref mut handler), _, error| { warn!(device.logger, "DrmDevice errored: {}", error);
warn!(device.logger, "DrmDevice errored: {}", error); self.handler.error(&mut device, error.into());
handler.error(evlh, device, error.into()); }
}, }
} }
} }
@ -629,7 +655,7 @@ impl<A: ControlDevice + 'static> AsSessionObserver<DrmDeviceObserver<A>> for Drm
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
impl<A: ControlDevice + 'static> SessionObserver for DrmDeviceObserver<A> { impl<A: ControlDevice + 'static> SessionObserver for DrmDeviceObserver<A> {
fn pause(&mut self, _evlh: &mut EventLoopHandle, devnum: Option<(u32, u32)>) { fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some((major, minor)) = devnum { if let Some((major, minor)) = devnum {
if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) { if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) {
return; return;
@ -665,7 +691,7 @@ impl<A: ControlDevice + 'static> SessionObserver for DrmDeviceObserver<A> {
} }
} }
fn activate(&mut self, _evlh: &mut EventLoopHandle, devnum: Option<(u32, u32, Option<RawFd>)>) { fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
if let Some((major, minor, fd)) = devnum { if let Some((major, minor, fd)) = devnum {
if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) { if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) {
return; return;

View File

@ -50,7 +50,10 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> DerefMut for EGLContext<B,
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> { impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
/// Create a new `EGLContext` from a given `NativeDisplay` /// Create a new `EGLContext` from a given `NativeDisplay`
pub fn new<L>( pub fn new<L>(
native: N, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L native: N,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
logger: L,
) -> Result<EGLContext<B, N>> ) -> Result<EGLContext<B, N>>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
@ -82,7 +85,9 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
} }
unsafe fn new_internal( unsafe fn new_internal(
ptr: ffi::NativeDisplayType, mut attributes: GlAttributes, reqs: PixelFormatRequirements, ptr: ffi::NativeDisplayType,
mut attributes: GlAttributes,
reqs: PixelFormatRequirements,
log: ::slog::Logger, log: ::slog::Logger,
) -> Result< ) -> Result<
( (

View File

@ -42,7 +42,8 @@ pub mod egl {
#[allow(non_snake_case, unused_variables, dead_code)] #[allow(non_snake_case, unused_variables, dead_code)]
#[inline] #[inline]
pub unsafe fn BindWaylandDisplayWL( pub unsafe fn BindWaylandDisplayWL(
dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void dpy: types::EGLDisplay,
display: *mut __gl_imports::raw::c_void,
) -> types::EGLBoolean { ) -> types::EGLBoolean {
__gl_imports::mem::transmute::< __gl_imports::mem::transmute::<
_, _,
@ -53,7 +54,8 @@ pub mod egl {
#[allow(non_snake_case, unused_variables, dead_code)] #[allow(non_snake_case, unused_variables, dead_code)]
#[inline] #[inline]
pub unsafe fn UnbindWaylandDisplayWL( pub unsafe fn UnbindWaylandDisplayWL(
dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void dpy: types::EGLDisplay,
display: *mut __gl_imports::raw::c_void,
) -> types::EGLBoolean { ) -> types::EGLBoolean {
__gl_imports::mem::transmute::< __gl_imports::mem::transmute::<
_, _,
@ -64,7 +66,9 @@ pub mod egl {
#[allow(non_snake_case, unused_variables, dead_code)] #[allow(non_snake_case, unused_variables, dead_code)]
#[inline] #[inline]
pub unsafe fn QueryWaylandBufferWL( pub unsafe fn QueryWaylandBufferWL(
dpy: types::EGLDisplay, buffer: *mut __gl_imports::raw::c_void, attribute: types::EGLint, dpy: types::EGLDisplay,
buffer: *mut __gl_imports::raw::c_void,
attribute: types::EGLint,
value: *mut types::EGLint, value: *mut types::EGLint,
) -> types::EGLBoolean { ) -> types::EGLBoolean {
__gl_imports::mem::transmute::< __gl_imports::mem::transmute::<

View File

@ -31,7 +31,9 @@ pub trait Backend {
/// The returned `EGLDisplay` needs to be a valid ptr for egl, /// The returned `EGLDisplay` needs to be a valid ptr for egl,
/// but there is no way to test that. /// but there is no way to test that.
unsafe fn get_display<F: Fn(&str) -> bool>( unsafe fn get_display<F: Fn(&str) -> bool>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay; ) -> ffi::egl::types::EGLDisplay;
} }
@ -43,7 +45,9 @@ impl Backend for Wayland {
type Surface = wegl::WlEglSurface; type Surface = wegl::WlEglSurface;
unsafe fn get_display<F>( unsafe fn get_display<F>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay ) -> ffi::egl::types::EGLDisplay
where where
F: Fn(&str) -> bool, F: Fn(&str) -> bool,
@ -87,7 +91,9 @@ impl Backend for X11 {
type Surface = XlibWindow; type Surface = XlibWindow;
unsafe fn get_display<F>( unsafe fn get_display<F>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay ) -> ffi::egl::types::EGLDisplay
where where
F: Fn(&str) -> bool, F: Fn(&str) -> bool,
@ -114,7 +120,9 @@ impl<T: 'static> Backend for Gbm<T> {
type Surface = GbmSurface<T>; type Surface = GbmSurface<T>;
unsafe fn get_display<F>( unsafe fn get_display<F>(
display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay ) -> ffi::egl::types::EGLDisplay
where where
F: Fn(&str) -> bool, F: Fn(&str) -> bool,

View File

@ -30,7 +30,8 @@ impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
impl<N: native::NativeSurface> EGLSurface<N> { impl<N: native::NativeSurface> EGLSurface<N> {
pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>( pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>(
context: &EGLContext<B, D>, native: N context: &EGLContext<B, D>,
native: N,
) -> Result<EGLSurface<N>> { ) -> Result<EGLSurface<N>> {
let surface = unsafe { let surface = unsafe {
ffi::egl::CreateWindowSurface( ffi::egl::CreateWindowSurface(

View File

@ -17,7 +17,7 @@ use nix::libc::c_uint;
use std::fmt; use std::fmt;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use wayland_server::{Display, Resource}; use wayland_server::{Display, Resource};
use wayland_server::protocol::wl_buffer::WlBuffer; use wayland_server::protocol::wl_buffer::{self, WlBuffer};
use wayland_sys::server::wl_display; use wayland_sys::server::wl_display;
/// Error that can occur when accessing an EGL buffer /// Error that can occur when accessing an EGL buffer
@ -25,7 +25,7 @@ pub enum BufferAccessError {
/// The corresponding Context is not alive anymore /// The corresponding Context is not alive anymore
ContextLost, ContextLost,
/// This buffer is not managed by the EGL buffer /// This buffer is not managed by the EGL buffer
NotManaged(WlBuffer), NotManaged(Resource<WlBuffer>),
/// Failed to create EGLImages from the buffer /// Failed to create EGLImages from the buffer
EGLImageCreationFailed, EGLImageCreationFailed,
/// The required EGL extension is not supported by the underlying EGL implementation /// The required EGL extension is not supported by the underlying EGL implementation
@ -175,7 +175,7 @@ pub struct EGLImages {
/// Format of these images /// Format of these images
pub format: Format, pub format: Format,
images: Vec<EGLImage>, images: Vec<EGLImage>,
buffer: WlBuffer, buffer: Resource<WlBuffer>,
} }
impl EGLImages { impl EGLImages {
@ -192,7 +192,9 @@ impl EGLImages {
/// ///
/// The given `tex_id` needs to be a valid GL texture otherwise undefined behavior might occur. /// The given `tex_id` needs to be a valid GL texture otherwise undefined behavior might occur.
pub unsafe fn bind_to_texture( pub unsafe fn bind_to_texture(
&self, plane: usize, tex_id: c_uint &self,
plane: usize,
tex_id: c_uint,
) -> ::std::result::Result<(), TextureCreationError> { ) -> ::std::result::Result<(), TextureCreationError> {
if self.display.upgrade().is_some() { if self.display.upgrade().is_some() {
let mut old_tex_id: i32 = 0; let mut old_tex_id: i32 = 0;
@ -225,7 +227,7 @@ impl Drop for EGLImages {
} }
} }
} }
self.buffer.release(); self.buffer.send(wl_buffer::Event::Release);
} }
} }
@ -255,7 +257,8 @@ pub struct EGLDisplay(Weak<ffi::egl::types::EGLDisplay>, *mut wl_display);
impl EGLDisplay { impl EGLDisplay {
fn new<B: native::Backend, N: native::NativeDisplay<B>>( fn new<B: native::Backend, N: native::NativeDisplay<B>>(
context: &EGLContext<B, N>, display: *mut wl_display context: &EGLContext<B, N>,
display: *mut wl_display,
) -> EGLDisplay { ) -> EGLDisplay {
EGLDisplay(Rc::downgrade(&context.display), display) EGLDisplay(Rc::downgrade(&context.display), display)
} }
@ -266,14 +269,15 @@ impl EGLDisplay {
/// a `BufferAccessError::NotManaged(WlBuffer)` is returned with the original buffer /// a `BufferAccessError::NotManaged(WlBuffer)` is returned with the original buffer
/// to render it another way. /// to render it another way.
pub fn egl_buffer_contents( pub fn egl_buffer_contents(
&self, buffer: WlBuffer &self,
buffer: Resource<WlBuffer>,
) -> ::std::result::Result<EGLImages, BufferAccessError> { ) -> ::std::result::Result<EGLImages, BufferAccessError> {
if let Some(display) = self.0.upgrade() { if let Some(display) = self.0.upgrade() {
let mut format: i32 = 0; let mut format: i32 = 0;
if unsafe { if unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
*display, *display,
buffer.ptr() as *mut _, buffer.c_ptr() as *mut _,
ffi::egl::EGL_TEXTURE_FORMAT, ffi::egl::EGL_TEXTURE_FORMAT,
&mut format as *mut _, &mut format as *mut _,
) == 0 ) == 0
@ -294,7 +298,7 @@ impl EGLDisplay {
if unsafe { if unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
*display, *display,
buffer.ptr() as *mut _, buffer.c_ptr() as *mut _,
ffi::egl::WIDTH as i32, ffi::egl::WIDTH as i32,
&mut width as *mut _, &mut width as *mut _,
) == 0 ) == 0
@ -306,7 +310,7 @@ impl EGLDisplay {
if unsafe { if unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
*display, *display,
buffer.ptr() as *mut _, buffer.c_ptr() as *mut _,
ffi::egl::HEIGHT as i32, ffi::egl::HEIGHT as i32,
&mut height as *mut _, &mut height as *mut _,
) == 0 ) == 0
@ -318,7 +322,7 @@ impl EGLDisplay {
if unsafe { if unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
*display, *display,
buffer.ptr() as *mut _, buffer.c_ptr() as *mut _,
ffi::egl::WAYLAND_Y_INVERTED_WL, ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted as *mut _, &mut inverted as *mut _,
) != 0 ) != 0
@ -339,7 +343,7 @@ impl EGLDisplay {
*display, *display,
ffi::egl::NO_CONTEXT, ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL, ffi::egl::WAYLAND_BUFFER_WL,
buffer.ptr() as *mut _, buffer.c_ptr() as *mut _,
out.as_ptr(), out.as_ptr(),
) )
}; };
@ -394,10 +398,10 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLWaylandExtensions for E
if !self.egl_to_texture_support { if !self.egl_to_texture_support {
bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"])); bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"]));
} }
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) };
if res == 0 { if res == 0 {
bail!(ErrorKind::OtherEGLDisplayAlreadyBound); bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
} }
Ok(EGLDisplay::new(self, unsafe { display.ptr() })) Ok(EGLDisplay::new(self, display.c_ptr()))
} }
} }

View File

@ -30,7 +30,9 @@ pub trait GraphicsBackend {
/// from raw image buffers over a fixed list of possible cursor types to simply the /// from raw image buffers over a fixed list of possible cursor types to simply the
/// void type () to represent no possible customization of the cursor itself. /// void type () to represent no possible customization of the cursor itself.
fn set_cursor_representation( fn set_cursor_representation(
&self, cursor: &Self::CursorFormat, hotspot: (u32, u32) &self,
cursor: &Self::CursorFormat,
hotspot: (u32, u32),
) -> Result<(), Self::Error>; ) -> Result<(), Self::Error>;
} }

View File

@ -2,7 +2,6 @@
use std::error::Error; use std::error::Error;
use std::string::ToString; use std::string::ToString;
use wayland_server::EventLoopHandle;
/// A seat describes a group of input devices and at least one /// A seat describes a group of input devices and at least one
/// graphics device belonging together. /// graphics device belonging together.
@ -521,31 +520,31 @@ pub trait InputBackend: Sized {
type TouchFrameEvent: TouchFrameEvent; type TouchFrameEvent: TouchFrameEvent;
/// Sets a new handler for this `InputBackend` /// Sets a new handler for this `InputBackend`
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, evlh: &mut EventLoopHandle, handler: H); fn set_handler<H: InputHandler<Self> + 'static>(&mut self, handler: H);
/// Get a reference to the currently set handler, if any /// Get a reference to the currently set handler, if any
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>>; fn get_handler(&mut self) -> Option<&mut InputHandler<Self>>;
/// Clears the currently handler, if one is set /// Clears the currently handler, if one is set
fn clear_handler(&mut self, evlh: &mut EventLoopHandle); fn clear_handler(&mut self);
/// Get current `InputConfig` /// Get current `InputConfig`
fn input_config(&mut self) -> &mut Self::InputConfig; fn input_config(&mut self) -> &mut Self::InputConfig;
/// Processes new events of the underlying backend and drives the `InputHandler`. /// Processes new events of the underlying backend and drives the `InputHandler`.
fn dispatch_new_events(&mut self, evlh: &mut EventLoopHandle) -> Result<(), Self::EventError>; fn dispatch_new_events(&mut self) -> Result<(), Self::EventError>;
} }
/// Implement to receive input events from any `InputBackend`. /// Implement to receive input events from any `InputBackend`.
pub trait InputHandler<B: InputBackend> { pub trait InputHandler<B: InputBackend> {
/// Called when a new `Seat` has been created /// Called when a new `Seat` has been created
fn on_seat_created(&mut self, evlh: &mut EventLoopHandle, seat: &Seat); fn on_seat_created(&mut self, seat: &Seat);
/// Called when an existing `Seat` has been destroyed. /// Called when an existing `Seat` has been destroyed.
fn on_seat_destroyed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat); fn on_seat_destroyed(&mut self, seat: &Seat);
/// Called when a `Seat`'s properties have changed. /// Called when a `Seat`'s properties have changed.
/// ///
/// ## Note: /// ## Note:
/// ///
/// It is not guaranteed that any change has actually happened. /// It is not guaranteed that any change has actually happened.
fn on_seat_changed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat); fn on_seat_changed(&mut self, seat: &Seat);
/// Called when a new keyboard event was received. /// Called when a new keyboard event was received.
/// ///
@ -554,7 +553,7 @@ pub trait InputHandler<B: InputBackend> {
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The keyboard event /// - `event` - The keyboard event
/// ///
fn on_keyboard_key(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::KeyboardKeyEvent); fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent);
/// Called when a new pointer movement event was received. /// Called when a new pointer movement event was received.
/// ///
@ -562,30 +561,28 @@ pub trait InputHandler<B: InputBackend> {
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The pointer movement event /// - `event` - The pointer movement event
fn on_pointer_move(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionEvent); fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent);
/// Called when a new pointer absolute movement event was received. /// Called when a new pointer absolute movement event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The pointer absolute movement event /// - `event` - The pointer absolute movement event
fn on_pointer_move_absolute( fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent);
&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionAbsoluteEvent
);
/// Called when a new pointer button event was received. /// Called when a new pointer button event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The pointer button event /// - `event` - The pointer button event
fn on_pointer_button(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerButtonEvent); fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent);
/// Called when a new pointer scroll event was received. /// Called when a new pointer scroll event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. /// - `event` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
fn on_pointer_axis(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerAxisEvent); fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent);
/// Called when a new touch down event was received. /// Called when a new touch down event was received.
/// ///
@ -593,99 +590,97 @@ pub trait InputHandler<B: InputBackend> {
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch down event /// - `event` - The touch down event
fn on_touch_down(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchDownEvent); fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent);
/// Called when a new touch motion event was received. /// Called when a new touch motion event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch motion event. /// - `event` - The touch motion event.
fn on_touch_motion(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchMotionEvent); fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent);
/// Called when a new touch up event was received. /// Called when a new touch up event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch up event. /// - `event` - The touch up event.
fn on_touch_up(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchUpEvent); fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent);
/// Called when a new touch cancel event was received. /// Called when a new touch cancel event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch cancel event. /// - `event` - The touch cancel event.
fn on_touch_cancel(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchCancelEvent); fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent);
/// Called when a new touch frame event was received. /// Called when a new touch frame event was received.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `seat` - The `Seat` the event belongs to /// - `seat` - The `Seat` the event belongs to
/// - `event` - The touch frame event. /// - `event` - The touch frame event.
fn on_touch_frame(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchFrameEvent); fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent);
/// Called when the `InputConfig` was changed through an external event. /// Called when the `InputConfig` was changed through an external event.
/// ///
/// What kind of events can trigger this call is completely backend dependent. /// What kind of events can trigger this call is completely backend dependent.
/// E.g. an input devices was attached/detached or changed it's own configuration. /// E.g. an input devices was attached/detached or changed it's own configuration.
fn on_input_config_changed(&mut self, evlh: &mut EventLoopHandle, config: &mut B::InputConfig); fn on_input_config_changed(&mut self, config: &mut B::InputConfig);
} }
impl<B: InputBackend> InputHandler<B> for Box<InputHandler<B>> { impl<B: InputBackend> InputHandler<B> for Box<InputHandler<B>> {
fn on_seat_created(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) { fn on_seat_created(&mut self, seat: &Seat) {
(**self).on_seat_created(evlh, seat) (**self).on_seat_created(seat)
} }
fn on_seat_destroyed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) { fn on_seat_destroyed(&mut self, seat: &Seat) {
(**self).on_seat_destroyed(evlh, seat) (**self).on_seat_destroyed(seat)
} }
fn on_seat_changed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) { fn on_seat_changed(&mut self, seat: &Seat) {
(**self).on_seat_changed(evlh, seat) (**self).on_seat_changed(seat)
} }
fn on_keyboard_key(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::KeyboardKeyEvent) { fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent) {
(**self).on_keyboard_key(evlh, seat, event) (**self).on_keyboard_key(seat, event)
} }
fn on_pointer_move(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionEvent) { fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent) {
(**self).on_pointer_move(evlh, seat, event) (**self).on_pointer_move(seat, event)
} }
fn on_pointer_move_absolute( fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent) {
&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionAbsoluteEvent (**self).on_pointer_move_absolute(seat, event)
) {
(**self).on_pointer_move_absolute(evlh, seat, event)
} }
fn on_pointer_button(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerButtonEvent) { fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent) {
(**self).on_pointer_button(evlh, seat, event) (**self).on_pointer_button(seat, event)
} }
fn on_pointer_axis(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerAxisEvent) { fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent) {
(**self).on_pointer_axis(evlh, seat, event) (**self).on_pointer_axis(seat, event)
} }
fn on_touch_down(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchDownEvent) { fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent) {
(**self).on_touch_down(evlh, seat, event) (**self).on_touch_down(seat, event)
} }
fn on_touch_motion(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchMotionEvent) { fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent) {
(**self).on_touch_motion(evlh, seat, event) (**self).on_touch_motion(seat, event)
} }
fn on_touch_up(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchUpEvent) { fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent) {
(**self).on_touch_up(evlh, seat, event) (**self).on_touch_up(seat, event)
} }
fn on_touch_cancel(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchCancelEvent) { fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent) {
(**self).on_touch_cancel(evlh, seat, event) (**self).on_touch_cancel(seat, event)
} }
fn on_touch_frame(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchFrameEvent) { fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent) {
(**self).on_touch_frame(evlh, seat, event) (**self).on_touch_frame(seat, event)
} }
fn on_input_config_changed(&mut self, evlh: &mut EventLoopHandle, config: &mut B::InputConfig) { fn on_input_config_changed(&mut self, config: &mut B::InputConfig) {
(**self).on_input_config_changed(evlh, config) (**self).on_input_config_changed(config)
} }
} }

View File

@ -11,8 +11,9 @@ use std::hash::{Hash, Hasher};
use std::io::Error as IoError; use std::io::Error as IoError;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::path::Path; use std::path::Path;
use wayland_server::EventLoopHandle; use wayland_server::LoopToken;
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
// No idea if this is the same across unix platforms // No idea if this is the same across unix platforms
// Lets make this linux exclusive for now, once someone tries to build it for // Lets make this linux exclusive for now, once someone tries to build it for
@ -256,16 +257,14 @@ impl backend::InputBackend for LibinputInputBackend {
type TouchCancelEvent = event::touch::TouchCancelEvent; type TouchCancelEvent = event::touch::TouchCancelEvent;
type TouchFrameEvent = event::touch::TouchFrameEvent; type TouchFrameEvent = event::touch::TouchFrameEvent;
fn set_handler<H: backend::InputHandler<Self> + 'static>( fn set_handler<H: backend::InputHandler<Self> + 'static>(&mut self, mut handler: H) {
&mut self, evlh: &mut EventLoopHandle, mut handler: H
) {
if self.handler.is_some() { if self.handler.is_some() {
self.clear_handler(evlh); self.clear_handler();
} }
info!(self.logger, "New input handler set"); info!(self.logger, "New input handler set");
for seat in self.seats.values() { for seat in self.seats.values() {
trace!(self.logger, "Calling on_seat_created with {:?}", seat); trace!(self.logger, "Calling on_seat_created with {:?}", seat);
handler.on_seat_created(evlh, seat); handler.on_seat_created(seat);
} }
self.handler = Some(Box::new(handler)); self.handler = Some(Box::new(handler));
} }
@ -276,11 +275,11 @@ impl backend::InputBackend for LibinputInputBackend {
.map(|handler| handler as &mut backend::InputHandler<Self>) .map(|handler| handler as &mut backend::InputHandler<Self>)
} }
fn clear_handler(&mut self, evlh: &mut EventLoopHandle) { fn clear_handler(&mut self) {
if let Some(mut handler) = self.handler.take() { if let Some(mut handler) = self.handler.take() {
for seat in self.seats.values() { for seat in self.seats.values() {
trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat); trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat);
handler.on_seat_destroyed(evlh, seat); handler.on_seat_destroyed(seat);
} }
info!(self.logger, "Removing input handler"); info!(self.logger, "Removing input handler");
} }
@ -290,7 +289,7 @@ impl backend::InputBackend for LibinputInputBackend {
&mut self.devices &mut self.devices
} }
fn dispatch_new_events(&mut self, evlh: &mut EventLoopHandle) -> Result<(), IoError> { fn dispatch_new_events(&mut self) -> Result<(), IoError> {
use input::event::EventTrait; use input::event::EventTrait;
self.context.dispatch()?; self.context.dispatch()?;
@ -323,7 +322,7 @@ impl backend::InputBackend for LibinputInputBackend {
} }
if let Some(ref mut handler) = self.handler { if let Some(ref mut handler) = self.handler {
trace!(self.logger, "Calling on_seat_changed with {:?}", old_seat); trace!(self.logger, "Calling on_seat_changed with {:?}", old_seat);
handler.on_seat_changed(evlh, old_seat); handler.on_seat_changed(old_seat);
} }
} }
Entry::Vacant(seat_entry) => { Entry::Vacant(seat_entry) => {
@ -340,7 +339,7 @@ impl backend::InputBackend for LibinputInputBackend {
)); ));
if let Some(ref mut handler) = self.handler { if let Some(ref mut handler) = self.handler {
trace!(self.logger, "Calling on_seat_created with {:?}", seat); trace!(self.logger, "Calling on_seat_created with {:?}", seat);
handler.on_seat_created(evlh, seat); handler.on_seat_created(seat);
} }
} }
} }
@ -379,7 +378,7 @@ impl backend::InputBackend for LibinputInputBackend {
if let Some(seat) = self.seats.remove(&device_seat) { if let Some(seat) = self.seats.remove(&device_seat) {
if let Some(ref mut handler) = self.handler { if let Some(ref mut handler) = self.handler {
trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat); trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat);
handler.on_seat_destroyed(evlh, &seat); handler.on_seat_destroyed(&seat);
} }
} else { } else {
warn!(self.logger, "Seat destroyed that was never created"); warn!(self.logger, "Seat destroyed that was never created");
@ -389,7 +388,7 @@ impl backend::InputBackend for LibinputInputBackend {
} else if let Some(ref mut handler) = self.handler { } else if let Some(ref mut handler) = self.handler {
if let Some(seat) = self.seats.get(&device_seat) { if let Some(seat) = self.seats.get(&device_seat) {
trace!(self.logger, "Calling on_seat_changed with {:?}", seat); trace!(self.logger, "Calling on_seat_changed with {:?}", seat);
handler.on_seat_changed(evlh, &seat); handler.on_seat_changed(&seat);
} else { } else {
warn!(self.logger, "Seat changed that was never created"); warn!(self.logger, "Seat changed that was never created");
continue; continue;
@ -398,7 +397,7 @@ impl backend::InputBackend for LibinputInputBackend {
} }
} }
if let Some(ref mut handler) = self.handler { if let Some(ref mut handler) = self.handler {
handler.on_input_config_changed(evlh, &mut self.devices); handler.on_input_config_changed(&mut self.devices);
} }
} }
libinput::Event::Touch(touch_event) => { libinput::Event::Touch(touch_event) => {
@ -409,7 +408,7 @@ impl backend::InputBackend for LibinputInputBackend {
match touch_event { match touch_event {
TouchEvent::Down(down_event) => { TouchEvent::Down(down_event) => {
trace!(self.logger, "Calling on_touch_down with {:?}", down_event); trace!(self.logger, "Calling on_touch_down with {:?}", down_event);
handler.on_touch_down(evlh, seat, down_event) handler.on_touch_down(seat, down_event)
} }
TouchEvent::Motion(motion_event) => { TouchEvent::Motion(motion_event) => {
trace!( trace!(
@ -417,11 +416,11 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_touch_motion with {:?}", "Calling on_touch_motion with {:?}",
motion_event motion_event
); );
handler.on_touch_motion(evlh, seat, motion_event) handler.on_touch_motion(seat, motion_event)
} }
TouchEvent::Up(up_event) => { TouchEvent::Up(up_event) => {
trace!(self.logger, "Calling on_touch_up with {:?}", up_event); trace!(self.logger, "Calling on_touch_up with {:?}", up_event);
handler.on_touch_up(evlh, seat, up_event) handler.on_touch_up(seat, up_event)
} }
TouchEvent::Cancel(cancel_event) => { TouchEvent::Cancel(cancel_event) => {
trace!( trace!(
@ -429,11 +428,11 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_touch_cancel with {:?}", "Calling on_touch_cancel with {:?}",
cancel_event cancel_event
); );
handler.on_touch_cancel(evlh, seat, cancel_event) handler.on_touch_cancel(seat, cancel_event)
} }
TouchEvent::Frame(frame_event) => { TouchEvent::Frame(frame_event) => {
trace!(self.logger, "Calling on_touch_frame with {:?}", frame_event); trace!(self.logger, "Calling on_touch_frame with {:?}", frame_event);
handler.on_touch_frame(evlh, seat, frame_event) handler.on_touch_frame(seat, frame_event)
} }
} }
} else { } else {
@ -449,7 +448,7 @@ impl backend::InputBackend for LibinputInputBackend {
let device_seat = key_event.device().seat(); let device_seat = key_event.device().seat();
if let &Some(ref seat) = &self.seats.get(&device_seat) { if let &Some(ref seat) = &self.seats.get(&device_seat) {
trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event); trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event);
handler.on_keyboard_key(evlh, seat, key_event); handler.on_keyboard_key(seat, key_event);
} else { } else {
warn!(self.logger, "Recieved key event of non existing Seat"); warn!(self.logger, "Recieved key event of non existing Seat");
continue; continue;
@ -469,7 +468,7 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_pointer_move with {:?}", "Calling on_pointer_move with {:?}",
motion_event motion_event
); );
handler.on_pointer_move(evlh, seat, motion_event); handler.on_pointer_move(seat, motion_event);
} }
PointerEvent::MotionAbsolute(motion_abs_event) => { PointerEvent::MotionAbsolute(motion_abs_event) => {
trace!( trace!(
@ -477,11 +476,11 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_pointer_move_absolute with {:?}", "Calling on_pointer_move_absolute with {:?}",
motion_abs_event motion_abs_event
); );
handler.on_pointer_move_absolute(evlh, seat, motion_abs_event); handler.on_pointer_move_absolute(seat, motion_abs_event);
} }
PointerEvent::Axis(axis_event) => { PointerEvent::Axis(axis_event) => {
trace!(self.logger, "Calling on_pointer_axis with {:?}", axis_event); trace!(self.logger, "Calling on_pointer_axis with {:?}", axis_event);
handler.on_pointer_axis(evlh, seat, axis_event); handler.on_pointer_axis(seat, axis_event);
} }
PointerEvent::Button(button_event) => { PointerEvent::Button(button_event) => {
trace!( trace!(
@ -489,7 +488,7 @@ impl backend::InputBackend for LibinputInputBackend {
"Calling on_pointer_button with {:?}", "Calling on_pointer_button with {:?}",
button_event button_event
); );
handler.on_pointer_button(evlh, seat, button_event); handler.on_pointer_button(seat, button_event);
} }
} }
} else { } else {
@ -554,7 +553,7 @@ impl From<event::pointer::ButtonState> for backend::MouseButtonState {
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
impl SessionObserver for libinput::Libinput { impl SessionObserver for libinput::Libinput {
fn pause(&mut self, _state: &mut EventLoopHandle, device: Option<(u32, u32)>) { fn pause(&mut self, device: Option<(u32, u32)>) {
if let Some((major, _)) = device { if let Some((major, _)) = device {
if major != INPUT_MAJOR { if major != INPUT_MAJOR {
return; return;
@ -564,7 +563,7 @@ impl SessionObserver for libinput::Libinput {
self.suspend() self.suspend()
} }
fn activate(&mut self, _state: &mut EventLoopHandle, _device: Option<(u32, u32, Option<RawFd>)>) { fn activate(&mut self, _device: Option<(u32, u32, Option<RawFd>)>) {
// libinput closes the devices on suspend, so we should not get any INPUT_MAJOR calls // libinput closes the devices on suspend, so we should not get any INPUT_MAJOR calls
// also lets hope multiple resumes are okay in case of logind // also lets hope multiple resumes are okay in case of logind
self.resume().expect("Unable to resume libinput context"); self.resume().expect("Unable to resume libinput context");
@ -602,27 +601,25 @@ impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
/// Automatically feeds the backend with incoming events without any manual calls to /// Automatically feeds the backend with incoming events without any manual calls to
/// `dispatch_new_events`. Should be used to achieve the smallest possible latency. /// `dispatch_new_events`. Should be used to achieve the smallest possible latency.
pub fn libinput_bind( pub fn libinput_bind(
backend: LibinputInputBackend, evlh: &mut EventLoopHandle backend: LibinputInputBackend,
) -> ::std::result::Result<FdEventSource<LibinputInputBackend>, (IoError, LibinputInputBackend)> { token: LoopToken,
) -> ::std::result::Result<Source<FdEvent>, (IoError, LibinputInputBackend)> {
let fd = unsafe { backend.context.fd() }; let fd = unsafe { backend.context.fd() };
evlh.add_fd_event_source( token.add_fd_event_source(fd, FdInterest::READ, backend)
fd,
fd_event_source_implementation(),
backend,
FdInterest::READ,
)
} }
fn fd_event_source_implementation() -> FdEventSourceImpl<LibinputInputBackend> { impl Implementation<(), FdEvent> for LibinputInputBackend {
FdEventSourceImpl { fn receive(&mut self, event: FdEvent, (): ()) {
ready: |evlh, ref mut backend, _, _| { match event {
use backend::input::InputBackend; FdEvent::Ready { .. } => {
if let Err(error) = backend.dispatch_new_events(evlh) { use backend::input::InputBackend;
warn!(backend.logger, "Libinput errored: {}", error); if let Err(error) = self.dispatch_new_events() {
warn!(self.logger, "Libinput errored: {}", error);
}
} }
}, FdEvent::Error { error, .. } => {
error: |_evlh, ref backend, _, error| { warn!(self.logger, "Libinput fd errored: {}", error);
warn!(backend.logger, "Libinput fd errored: {}", error); }
}, }
} }
} }

View File

@ -39,8 +39,9 @@ use std::io::Error as IoError;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use wayland_server::EventLoopHandle; use wayland_server::LoopToken;
use wayland_server::sources::{EventSource, SignalEventSource}; use wayland_server::commons::downcast_impl;
use wayland_server::sources::{SignalEvent, Source};
/// `Session` using the best available inteface /// `Session` using the best available inteface
#[derive(Clone)] #[derive(Clone)]
@ -71,7 +72,7 @@ pub enum BoundAutoSession {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
Logind(BoundLogindSession), Logind(BoundLogindSession),
/// Bound direct / tty session /// Bound direct / tty session
Direct(SignalEventSource<DirectSessionNotifier>), Direct(Source<SignalEvent>),
} }
/// Id's used by the `AutoSessionNotifier` internally. /// Id's used by the `AutoSessionNotifier` internally.
@ -153,13 +154,14 @@ impl AutoSession {
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the /// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`. /// session state and call it's `SessionObservers`.
pub fn auto_session_bind( pub fn auto_session_bind(
notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle notifier: AutoSessionNotifier,
token: &LoopToken,
) -> ::std::result::Result<BoundAutoSession, (IoError, AutoSessionNotifier)> { ) -> ::std::result::Result<BoundAutoSession, (IoError, AutoSessionNotifier)> {
Ok(match notifier { Ok(match notifier {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, evlh) AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, token)
.map_err(|(error, notifier)| (error, AutoSessionNotifier::Logind(notifier)))?), .map_err(|(error, notifier)| (error, AutoSessionNotifier::Logind(notifier)))?),
AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, evlh) AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, token)
.map_err(|(error, notifier)| (error, AutoSessionNotifier::Direct(notifier)))?), .map_err(|(error, notifier)| (error, AutoSessionNotifier::Direct(notifier)))?),
}) })
} }
@ -210,7 +212,8 @@ impl SessionNotifier for AutoSessionNotifier {
type Id = AutoId; type Id = AutoId;
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>( fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
&mut self, signal: &mut A &mut self,
signal: &mut A,
) -> Self::Id { ) -> Self::Id {
match self { match self {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
@ -257,7 +260,9 @@ impl BoundAutoSession {
match self { match self {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()), BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()), BoundAutoSession::Direct(source) => {
AutoSessionNotifier::Direct(*downcast_impl(source.remove()).unwrap_or_else(|_| unreachable!()))
}
} }
} }
} }

View File

@ -42,8 +42,9 @@ use std::path::Path;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use systemd::login; use systemd::login;
use wayland_server::EventLoopHandle; use wayland_server::LoopToken;
use wayland_server::sources::{EventSource, FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
struct LogindSessionImpl { struct LogindSessionImpl {
conn: RefCell<Connection>, conn: RefCell<Connection>,
@ -62,6 +63,7 @@ pub struct LogindSession {
} }
/// `SessionNotifier` via the logind dbus interface /// `SessionNotifier` via the logind dbus interface
#[derive(Clone)]
pub struct LogindSessionNotifier { pub struct LogindSessionNotifier {
internal: Rc<LogindSessionImpl>, internal: Rc<LogindSessionImpl>,
} }
@ -186,7 +188,11 @@ impl LogindSessionNotifier {
impl LogindSessionImpl { impl LogindSessionImpl {
fn blocking_call<'d, 'p, 'i, 'm, D, P, I, M>( fn blocking_call<'d, 'p, 'i, 'm, D, P, I, M>(
conn: &Connection, destination: D, path: P, interface: I, method: M, conn: &Connection,
destination: D,
path: P,
interface: I,
method: M,
arguments: Option<Vec<MessageItem>>, arguments: Option<Vec<MessageItem>>,
) -> Result<Message> ) -> Result<Message>
where where
@ -230,7 +236,7 @@ impl LogindSessionImpl {
} }
} }
fn handle_signals(&self, evlh: &mut EventLoopHandle, signals: ConnectionItems) -> Result<()> { fn handle_signals(&self, signals: ConnectionItems) -> Result<()> {
for item in signals { for item in signals {
let message = if let ConnectionItem::Signal(ref s) = item { let message = if let ConnectionItem::Signal(ref s) = item {
s s
@ -246,7 +252,7 @@ impl LogindSessionImpl {
//So lets just put it to sleep.. forever //So lets just put it to sleep.. forever
for signal in &mut *self.signals.borrow_mut() { for signal in &mut *self.signals.borrow_mut() {
if let &mut Some(ref mut signal) = signal { if let &mut Some(ref mut signal) = signal {
signal.pause(evlh, None); signal.pause(None);
} }
} }
self.active.store(false, Ordering::SeqCst); self.active.store(false, Ordering::SeqCst);
@ -263,7 +269,7 @@ impl LogindSessionImpl {
); );
for signal in &mut *self.signals.borrow_mut() { for signal in &mut *self.signals.borrow_mut() {
if let &mut Some(ref mut signal) = signal { if let &mut Some(ref mut signal) = signal {
signal.pause(evlh, Some((major, minor))); signal.pause(Some((major, minor)));
} }
} }
// the other possible types are "force" or "gone" (unplugged), // the other possible types are "force" or "gone" (unplugged),
@ -289,7 +295,7 @@ impl LogindSessionImpl {
debug!(self.logger, "Reactivating device ({},{})", major, minor); debug!(self.logger, "Reactivating device ({},{})", major, minor);
for signal in &mut *self.signals.borrow_mut() { for signal in &mut *self.signals.borrow_mut() {
if let &mut Some(ref mut signal) = signal { if let &mut Some(ref mut signal) = signal {
signal.activate(evlh, Some((major, minor, Some(fd)))); signal.activate(Some((major, minor, Some(fd))));
} }
} }
} }
@ -393,7 +399,8 @@ impl SessionNotifier for LogindSessionNotifier {
type Id = Id; type Id = Id;
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>( fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
&mut self, signal: &mut A &mut self,
signal: &mut A,
) -> Self::Id { ) -> Self::Id {
self.internal self.internal
.signals .signals
@ -422,7 +429,7 @@ impl SessionNotifier for LogindSessionNotifier {
pub struct BoundLogindSession { pub struct BoundLogindSession {
notifier: LogindSessionNotifier, notifier: LogindSessionNotifier,
_watches: Vec<Watch>, _watches: Vec<Watch>,
sources: Vec<FdEventSource<Rc<LogindSessionImpl>>>, sources: Vec<Source<FdEvent>>,
} }
/// Bind a `LogindSessionNotifier` to an `EventLoop`. /// Bind a `LogindSessionNotifier` to an `EventLoop`.
@ -431,7 +438,8 @@ pub struct BoundLogindSession {
/// If you don't use this function `LogindSessionNotifier` will not correctly tell you the logind /// If you don't use this function `LogindSessionNotifier` will not correctly tell you the logind
/// session state and call it's `SessionObservers`. /// session state and call it's `SessionObservers`.
pub fn logind_session_bind( pub fn logind_session_bind(
notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle notifier: LogindSessionNotifier,
token: &LoopToken,
) -> ::std::result::Result<BoundLogindSession, (IoError, LogindSessionNotifier)> { ) -> ::std::result::Result<BoundLogindSession, (IoError, LogindSessionNotifier)> {
let watches = notifier.internal.conn.borrow().watch_fds(); let watches = notifier.internal.conn.borrow().watch_fds();
@ -443,14 +451,9 @@ pub fn logind_session_bind(
let mut interest = FdInterest::empty(); let mut interest = FdInterest::empty();
interest.set(FdInterest::READ, watch.readable()); interest.set(FdInterest::READ, watch.readable());
interest.set(FdInterest::WRITE, watch.writable()); interest.set(FdInterest::WRITE, watch.writable());
evlh.add_fd_event_source( token.add_fd_event_source(watch.fd(), interest, notifier.clone())
watch.fd(),
fd_event_source_implementation(),
notifier.internal.clone(),
interest,
)
}) })
.collect::<::std::result::Result<Vec<FdEventSource<Rc<LogindSessionImpl>>>, (IoError, _)>>() .collect::<::std::result::Result<Vec<Source<FdEvent>>, (IoError, _)>>()
.map_err(|(err, _)| { .map_err(|(err, _)| {
( (
err, err,
@ -492,35 +495,40 @@ impl Drop for LogindSessionNotifier {
} }
} }
fn fd_event_source_implementation() -> FdEventSourceImpl<Rc<LogindSessionImpl>> { impl Implementation<(), FdEvent> for LogindSessionNotifier {
FdEventSourceImpl { fn receive(&mut self, event: FdEvent, (): ()) {
ready: |evlh, session, fd, interest| { match event {
let conn = session.conn.borrow(); FdEvent::Ready { fd, mask } => {
let items = conn.watch_handle( let conn = self.internal.conn.borrow();
fd, let items = conn.watch_handle(
match interest { fd,
x if x.contains(FdInterest::READ) && x.contains(FdInterest::WRITE) => { match mask {
WatchEvent::Readable as u32 | WatchEvent::Writable as u32 x if x.contains(FdInterest::READ) && x.contains(FdInterest::WRITE) => {
} WatchEvent::Readable as u32 | WatchEvent::Writable as u32
x if x.contains(FdInterest::READ) => WatchEvent::Readable as u32, }
x if x.contains(FdInterest::WRITE) => WatchEvent::Writable as u32, x if x.contains(FdInterest::READ) => WatchEvent::Readable as u32,
_ => return, x if x.contains(FdInterest::WRITE) => WatchEvent::Writable as u32,
}, _ => return,
); },
if let Err(err) = session.handle_signals(evlh, items) { );
error!(session.logger, "Error handling dbus signals: {}", err); if let Err(err) = self.internal.handle_signals(items) {
error!(self.internal.logger, "Error handling dbus signals: {}", err);
}
} }
}, FdEvent::Error { fd, error } => {
error: |evlh, session, fd, error| { warn!(
warn!(session.logger, "Error on dbus connection: {:?}", error); self.internal.logger,
// handle the remaining messages, they might contain the SessionRemoved event "Error on dbus connection: {:?}", error
// in case the server did close the connection. );
let conn = session.conn.borrow(); // handle the remaining messages, they might contain the SessionRemoved event
let items = conn.watch_handle(fd, WatchEvent::Error as u32); // in case the server did close the connection.
if let Err(err) = session.handle_signals(evlh, items) { let conn = self.internal.conn.borrow();
error!(session.logger, "Error handling dbus signals: {}", err); let items = conn.watch_handle(fd, WatchEvent::Error as u32);
if let Err(err) = self.internal.handle_signals(items) {
error!(self.internal.logger, "Error handling dbus signals: {}", err);
}
} }
}, }
} }
} }

View File

@ -59,8 +59,9 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "backend_session_udev")] #[cfg(feature = "backend_session_udev")]
use udev::Context; use udev::Context;
use wayland_server::EventLoopHandle; use wayland_server::LoopToken;
use wayland_server::sources::SignalEventSource; use wayland_server::commons::Implementation;
use wayland_server::sources::{SignalEvent, Source};
#[allow(dead_code)] #[allow(dead_code)]
mod tty { mod tty {
@ -171,8 +172,11 @@ impl DirectSession {
.new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt")); .new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt"));
let fd = tty.map(|path| { let fd = tty.map(|path| {
open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty()) open(
.chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy()))) path,
fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC,
Mode::empty(),
).chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))
}).unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from("<stdin>"))))?; }).unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from("<stdin>"))))?;
let active = Arc::new(AtomicBool::new(true)); let active = Arc::new(AtomicBool::new(true));
@ -345,7 +349,8 @@ impl SessionNotifier for DirectSessionNotifier {
type Id = Id; type Id = Id;
fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>( fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
&mut self, signal: &mut A &mut self,
signal: &mut A,
) -> Self::Id { ) -> Self::Id {
self.signals.push(Some(Box::new(signal.observer()))); self.signals.push(Some(Box::new(signal.observer())));
Id(self.signals.len() - 1) Id(self.signals.len() - 1)
@ -362,47 +367,48 @@ impl SessionNotifier for DirectSessionNotifier {
} }
} }
impl Implementation<(), SignalEvent> for DirectSessionNotifier {
fn receive(&mut self, _signal: SignalEvent, (): ()) {
if self.is_active() {
info!(self.logger, "Session shall become inactive.");
for signal in &mut self.signals {
if let &mut Some(ref mut signal) = signal {
signal.pause(None);
}
}
self.active.store(false, Ordering::SeqCst);
unsafe {
tty::vt_rel_disp(self.tty, 1).expect("Unable to release tty lock");
}
debug!(self.logger, "Session is now inactive");
} else {
debug!(self.logger, "Session will become active again");
unsafe {
tty::vt_rel_disp(self.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
}
for signal in &mut self.signals {
if let &mut Some(ref mut signal) = signal {
signal.activate(None);
}
}
self.active.store(true, Ordering::SeqCst);
info!(self.logger, "Session is now active again");
}
}
}
/// Bind a `DirectSessionNotifier` to an `EventLoop`. /// Bind a `DirectSessionNotifier` to an `EventLoop`.
/// ///
/// Allows the `DirectSessionNotifier` to listen for incoming signals signalling the session state. /// Allows the `DirectSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current /// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
/// session state and call it's `SessionObservers`. /// session state and call it's `SessionObservers`.
pub fn direct_session_bind( pub fn direct_session_bind(
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle notifier: DirectSessionNotifier,
) -> ::std::result::Result<SignalEventSource<DirectSessionNotifier>, (IoError, DirectSessionNotifier)> { token: &LoopToken,
) -> ::std::result::Result<Source<SignalEvent>, (IoError, DirectSessionNotifier)> {
let signal = notifier.signal; let signal = notifier.signal;
evlh.add_signal_event_source( token.add_signal_event_source(signal, notifier)
|evlh, notifier, _| {
if notifier.is_active() {
info!(notifier.logger, "Session shall become inactive");
for signal in &mut notifier.signals {
if let &mut Some(ref mut signal) = signal {
signal.pause(evlh, None);
}
}
notifier.active.store(false, Ordering::SeqCst);
unsafe {
tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock");
}
debug!(notifier.logger, "Session is now inactive");
} else {
debug!(notifier.logger, "Session will become active again");
unsafe {
tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
}
for signal in &mut notifier.signals {
if let &mut Some(ref mut signal) = signal {
signal.activate(evlh, None);
}
}
notifier.active.store(true, Ordering::SeqCst);
info!(notifier.logger, "Session is now active again");
}
},
notifier,
signal,
)
} }
error_chain! { error_chain! {

View File

@ -16,7 +16,6 @@ use std::os::unix::io::RawFd;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use wayland_server::EventLoopHandle;
/// General session interface. /// General session interface.
/// ///
@ -88,7 +87,7 @@ pub trait SessionObserver {
/// If only a specific device shall be closed a device number in the form of /// If only a specific device shall be closed a device number in the form of
/// (major, minor) is provided. All observers not using the specified device should /// (major, minor) is provided. All observers not using the specified device should
/// ignore the signal in that case. /// ignore the signal in that case.
fn pause(&mut self, evlh: &mut EventLoopHandle, device: Option<(u32, u32)>); fn pause(&mut self, device: Option<(u32, u32)>);
/// Session/Device got active again /// Session/Device got active again
/// ///
/// If only a specific device shall be activated again a device number in the form of /// If only a specific device shall be activated again a device number in the form of
@ -96,7 +95,7 @@ pub trait SessionObserver {
/// the currently open file descriptor of the device with a new one. In that case the old one /// the currently open file descriptor of the device with a new one. In that case the old one
/// should not be used anymore and be closed. All observers not using the specified device should /// should not be used anymore and be closed. All observers not using the specified device should
/// ignore the signal in that case. /// ignore the signal in that case.
fn activate(&mut self, evlh: &mut EventLoopHandle, device: Option<(u32, u32, Option<RawFd>)>); fn activate(&mut self, device: Option<(u32, u32, Option<RawFd>)>);
} }
impl Session for () { impl Session for () {

View File

@ -24,8 +24,9 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult}; use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
use wayland_server::EventLoopHandle; use wayland_server::LoopToken;
use wayland_server::sources::{EventSource, FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::commons::Implementation;
use wayland_server::sources::{FdEvent, FdInterest, Source};
/// Udev's `DrmDevice` type based on the underlying session /// Udev's `DrmDevice` type based on the underlying session
pub struct SessionFdDrmDevice(RawFd); pub struct SessionFdDrmDevice(RawFd);
@ -48,11 +49,13 @@ pub struct UdevBackend<
S: Session + 'static, S: Session + 'static,
T: UdevHandler<H> + 'static, T: UdevHandler<H> + 'static,
> { > {
devices: Rc<RefCell<HashMap<dev_t, FdEventSource<(DrmDevice<SessionFdDrmDevice>, H)>>>>, _handler: ::std::marker::PhantomData<H>,
devices: Rc<RefCell<HashMap<dev_t, (Source<FdEvent>, Rc<RefCell<DrmDevice<SessionFdDrmDevice>>>)>>>,
monitor: MonitorSocket, monitor: MonitorSocket,
session: S, session: S,
handler: T, handler: T,
logger: ::slog::Logger, logger: ::slog::Logger,
token: LoopToken,
} }
impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevHandler<H> + 'static> impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevHandler<H> + 'static>
@ -67,7 +70,11 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
/// `handler` - User-provided handler to respond to any detected changes /// `handler` - User-provided handler to respond to any detected changes
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`. /// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
pub fn new<'a, L>( pub fn new<'a, L>(
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L token: LoopToken,
context: &Context,
mut session: S,
mut handler: T,
logger: L,
) -> Result<UdevBackend<H, S, T>> ) -> Result<UdevBackend<H, S, T>>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
@ -81,7 +88,7 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
.flat_map(|path| { .flat_map(|path| {
match DrmDevice::new( match DrmDevice::new(
{ {
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { match session.open(&path, fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC | fcntl::OFlag::O_NOCTTY | fcntl::OFlag::O_NONBLOCK) {
Ok(fd) => SessionFdDrmDevice(fd), Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => { Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
@ -94,13 +101,13 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
Ok(mut device) => { Ok(mut device) => {
let devnum = device.device_id(); let devnum = device.device_id();
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
match handler.device_added(evlh, &mut device) { match handler.device_added(&mut device) {
Some(drm_handler) => { Some(drm_handler) => {
match drm_device_bind(&mut evlh, device, drm_handler) { match drm_device_bind(&token, device, drm_handler) {
Ok(event_source) => Some((devnum, event_source)), Ok((event_source, device)) => Some((devnum, (event_source, device))),
Err((err, (mut device, _))) => { Err((err, (mut device, _))) => {
warn!(logger, "Failed to bind device. Error: {:?}.", err); warn!(logger, "Failed to bind device. Error: {:?}.", err);
handler.device_removed(evlh, &mut device); handler.device_removed(&mut device);
drop(device); drop(device);
if let Err(err) = session.close(fd) { if let Err(err) = session.close(fd) {
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
@ -124,7 +131,7 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
} }
} }
}) })
.collect::<HashMap<dev_t, FdEventSource<(DrmDevice<SessionFdDrmDevice>, H)>>>(); .collect::<HashMap<dev_t, _>>();
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?; let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
builder builder
@ -135,20 +142,25 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
.chain_err(|| ErrorKind::FailedToInitMonitor)?; .chain_err(|| ErrorKind::FailedToInitMonitor)?;
Ok(UdevBackend { Ok(UdevBackend {
_handler: ::std::marker::PhantomData,
devices: Rc::new(RefCell::new(devices)), devices: Rc::new(RefCell::new(devices)),
monitor, monitor,
session, session,
handler, handler,
logger, logger,
token,
}) })
} }
/// Closes the udev backend and frees all remaining open devices. /// Closes the udev backend and frees all remaining open devices.
pub fn close(&mut self, evlh: &mut EventLoopHandle) { pub fn close(&mut self) {
let mut devices = self.devices.borrow_mut(); let mut devices = self.devices.borrow_mut();
for (_, event_source) in devices.drain() { for (_, (event_source, device)) in devices.drain() {
let (mut device, _) = event_source.remove(); event_source.remove();
self.handler.device_removed(evlh, &mut device); let mut device = Rc::try_unwrap(device)
.unwrap_or_else(|_| unreachable!())
.into_inner();
self.handler.device_removed(&mut device);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
drop(device); drop(device);
if let Err(err) = self.session.close(fd) { if let Err(err) = self.session.close(fd) {
@ -163,8 +175,8 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static, S: Session + 'static, T: UdevH
} }
/// `SessionObserver` linked to the `UdevBackend` it was created from. /// `SessionObserver` linked to the `UdevBackend` it was created from.
pub struct UdevBackendObserver<H: DrmHandler<SessionFdDrmDevice> + 'static> { pub struct UdevBackendObserver {
devices: Weak<RefCell<HashMap<dev_t, FdEventSource<(DrmDevice<SessionFdDrmDevice>, H)>>>>, devices: Weak<RefCell<HashMap<dev_t, (Source<FdEvent>, Rc<RefCell<DrmDevice<SessionFdDrmDevice>>>)>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -172,9 +184,9 @@ impl<
H: DrmHandler<SessionFdDrmDevice> + 'static, H: DrmHandler<SessionFdDrmDevice> + 'static,
S: Session + 'static, S: Session + 'static,
T: UdevHandler<H> + 'static, T: UdevHandler<H> + 'static,
> AsSessionObserver<UdevBackendObserver<H>> for UdevBackend<H, S, T> > AsSessionObserver<UdevBackendObserver> for UdevBackend<H, S, T>
{ {
fn observer(&mut self) -> UdevBackendObserver<H> { fn observer(&mut self) -> UdevBackendObserver {
UdevBackendObserver { UdevBackendObserver {
devices: Rc::downgrade(&self.devices), devices: Rc::downgrade(&self.devices),
logger: self.logger.clone(), logger: self.logger.clone(),
@ -182,25 +194,21 @@ impl<
} }
} }
impl<H: DrmHandler<SessionFdDrmDevice> + 'static> SessionObserver for UdevBackendObserver<H> { impl SessionObserver for UdevBackendObserver {
fn pause<'a>(&mut self, evlh: &mut EventLoopHandle, devnum: Option<(u32, u32)>) { fn pause<'a>(&mut self, devnum: Option<(u32, u32)>) {
if let Some(devices) = self.devices.upgrade() { if let Some(devices) = self.devices.upgrade() {
for fd_event_source in devices.borrow_mut().values_mut() { for &mut (_, ref device) in devices.borrow_mut().values_mut() {
fd_event_source.with_idata(evlh, |&mut (ref mut device, _), evlh| { info!(self.logger, "changed successful");
info!(self.logger, "changed successful"); device.borrow_mut().observer().pause(devnum);
device.observer().pause(evlh, devnum);
})
} }
} }
} }
fn activate<'a>(&mut self, evlh: &mut EventLoopHandle, devnum: Option<(u32, u32, Option<RawFd>)>) { fn activate<'a>(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
if let Some(devices) = self.devices.upgrade() { if let Some(devices) = self.devices.upgrade() {
for fd_event_source in devices.borrow_mut().values_mut() { for &mut (_, ref device) in devices.borrow_mut().values_mut() {
fd_event_source.with_idata(evlh, |&mut (ref mut device, _), evlh| { info!(self.logger, "changed successful");
info!(self.logger, "changed successful"); device.borrow_mut().observer().activate(devnum);
device.observer().activate(evlh, devnum);
})
} }
} }
} }
@ -210,143 +218,153 @@ impl<H: DrmHandler<SessionFdDrmDevice> + 'static> SessionObserver for UdevBacken
/// ///
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`. /// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
/// No runtime functionality can be provided without using this function. /// No runtime functionality can be provided without using this function.
pub fn udev_backend_bind<S, H, T>( pub fn udev_backend_bind<H, S, T>(
evlh: &mut EventLoopHandle, udev: UdevBackend<H, S, T> token: &LoopToken,
) -> ::std::result::Result<FdEventSource<UdevBackend<H, S, T>>, (IoError, UdevBackend<H, S, T>)> udev: UdevBackend<H, S, T>,
) -> ::std::result::Result<Source<FdEvent>, (IoError, UdevBackend<H, S, T>)>
where where
H: DrmHandler<SessionFdDrmDevice> + 'static, H: DrmHandler<SessionFdDrmDevice> + 'static,
T: UdevHandler<H> + 'static, T: UdevHandler<H> + 'static,
S: Session + 'static, S: Session + 'static,
{ {
let fd = udev.monitor.as_raw_fd(); let fd = udev.monitor.as_raw_fd();
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ) token.add_fd_event_source(fd, FdInterest::READ, udev)
} }
fn fd_event_source_implementation<S, H, T>() -> FdEventSourceImpl<UdevBackend<H, S, T>> impl<H, S, T> Implementation<(), FdEvent> for UdevBackend<H, S, T>
where where
H: DrmHandler<SessionFdDrmDevice> + 'static, H: DrmHandler<SessionFdDrmDevice> + 'static,
T: UdevHandler<H> + 'static, T: UdevHandler<H> + 'static,
S: Session + 'static, S: Session + 'static,
{ {
FdEventSourceImpl { fn receive(&mut self, event: FdEvent, (): ()) {
ready: |mut evlh, udev, _, _| { match event {
let events = udev.monitor.clone().collect::<Vec<Event>>(); FdEvent::Ready { .. } => {
for event in events { let events = self.monitor.clone().collect::<Vec<Event>>();
match event.event_type() { for event in events {
// New device match event.event_type() {
EventType::Add => { // New device
info!(udev.logger, "Device Added"); EventType::Add => {
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { info!(self.logger, "Device Added");
let mut device = { if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
match DrmDevice::new( let mut device = {
{ match DrmDevice::new(
let logger = udev.logger.clone(); {
match udev.session.open( let logger = self.logger.clone();
path, match self.session.open(
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY path,
| fcntl::O_NONBLOCK, fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC
) { | fcntl::OFlag::O_NOCTTY
Ok(fd) => SessionFdDrmDevice(fd), | fcntl::OFlag::O_NONBLOCK,
Err(err) => { ) {
Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => {
warn!(
logger,
"Unable to open drm device {:?}, Error: {:?}. Skipping",
path,
err
);
continue;
}
}
},
self.logger.clone(),
) {
Ok(dev) => dev,
Err(err) => {
warn!(
self.logger,
"Failed to initialize device {:?}. Error: {}. Skipping",
path,
err
);
continue;
}
}
};
let fd = device.as_raw_fd();
match self.handler.device_added(&mut device) {
Some(drm_handler) => {
match drm_device_bind(&self.token, device, drm_handler) {
Ok(fd_event_source) => {
self.devices.borrow_mut().insert(devnum, fd_event_source);
}
Err((err, (mut device, _))) => {
warn!( warn!(
logger, self.logger,
"Unable to open drm device {:?}, Error: {:?}. Skipping", "Failed to bind device. Error: {:?}.", err
path,
err
); );
continue; self.handler.device_removed(&mut device);
drop(device);
if let Err(err) = self.session.close(fd) {
warn!(
self.logger,
"Failed to close dropped device. Error: {:?}. Ignoring", err
);
};
} }
} }
},
udev.logger.clone(),
) {
Ok(dev) => dev,
Err(err) => {
warn!(
udev.logger,
"Failed to initialize device {:?}. Error: {}. Skipping",
path,
err
);
continue;
} }
} None => {
}; self.handler.device_removed(&mut device);
let fd = device.as_raw_fd();
match udev.handler.device_added(evlh, &mut device) {
Some(drm_handler) => match drm_device_bind(&mut evlh, device, drm_handler) {
Ok(fd_event_source) => {
udev.devices.borrow_mut().insert(devnum, fd_event_source);
}
Err((err, (mut device, _))) => {
warn!(udev.logger, "Failed to bind device. Error: {:?}.", err);
udev.handler.device_removed(evlh, &mut device);
drop(device); drop(device);
if let Err(err) = udev.session.close(fd) { if let Err(err) = self.session.close(fd) {
warn!( warn!(
udev.logger, self.logger,
"Failed to close dropped device. Error: {:?}. Ignoring", err "Failed to close unused device. Error: {:?}", err
); );
}; }
} }
},
None => {
udev.handler.device_removed(evlh, &mut device);
drop(device);
if let Err(err) = udev.session.close(fd) {
warn!(
udev.logger,
"Failed to close unused device. Error: {:?}", err
);
}
}
};
}
}
// Device removed
EventType::Remove => {
info!(udev.logger, "Device Remove");
if let Some(devnum) = event.devnum() {
if let Some(fd_event_source) = udev.devices.borrow_mut().remove(&devnum) {
let (mut device, _) = fd_event_source.remove();
udev.handler.device_removed(evlh, &mut device);
let fd = device.as_raw_fd();
drop(device);
if let Err(err) = udev.session.close(fd) {
warn!(
udev.logger,
"Failed to close device {:?}. Error: {:?}. Ignoring",
event.sysname(),
err
);
}; };
} }
} }
} // Device removed
// New connector EventType::Remove => {
EventType::Change => { info!(self.logger, "Device Remove");
info!(udev.logger, "Device Changed"); if let Some(devnum) = event.devnum() {
if let Some(devnum) = event.devnum() { if let Some((fd_event_source, device)) =
info!(udev.logger, "Devnum: {:b}", devnum); self.devices.borrow_mut().remove(&devnum)
if let Some(fd_event_source) = udev.devices.borrow_mut().get_mut(&devnum) { {
let handler = &mut udev.handler; fd_event_source.remove();
let logger = &udev.logger; let mut device = Rc::try_unwrap(device)
fd_event_source.with_idata(evlh, move |&mut (ref mut device, _), evlh| { .unwrap_or_else(|_| unreachable!())
info!(logger, "changed successful"); .into_inner();
handler.device_changed(evlh, device); self.handler.device_removed(&mut device);
}) let fd = device.as_raw_fd();
} else { drop(device);
info!(udev.logger, "changed, but device not tracked by backend"); if let Err(err) = self.session.close(fd) {
}; warn!(
} else { self.logger,
info!(udev.logger, "changed, but no devnum"); "Failed to close device {:?}. Error: {:?}. Ignoring",
event.sysname(),
err
);
};
}
}
} }
// New connector
EventType::Change => {
info!(self.logger, "Device Changed");
if let Some(devnum) = event.devnum() {
info!(self.logger, "Devnum: {:b}", devnum);
if let Some(&(_, ref device)) = self.devices.borrow_mut().get(&devnum) {
let handler = &mut self.handler;
handler.device_changed(&mut device.borrow_mut());
} else {
info!(self.logger, "changed, but device not tracked by backend");
};
} else {
info!(self.logger, "changed, but no devnum");
}
}
_ => {}
} }
_ => {}
} }
} }
}, FdEvent::Error { error, .. } => self.handler.error(error),
error: |evlh, udev, _, err| udev.handler.error(evlh, err), }
} }
} }
@ -358,9 +376,7 @@ pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_added( fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<H>;
&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>
) -> Option<H>;
/// Called when an open device is changed. /// Called when an open device is changed.
/// ///
/// This usually indicates that some connectors did become available or were unplugged. The handler /// This usually indicates that some connectors did become available or were unplugged. The handler
@ -368,7 +384,7 @@ pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_changed(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>); fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>);
/// Called when a device was removed. /// Called when a device was removed.
/// ///
/// The device will not accept any operations anymore and its file descriptor will be closed once /// The device will not accept any operations anymore and its file descriptor will be closed once
@ -376,12 +392,12 @@ pub trait UdevHandler<H: DrmHandler<SessionFdDrmDevice> + 'static> {
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_removed(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<SessionFdDrmDevice>); fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>);
/// Called when the udev context has encountered and error. /// Called when the udev context has encountered and error.
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError); fn error(&mut self, error: IoError);
} }
/// Returns the path of the primary gpu device if any /// Returns the path of the primary gpu device if any

View File

@ -18,7 +18,7 @@ use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::time::Instant; use std::time::Instant;
use wayland_client::egl as wegl; use wayland_client::egl as wegl;
use wayland_server::{Display, EventLoopHandle}; use wayland_server::Display;
use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor, use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
MouseScrollDelta, Touch, TouchPhase, Window as WinitWindow, WindowBuilder, WindowEvent}; MouseScrollDelta, Touch, TouchPhase, Window as WinitWindow, WindowBuilder, WindowEvent};
@ -102,7 +102,8 @@ where
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding /// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
/// `WinitInputBackend`, which implements the `InputBackend` trait /// `WinitInputBackend`, which implements the `InputBackend` trait
pub fn init_from_builder<L>( pub fn init_from_builder<L>(
builder: WindowBuilder, logger: L builder: WindowBuilder,
logger: L,
) -> Result<(WinitGraphicsBackend, WinitInputBackend)> ) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
@ -124,7 +125,9 @@ where
/// `GlAttributes` for further customization of the rendering pipeline and a /// `GlAttributes` for further customization of the rendering pipeline and a
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait. /// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
pub fn init_from_builder_with_gl_attr<L>( pub fn init_from_builder_with_gl_attr<L>(
builder: WindowBuilder, attributes: GlAttributes, logger: L builder: WindowBuilder,
attributes: GlAttributes,
logger: L,
) -> Result<(WinitGraphicsBackend, WinitInputBackend)> ) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
@ -185,15 +188,15 @@ where
/// Handler trait to recieve window-related events to provide a better *nested* experience. /// Handler trait to recieve window-related events to provide a better *nested* experience.
pub trait WinitEventsHandler { pub trait WinitEventsHandler {
/// The window was resized, can be used to adjust the associated `wayland::output::Output`s mode. /// The window was resized, can be used to adjust the associated `wayland::output::Output`s mode.
fn resized(&mut self, evlh: &mut EventLoopHandle, width: u32, height: u32); fn resized(&mut self, width: u32, height: u32);
/// The window was moved /// The window was moved
fn moved(&mut self, evlh: &mut EventLoopHandle, x: i32, h: i32); fn moved(&mut self, x: i32, h: i32);
/// The window gained or lost focus /// The window gained or lost focus
fn focus_changed(&mut self, evlh: &mut EventLoopHandle, focused: bool); fn focus_changed(&mut self, focused: bool);
/// The window needs to be redrawn /// The window needs to be redrawn
fn refresh(&mut self, evlh: &mut EventLoopHandle); fn refresh(&mut self);
/// The window's hidpi factor changed /// The window's hidpi factor changed
fn hidpi_changed(&mut self, evlh: &mut EventLoopHandle, scale: f32); fn hidpi_changed(&mut self, scale: f32);
} }
impl WinitGraphicsBackend { impl WinitGraphicsBackend {
@ -213,7 +216,9 @@ impl GraphicsBackend for WinitGraphicsBackend {
} }
fn set_cursor_representation( fn set_cursor_representation(
&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32) &self,
cursor: &Self::CursorFormat,
_hotspot: (u32, u32),
) -> ::std::result::Result<(), ()> { ) -> ::std::result::Result<(), ()> {
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be // Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation"); debug!(self.logger, "Changing cursor representation");
@ -621,13 +626,13 @@ impl InputBackend for WinitInputBackend {
type TouchCancelEvent = WinitTouchCancelledEvent; type TouchCancelEvent = WinitTouchCancelledEvent;
type TouchFrameEvent = UnusedEvent; type TouchFrameEvent = UnusedEvent;
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, evlh: &mut EventLoopHandle, mut handler: H) { fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
if self.handler.is_some() { if self.handler.is_some() {
self.clear_handler(evlh); self.clear_handler();
} }
info!(self.logger, "New input handler set."); info!(self.logger, "New input handler set.");
trace!(self.logger, "Calling on_seat_created with {:?}", self.seat); trace!(self.logger, "Calling on_seat_created with {:?}", self.seat);
handler.on_seat_created(evlh, &self.seat); handler.on_seat_created(&self.seat);
self.handler = Some(Box::new(handler)); self.handler = Some(Box::new(handler));
} }
@ -637,14 +642,14 @@ impl InputBackend for WinitInputBackend {
.map(|handler| handler as &mut InputHandler<Self>) .map(|handler| handler as &mut InputHandler<Self>)
} }
fn clear_handler(&mut self, evlh: &mut EventLoopHandle) { fn clear_handler(&mut self) {
if let Some(mut handler) = self.handler.take() { if let Some(mut handler) = self.handler.take() {
trace!( trace!(
self.logger, self.logger,
"Calling on_seat_destroyed with {:?}", "Calling on_seat_destroyed with {:?}",
self.seat self.seat
); );
handler.on_seat_destroyed(evlh, &self.seat); handler.on_seat_destroyed(&self.seat);
} }
info!(self.logger, "Removing input handler"); info!(self.logger, "Removing input handler");
} }
@ -665,9 +670,7 @@ impl InputBackend for WinitInputBackend {
/// ///
/// The linked `WinitGraphicsBackend` will error with a lost Context and should /// The linked `WinitGraphicsBackend` will error with a lost Context and should
/// not be used anymore as well. /// not be used anymore as well.
fn dispatch_new_events( fn dispatch_new_events(&mut self) -> ::std::result::Result<(), WinitInputError> {
&mut self, evlh: &mut EventLoopHandle
) -> ::std::result::Result<(), WinitInputError> {
let mut closed = false; let mut closed = false;
{ {
@ -701,18 +704,16 @@ impl InputBackend for WinitInputBackend {
_ => {} _ => {}
}; };
if let Some(events_handler) = events_handler { if let Some(events_handler) = events_handler {
events_handler.resized(evlh, w, h); events_handler.resized(w, h);
} }
} }
(WindowEvent::Moved(x, y), _, Some(events_handler)) => { (WindowEvent::Moved(x, y), _, Some(events_handler)) => events_handler.moved(x, y),
events_handler.moved(evlh, x, y)
}
(WindowEvent::Focused(focus), _, Some(events_handler)) => { (WindowEvent::Focused(focus), _, Some(events_handler)) => {
events_handler.focus_changed(evlh, focus) events_handler.focus_changed(focus)
} }
(WindowEvent::Refresh, _, Some(events_handler)) => events_handler.refresh(evlh), (WindowEvent::Refresh, _, Some(events_handler)) => events_handler.refresh(),
(WindowEvent::HiDPIFactorChanged(factor), _, Some(events_handler)) => { (WindowEvent::HiDPIFactorChanged(factor), _, Some(events_handler)) => {
events_handler.hidpi_changed(evlh, factor) events_handler.hidpi_changed(factor)
} }
( (
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
@ -737,7 +738,6 @@ impl InputBackend for WinitInputBackend {
(scancode, state) (scancode, state)
); );
handler.on_keyboard_key( handler.on_keyboard_key(
evlh,
seat, seat,
WinitKeyboardInputEvent { WinitKeyboardInputEvent {
time, time,
@ -756,7 +756,6 @@ impl InputBackend for WinitInputBackend {
) => { ) => {
trace!(logger, "Calling on_pointer_move_absolute with {:?}", (x, y)); trace!(logger, "Calling on_pointer_move_absolute with {:?}", (x, y));
handler.on_pointer_move_absolute( handler.on_pointer_move_absolute(
evlh,
seat, seat,
WinitMouseMovedEvent { WinitMouseMovedEvent {
window: window.clone(), window: window.clone(),
@ -769,7 +768,7 @@ impl InputBackend for WinitInputBackend {
(WindowEvent::MouseWheel { delta, .. }, Some(handler), _) => { (WindowEvent::MouseWheel { delta, .. }, Some(handler), _) => {
let event = WinitMouseWheelEvent { time, delta }; let event = WinitMouseWheelEvent { time, delta };
trace!(logger, "Calling on_pointer_axis with {:?}", delta); trace!(logger, "Calling on_pointer_axis with {:?}", delta);
handler.on_pointer_axis(evlh, seat, event); handler.on_pointer_axis(seat, event);
} }
(WindowEvent::MouseInput { state, button, .. }, Some(handler), _) => { (WindowEvent::MouseInput { state, button, .. }, Some(handler), _) => {
trace!( trace!(
@ -778,7 +777,6 @@ impl InputBackend for WinitInputBackend {
(button, state) (button, state)
); );
handler.on_pointer_button( handler.on_pointer_button(
evlh,
seat, seat,
WinitMouseInputEvent { WinitMouseInputEvent {
time, time,
@ -799,7 +797,6 @@ impl InputBackend for WinitInputBackend {
) => { ) => {
trace!(logger, "Calling on_touch_down at {:?}", (x, y)); trace!(logger, "Calling on_touch_down at {:?}", (x, y));
handler.on_touch_down( handler.on_touch_down(
evlh,
seat, seat,
WinitTouchStartedEvent { WinitTouchStartedEvent {
window: window.clone(), window: window.clone(),
@ -821,7 +818,6 @@ impl InputBackend for WinitInputBackend {
) => { ) => {
trace!(logger, "Calling on_touch_motion at {:?}", (x, y)); trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
handler.on_touch_motion( handler.on_touch_motion(
evlh,
seat, seat,
WinitTouchMovedEvent { WinitTouchMovedEvent {
window: window.clone(), window: window.clone(),
@ -843,7 +839,6 @@ impl InputBackend for WinitInputBackend {
) => { ) => {
trace!(logger, "Calling on_touch_motion at {:?}", (x, y)); trace!(logger, "Calling on_touch_motion at {:?}", (x, y));
handler.on_touch_motion( handler.on_touch_motion(
evlh,
seat, seat,
WinitTouchMovedEvent { WinitTouchMovedEvent {
window: window.clone(), window: window.clone(),
@ -853,7 +848,7 @@ impl InputBackend for WinitInputBackend {
}, },
); );
trace!(logger, "Calling on_touch_up"); trace!(logger, "Calling on_touch_up");
handler.on_touch_up(evlh, seat, WinitTouchEndedEvent { time, id: id }); handler.on_touch_up(seat, WinitTouchEndedEvent { time, id: id });
} }
( (
WindowEvent::Touch(Touch { WindowEvent::Touch(Touch {
@ -865,7 +860,7 @@ impl InputBackend for WinitInputBackend {
_, _,
) => { ) => {
trace!(logger, "Calling on_touch_cancel"); trace!(logger, "Calling on_touch_cancel");
handler.on_touch_cancel(evlh, seat, WinitTouchCancelledEvent { time, id: id }) handler.on_touch_cancel(seat, WinitTouchCancelledEvent { time, id: id })
} }
(WindowEvent::Closed, _, _) => { (WindowEvent::Closed, _, _) => {
warn!(logger, "Window closed"); warn!(logger, "Window closed");

View File

@ -1,168 +1,188 @@
use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole, use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole, SurfaceEvent};
SurfaceUserImplementation};
use super::region::RegionData; use super::region::RegionData;
use super::tree::{Location, SurfaceData}; use super::tree::{Location, SurfaceData};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use wayland_server::{Client, EventLoopHandle, Liveness, Resource}; use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface}; use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface};
/* /*
* wl_compositor * wl_compositor
*/ */
pub(crate) fn compositor_bind<U, R, ID>( pub(crate) fn implement_compositor<U, R, Impl>(
evlh: &mut EventLoopHandle, idata: &mut SurfaceIData<U, R, ID>, _: &Client, compositor: NewResource<wl_compositor::WlCompositor>,
compositor: wl_compositor::WlCompositor, token: LoopToken,
) where log: ::slog::Logger,
U: Default + 'static, implem: Rc<RefCell<Impl>>,
R: Default + 'static, ) -> Resource<wl_compositor::WlCompositor>
ID: 'static,
{
trace!(idata.log, "Binding a new wl_compositor.");
evlh.register(
&compositor,
compositor_implementation::<U, R, ID>(),
idata.clone(),
None,
);
}
fn compositor_implementation<U, R, ID>() -> wl_compositor::Implementation<SurfaceIData<U, R, ID>>
where where
U: Default + 'static, U: Default + 'static,
R: Default + 'static, R: Default + 'static,
ID: 'static, Impl: Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent> + 'static,
{ {
wl_compositor::Implementation { let my_token = token.clone();
create_surface: |evlh, idata, _, _, surface| { compositor.implement_nonsend(
unsafe { SurfaceData::<U, R>::init(&surface) }; move |request, _compositor| match request {
evlh.register( wl_compositor::Request::CreateSurface { id } => {
&surface, trace!(log, "Creating a new wl_surface.");
surface_implementation::<U, R, ID>(), implement_surface(id, &token, log.clone(), implem.clone());
idata.clone(), }
Some(destroy_surface::<U, R>), wl_compositor::Request::CreateRegion { id } => {
); trace!(log, "Creating a new wl_region.");
implement_region(id, &token);
}
}, },
create_region: |evlh, _, _, _, region| { None::<fn(_, _)>,
unsafe { RegionData::init(&region) }; &my_token,
evlh.register(&region, region_implementation(), (), Some(destroy_region)); )
},
}
} }
/* /*
* wl_surface * wl_surface
*/ */
/// Internal implementation data of surfaces // Internal implementation data of surfaces
/// pub(crate) struct SurfaceImplem<U, R> {
/// This type is only visible as type parameter of
/// the `Global` handle you are provided.
pub struct SurfaceIData<U, R, ID> {
log: ::slog::Logger, log: ::slog::Logger,
implem: SurfaceUserImplementation<U, R, ID>, implem:
idata: Rc<RefCell<ID>>, Rc<RefCell<Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent>>>,
} }
impl<U, R, ID> SurfaceIData<U, R, ID> { impl<U, R> SurfaceImplem<U, R> {
pub(crate) fn make( fn make<Impl>(log: ::slog::Logger, implem: Rc<RefCell<Impl>>) -> SurfaceImplem<U, R>
log: ::slog::Logger, implem: SurfaceUserImplementation<U, R, ID>, idata: ID where
) -> SurfaceIData<U, R, ID> { Impl: Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent>
SurfaceIData { + 'static,
{
SurfaceImplem {
log: log, log: log,
implem: implem, implem: implem,
idata: Rc::new(RefCell::new(idata)),
} }
} }
} }
impl<U, R, ID> Clone for SurfaceIData<U, R, ID> { impl<U, R> Implementation<Resource<wl_surface::WlSurface>, wl_surface::Request> for SurfaceImplem<U, R>
fn clone(&self) -> SurfaceIData<U, R, ID> { where
SurfaceIData { U: 'static,
log: self.log.clone(), R: 'static,
implem: self.implem, {
idata: self.idata.clone(), fn receive(&mut self, req: wl_surface::Request, surface: Resource<wl_surface::WlSurface>) {
match req {
wl_surface::Request::Attach { buffer, x, y } => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| {
d.buffer = Some(buffer.map(|b| (b.clone(), (x, y))))
});
},
wl_surface::Request::Damage {
x,
y,
width,
height,
} => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| {
d.damage = Damage::Surface(Rectangle {
x,
y,
width,
height,
})
});
},
wl_surface::Request::Frame { callback } => {
let mut user_impl = self.implem.borrow_mut();
trace!(self.log, "Calling user implementation for wl_surface.frame");
user_impl.receive(
SurfaceEvent::Frame { callback },
(surface, CompositorToken::make()),
);
}
wl_surface::Request::SetOpaqueRegion { region } => unsafe {
let attributes = region.map(|r| RegionData::get_attributes(&r));
SurfaceData::<U, R>::with_data(&surface, |d| d.opaque_region = attributes);
},
wl_surface::Request::SetInputRegion { region } => unsafe {
let attributes = region.map(|r| RegionData::get_attributes(&r));
SurfaceData::<U, R>::with_data(&surface, |d| d.input_region = attributes);
},
wl_surface::Request::Commit => {
let mut user_impl = self.implem.borrow_mut();
trace!(
self.log,
"Calling user implementation for wl_surface.commit"
);
user_impl.receive(SurfaceEvent::Commit, (surface, CompositorToken::make()));
}
wl_surface::Request::SetBufferTransform { transform } => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| d.buffer_transform = transform);
},
wl_surface::Request::SetBufferScale { scale } => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| d.buffer_scale = scale);
},
wl_surface::Request::DamageBuffer {
x,
y,
width,
height,
} => unsafe {
SurfaceData::<U, R>::with_data(&surface, |d| {
d.damage = Damage::Buffer(Rectangle {
x,
y,
width,
height,
})
});
},
wl_surface::Request::Destroy => {
// All is already handled by our destructor
}
} }
} }
} }
pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>( fn implement_surface<U, R, Impl>(
) -> wl_surface::Implementation<SurfaceIData<U, R, ID>> { surface: NewResource<wl_surface::WlSurface>,
wl_surface::Implementation { token: &LoopToken,
attach: |_, _, _, surface, buffer, x, y| unsafe { log: ::slog::Logger,
SurfaceData::<U, R>::with_data(surface, |d| { implem: Rc<RefCell<Impl>>,
d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y)))) ) -> Resource<wl_surface::WlSurface>
}); where
}, U: Default + 'static,
damage: |_, _, _, surface, x, y, width, height| unsafe { R: Default + 'static,
SurfaceData::<U, R>::with_data(surface, |d| { Impl: Implementation<(Resource<wl_surface::WlSurface>, CompositorToken<U, R>), SurfaceEvent> + 'static,
d.damage = Damage::Surface(Rectangle { {
x, let surface = surface.implement_nonsend(
y, SurfaceImplem::make(log, implem),
width, Some(|surface, _| unsafe {
height, SurfaceData::<U, R>::cleanup(&surface);
}) }),
}); token,
}, );
frame: |evlh, idata, _, surface, callback| { unsafe {
let mut user_idata = idata.idata.borrow_mut(); SurfaceData::<U, R>::init(&surface);
trace!(idata.log, "Calling user callback for wl_surface.frame");
(idata.implem.frame)(
evlh,
&mut *user_idata,
surface,
callback,
CompositorToken::make(),
)
},
set_opaque_region: |_, _, _, surface, region| unsafe {
let attributes = region.map(|r| RegionData::get_attributes(r));
SurfaceData::<U, R>::with_data(surface, |d| d.opaque_region = attributes);
},
set_input_region: |_, _, _, surface, region| unsafe {
let attributes = region.map(|r| RegionData::get_attributes(r));
SurfaceData::<U, R>::with_data(surface, |d| d.input_region = attributes);
},
commit: |evlh, idata, _, surface| {
let mut user_idata = idata.idata.borrow_mut();
trace!(idata.log, "Calling user callback for wl_surface.commit");
(idata.implem.commit)(evlh, &mut *user_idata, surface, CompositorToken::make())
},
set_buffer_transform: |_, _, _, surface, transform| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| d.buffer_transform = transform);
},
set_buffer_scale: |_, _, _, surface, scale| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| d.buffer_scale = scale);
},
damage_buffer: |_, _, _, surface, x, y, width, height| unsafe {
SurfaceData::<U, R>::with_data(surface, |d| {
d.damage = Damage::Buffer(Rectangle {
x,
y,
width,
height,
})
});
},
destroy: |_, _, _, _| {},
} }
} surface
fn destroy_surface<U: 'static, R: 'static>(surface: &wl_surface::WlSurface) {
unsafe { SurfaceData::<U, R>::cleanup(surface) }
} }
/* /*
* wl_region * wl_region
*/ */
pub(crate) fn region_implementation() -> wl_region::Implementation<()> { pub(crate) struct RegionImplem;
wl_region::Implementation {
add: |_, _, _, region, x, y, width, height| { impl Implementation<Resource<wl_region::WlRegion>, wl_region::Request> for RegionImplem {
unsafe { fn receive(&mut self, request: wl_region::Request, region: Resource<wl_region::WlRegion>) {
RegionData::add_rectangle( unsafe {
region, match request {
wl_region::Request::Add {
x,
y,
width,
height,
} => RegionData::add_rectangle(
&region,
RectangleKind::Add, RectangleKind::Add,
Rectangle { Rectangle {
x, x,
@ -170,13 +190,14 @@ pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
width, width,
height, height,
}, },
) ),
}; wl_region::Request::Subtract {
}, x,
subtract: |_, _, _, region, x, y, width, height| { y,
unsafe { width,
RegionData::add_rectangle( height,
region, } => RegionData::add_rectangle(
&region,
RectangleKind::Subtract, RectangleKind::Subtract,
Rectangle { Rectangle {
x, x,
@ -184,134 +205,148 @@ pub(crate) fn region_implementation() -> wl_region::Implementation<()> {
width, width,
height, height,
}, },
) ),
}; wl_region::Request::Destroy => {
}, // all is handled by our destructor
destroy: |_, _, _, _| {}, }
}
}
} }
} }
fn destroy_region(region: &wl_region::WlRegion) { fn implement_region(
unsafe { RegionData::cleanup(region) }; region: NewResource<wl_region::WlRegion>,
token: &LoopToken,
) -> Resource<wl_region::WlRegion> {
let region = region.implement_nonsend(
RegionImplem,
Some(|region, _| unsafe { RegionData::cleanup(&region) }),
token,
);
unsafe {
RegionData::init(&region);
}
region
} }
/* /*
* wl_subcompositor * wl_subcompositor
*/ */
pub(crate) fn subcompositor_bind<U, R>( pub(crate) fn implement_subcompositor<U, R>(
evlh: &mut EventLoopHandle, _: &mut (), _: &Client, subcompositor: wl_subcompositor::WlSubcompositor subcompositor: NewResource<wl_subcompositor::WlSubcompositor>,
) where token: LoopToken,
R: RoleType + Role<SubsurfaceRole> + 'static, ) -> Resource<wl_subcompositor::WlSubcompositor>
U: 'static,
{
evlh.register(
&subcompositor,
subcompositor_implementation::<U, R>(),
(),
None,
);
}
fn subcompositor_implementation<U, R>() -> wl_subcompositor::Implementation<()>
where where
R: RoleType + Role<SubsurfaceRole> + 'static, R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static, U: 'static,
{ {
wl_subcompositor::Implementation { let my_token = token.clone();
get_subsurface: |evlh, _, _, subcompositor, subsurface, surface, parent| { subcompositor.implement_nonsend(
if let Err(()) = unsafe { SurfaceData::<U, R>::set_parent(surface, parent) } { move |request, subcompositor: Resource<_>| match request {
subcompositor.post_error( wl_subcompositor::Request::GetSubsurface {
wl_subcompositor::Error::BadSurface as u32, id,
"Surface already has a role.".into(), surface,
); parent,
return; } => {
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;
}
let subsurface = implement_subsurface::<U, R>(id, &token);
subsurface.set_user_data(Box::into_raw(Box::new(surface.clone())) as *mut ());
} }
subsurface.set_user_data(Box::into_raw(Box::new(unsafe { surface.clone_unchecked() })) as *mut _); wl_subcompositor::Request::Destroy => {}
evlh.register(
&subsurface,
subsurface_implementation::<U, R>(),
(),
Some(destroy_subsurface::<U, R>),
);
}, },
destroy: |_, _, _, _| {}, None::<fn(_, _)>,
} &my_token,
)
} }
/* /*
* wl_subsurface * wl_subsurface
*/ */
unsafe fn with_subsurface_attributes<U, R, F>(subsurface: &wl_subsurface::WlSubsurface, f: F) unsafe fn with_subsurface_attributes<U, R, F>(subsurface: &Resource<wl_subsurface::WlSubsurface>, f: F)
where where
F: FnOnce(&mut SubsurfaceRole), F: FnOnce(&mut SubsurfaceRole),
U: 'static, U: 'static,
R: RoleType + Role<SubsurfaceRole> + '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 Resource<wl_surface::WlSurface>);
SurfaceData::<U, R>::with_role_data::<SubsurfaceRole, _, _>(surface, |d| f(d)) SurfaceData::<U, R>::with_role_data::<SubsurfaceRole, _, _>(surface, |d| f(d))
.expect("The surface does not have a subsurface role while it has a wl_subsurface?!"); .expect("The surface does not have a subsurface role while it has a wl_subsurface?!");
} }
fn subsurface_implementation<U, R>() -> wl_subsurface::Implementation<()> fn implement_subsurface<U, R>(
subsurface: NewResource<wl_subsurface::WlSubsurface>,
token: &LoopToken,
) -> Resource<wl_subsurface::WlSubsurface>
where where
R: RoleType + Role<SubsurfaceRole> + 'static,
U: 'static, U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static,
{ {
wl_subsurface::Implementation { subsurface.implement_nonsend(
set_position: |_, _, _, subsurface, x, y| unsafe { |request, subsurface| unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| { match request {
attrs.x = x; wl_subsurface::Request::SetPosition { x, y } => {
attrs.y = y; with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
}); attrs.location = (x, y);
}, })
place_above: |_, _, _, subsurface, sibling| unsafe { }
let ptr = subsurface.get_user_data(); wl_subsurface::Request::PlaceAbove { sibling } => {
let surface = &*(ptr as *mut wl_surface::WlSurface); let surface = &*(subsurface.get_user_data() as *mut Resource<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) {
subsurface.post_error( subsurface.post_error(
wl_subsurface::Error::BadSurface as u32, wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(), "Provided surface is not a sibling or parent.".into(),
); )
}
}
wl_subsurface::Request::PlaceBelow { sibling } => {
let surface = &*(subsurface.get_user_data() as *mut Resource<wl_surface::WlSurface>);
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, &sibling) {
subsurface.post_error(
wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(),
)
}
}
wl_subsurface::Request::SetSync => {
with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
attrs.sync = true;
})
}
wl_subsurface::Request::SetDesync => {
with_subsurface_attributes::<U, R, _>(&subsurface, |attrs| {
attrs.sync = false;
})
}
wl_subsurface::Request::Destroy => {
// Our destructor already handles it
}
} }
}, },
place_below: |_, _, _, subsurface, sibling| unsafe { Some(|subsurface, _| unsafe {
let ptr = subsurface.get_user_data(); destroy_subsurface::<U, R>(&subsurface);
let surface = &*(ptr as *mut wl_surface::WlSurface); }),
if let Err(()) = SurfaceData::<U, R>::reorder(surface, Location::Before, sibling) { token,
subsurface.post_error( )
wl_subsurface::Error::BadSurface as u32,
"Provided surface is not a sibling or parent.".into(),
);
}
},
set_sync: |_, _, _, subsurface| unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
attrs.sync = true;
});
},
set_desync: |_, _, _, subsurface| unsafe {
with_subsurface_attributes::<U, R, _>(subsurface, |attrs| {
attrs.sync = false;
});
},
destroy: |_, _, _, _| {},
}
} }
fn destroy_subsurface<U, R>(subsurface: &wl_subsurface::WlSubsurface) unsafe fn destroy_subsurface<U, R>(subsurface: &Resource<wl_subsurface::WlSubsurface>)
where where
U: 'static, U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static, R: RoleType + Role<SubsurfaceRole> + 'static,
{ {
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 { let surface = Box::from_raw(ptr as *mut Resource<wl_surface::WlSurface>);
let surface = Box::from_raw(ptr as *mut wl_surface::WlSurface); if surface.is_alive() {
if surface.status() == Liveness::Alive { SurfaceData::<U, R>::unset_parent(&surface);
SurfaceData::<U, R>::unset_parent(&surface);
}
} }
} }

View File

@ -29,9 +29,7 @@
//! ``` //! ```
//! # 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 smithay::wayland::compositor::compositor_init;
//! use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
//! use smithay::wayland::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
@ -45,21 +43,18 @@
//! define_roles!(MyRoles); //! define_roles!(MyRoles);
//! //!
//! # fn main() { //! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display(); //! # let (mut display, event_loop) = wayland_server::Display::new();
//! // 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 = ();
//!
//! // Call the init function: //! // Call the init function:
//! let (token, _, _) = compositor_init::<MyData, MyRoles, _, _>( //! let (token, _, _) = compositor_init::<MyData, MyRoles, _, _>(
//! &mut event_loop, //! &mut display,
//! my_implementation, // instance of compositor::SurfaceUserImplementation //! event_loop.token(),
//! my_implementation_data, // whatever implementation data you need //! |request, (surface, compositor_token)| {
//! None // put a logger here //! /*
//! Your handling of the user requests. This closure can also
//! be a struct implementing the appropriate Implementation trait.
//! */
//! },
//! None // put a logger here
//! ); //! );
//! //!
//! // this `token` is what you'll use to access the surface associated data //! // this `token` is what you'll use to access the surface associated data
@ -82,20 +77,23 @@
//! 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.
use std::cell::RefCell;
use std::rc::Rc;
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 utils::Rectangle; use utils::Rectangle;
use wayland_server::{resource_is_registered, EventLoopHandle, Global}; use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, use wayland_server::commons::Implementation;
wl_subcompositor, wl_surface}; use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, wl_subcompositor};
use wayland_server::protocol::wl_surface::WlSurface;
/// 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
@ -135,7 +133,7 @@ pub struct SurfaceAttributes<U> {
/// You are free to set this field to `None` to avoid processing it several /// You are free to set this field to `None` to avoid processing it several
/// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to /// times. It'll be set to `Some(...)` if the user attaches a buffer (or NULL) to
/// the surface. /// the surface.
pub buffer: Option<Option<(wl_buffer::WlBuffer, (i32, i32))>>, pub buffer: Option<Option<(Resource<wl_buffer::WlBuffer>, (i32, i32))>>,
/// Scale of the contents of the buffer, for higher-resolution contents. /// Scale of the contents of the buffer, for higher-resolution contents.
/// ///
/// If it matches the one of the output displaying this surface, no change /// If it matches the one of the output displaying this surface, no change
@ -182,12 +180,9 @@ impl<U: Default> Default for SurfaceAttributes<U> {
/// Attributes defining the behaviour of a sub-surface relative to its parent /// Attributes defining the behaviour of a sub-surface relative to its parent
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct SubsurfaceRole { pub struct SubsurfaceRole {
/// Horizontal location of the top-left corner of this sub-surface relative to /// Location of the top-left corner of this sub-surface relative to
/// the top-left corner of its parent /// the top-left corner of its parent
pub x: i32, pub location: (i32, i32),
/// Vertical location of the top-left corner of this sub-surface relative to
/// the top-left corner of its parent
pub y: i32,
/// Sync status of this sub-surface /// Sync status of this sub-surface
/// ///
/// If `true`, this surface should be repainted synchronously with its parent /// If `true`, this surface should be repainted synchronously with its parent
@ -199,8 +194,7 @@ pub struct SubsurfaceRole {
impl Default for SubsurfaceRole { impl Default for SubsurfaceRole {
fn default() -> SubsurfaceRole { fn default() -> SubsurfaceRole {
SubsurfaceRole { SubsurfaceRole {
x: 0, location: (0, 0),
y: 0,
sync: true, sync: true,
} }
} }
@ -240,58 +234,52 @@ 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, ID> { pub struct CompositorToken<U, R> {
_data: ::std::marker::PhantomData<*mut U>, _data: ::std::marker::PhantomData<*mut U>,
_role: ::std::marker::PhantomData<*mut R>, _role: ::std::marker::PhantomData<*mut R>,
_idata: ::std::marker::PhantomData<*mut ID>,
} }
// we implement them manually because #[derive(..)] would require // we implement them manually because #[derive(..)] would require
// U: Clone and R: Clone // U: Clone and R: Clone
impl<U, R, ID> Copy for CompositorToken<U, R, ID> {} impl<U, R> Copy for CompositorToken<U, R> {}
impl<U, R, ID> Clone for CompositorToken<U, R, ID> { impl<U, R> Clone for CompositorToken<U, R> {
fn clone(&self) -> CompositorToken<U, R, ID> { fn clone(&self) -> CompositorToken<U, R> {
*self *self
} }
} }
impl<U, R, ID> CompositorToken<U, R, ID> { impl<U, R> CompositorToken<U, R> {
pub(crate) fn make() -> CompositorToken<U, R, ID> { pub(crate) fn make() -> CompositorToken<U, R> {
CompositorToken { CompositorToken {
_data: ::std::marker::PhantomData, _data: ::std::marker::PhantomData,
_role: ::std::marker::PhantomData, _role: ::std::marker::PhantomData,
_idata: ::std::marker::PhantomData,
} }
} }
} }
impl<U: 'static, R: 'static, ID: 'static> CompositorToken<U, R, ID> { impl<U: 'static, R: 'static> CompositorToken<U, R> {
/// 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.
/// ///
/// 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 with_surface_data<F, T>(&self, surface: &wl_surface::WlSurface, f: F) -> T pub fn with_surface_data<F, T>(&self, surface: &Resource<WlSurface>, f: F) -> T
where where
F: FnOnce(&mut SurfaceAttributes<U>) -> T, F: FnOnce(&mut SurfaceAttributes<U>) -> T,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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, ID> CompositorToken<U, R, ID> impl<U, R> CompositorToken<U, R>
where where
U: 'static, U: 'static,
R: RoleType + Role<SubsurfaceRole> + 'static, R: RoleType + Role<SubsurfaceRole> + 'static,
ID: 'static,
{ {
/// Access the data of a surface tree from bottom to top /// Access the data of a surface tree from bottom to top
/// ///
@ -304,22 +292,22 @@ where
/// - The surface object itself /// - The surface object itself
/// - a mutable reference to its surface attribute data /// - a mutable reference to its surface attribute data
/// - a mutable reference to its role data, /// - a mutable reference to its role data,
/// - a custom value that is passer in a fold-like maneer, but only from the output of a parent /// - a custom value that is passed in a fold-like maneer, but only from the output of a parent
/// to its children. See `TraversalAction` for details. /// to its children. See `TraversalAction` for details.
/// ///
/// If the surface not managed by the CompositorGlobal that provided this token, this /// If the surface 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 with_surface_tree_upward<F, T>( pub fn with_surface_tree_upward<F, T>(
&self, surface: &wl_surface::WlSurface, initial: T, f: F &self,
surface: &Resource<WlSurface>,
initial: T,
f: F,
) -> Result<(), ()> ) -> Result<(), ()>
where where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>, F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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 {
@ -336,16 +324,16 @@ where
/// ///
/// Behavior is the same as `with_surface_tree_upward`. /// Behavior is the same as `with_surface_tree_upward`.
pub fn with_surface_tree_downward<F, T>( pub fn with_surface_tree_downward<F, T>(
&self, surface: &wl_surface::WlSurface, initial: T, f: F &self,
surface: &Resource<WlSurface>,
initial: T,
f: F,
) -> Result<(), ()> ) -> Result<(), ()>
where where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>, F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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 {
@ -360,12 +348,9 @@ where
/// ///
/// 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 get_parent(&self, surface: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> { pub fn get_parent(&self, surface: &Resource<WlSurface>) -> Option<Resource<WlSurface>> {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -375,29 +360,23 @@ where
/// ///
/// 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 get_children(&self, surface: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> { pub fn get_children(&self, surface: &Resource<WlSurface>) -> Vec<Resource<WlSurface>> {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> { impl<U: 'static, R: RoleType + 'static> CompositorToken<U, R> {
/// 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: &Resource<WlSurface>) -> bool {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -407,15 +386,12 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// ///
/// 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_role<RoleData>(&self, surface: &wl_surface::WlSurface) -> bool pub fn has_role<RoleData>(&self, surface: &Resource<WlSurface>) -> bool
where where
R: Role<RoleData>, R: Role<RoleData>,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -427,16 +403,13 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// ///
/// 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 give_role<RoleData>(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> pub fn give_role<RoleData>(&self, surface: &Resource<WlSurface>) -> Result<(), ()>
where where
R: Role<RoleData>, R: Role<RoleData>,
RoleData: Default, RoleData: Default,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -449,16 +422,15 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// 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 give_role_with<RoleData>( pub fn give_role_with<RoleData>(
&self, surface: &wl_surface::WlSurface, data: RoleData &self,
surface: &Resource<WlSurface>,
data: RoleData,
) -> Result<(), RoleData> ) -> Result<(), RoleData>
where where
R: Role<RoleData>, R: Role<RoleData>,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -470,18 +442,13 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// ///
/// 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 with_role_data<RoleData, F, T>( pub fn with_role_data<RoleData, F, T>(&self, surface: &Resource<WlSurface>, f: F) -> Result<T, WrongRole>
&self, surface: &wl_surface::WlSurface, f: F
) -> Result<T, WrongRole>
where where
R: Role<RoleData>, R: Role<RoleData>,
F: FnOnce(&mut RoleData) -> T, F: FnOnce(&mut RoleData) -> T,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -493,15 +460,12 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// ///
/// 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 remove_role<RoleData>(&self, surface: &wl_surface::WlSurface) -> Result<RoleData, WrongRole> pub fn remove_role<RoleData>(&self, surface: &Resource<WlSurface>) -> Result<RoleData, WrongRole>
where where
R: Role<RoleData>, R: Role<RoleData>,
{ {
assert!( assert!(
resource_is_registered( surface.is_implemented_with::<self::handlers::SurfaceImplem<U, R>>(),
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) }
@ -511,10 +475,10 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// ///
/// If the region is not managed by the CompositorGlobal that provided this token, this /// If the region 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 get_region_attributes(&self, region: &wl_region::WlRegion) -> RegionAttributes { pub fn get_region_attributes(&self, region: &Resource<wl_region::WlRegion>) -> RegionAttributes {
assert!( assert!(
resource_is_registered(region, &self::handlers::region_implementation()), region.is_implemented_with::<self::handlers::RegionImplem>(),
"Accessing the data of foreign surfaces is not supported." "Accessing the data of foreign regions is not supported."
); );
unsafe { RegionData::get_attributes(region) } unsafe { RegionData::get_attributes(region) }
} }
@ -528,46 +492,50 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
/// ///
/// It also returns the two global handles, in case you whish to remove these /// It also returns the two global handles, in case you whish to remove these
/// globals from the event loop in the future. /// globals from the event loop in the future.
pub fn compositor_init<U, R, ID, L>( pub fn compositor_init<U, R, Impl, L>(
evlh: &mut EventLoopHandle, implem: SurfaceUserImplementation<U, R, ID>, idata: ID, logger: L display: &mut Display,
token: LoopToken,
implem: Impl,
logger: L,
) -> ( ) -> (
CompositorToken<U, R, ID>, CompositorToken<U, R>,
Global<wl_compositor::WlCompositor, self::handlers::SurfaceIData<U, R, ID>>, Global<wl_compositor::WlCompositor>,
Global<wl_subcompositor::WlSubcompositor, ()>, Global<wl_subcompositor::WlSubcompositor>,
) )
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
U: Default + 'static, U: Default + 'static,
R: Default + RoleType + Role<SubsurfaceRole> + 'static, R: Default + RoleType + Role<SubsurfaceRole> + 'static,
ID: 'static, Impl: Implementation<(Resource<WlSurface>, CompositorToken<U, R>), SurfaceEvent> + 'static,
{ {
let log = ::slog_or_stdlog(logger); let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "compositor_handler"));
let idata = self::handlers::SurfaceIData::make( let implem = Rc::new(RefCell::new(implem));
log.new(o!("smithay_module" => "compositor_handler")),
implem,
idata,
);
let compositor_global_token =
evlh.register_global::<wl_compositor::WlCompositor, _>(4, self::handlers::compositor_bind, idata);
let subcompositor_global_token = evlh.register_global::<wl_subcompositor::WlSubcompositor, _>(
1,
self::handlers::subcompositor_bind::<U, R>,
(),
);
( let comp_token = token.clone();
CompositorToken::make(), let sub_token = token.clone();
compositor_global_token,
subcompositor_global_token, let compositor = display.create_global(&token, 4, move |_version, new_compositor| {
) self::handlers::implement_compositor::<U, R, Impl>(
new_compositor,
comp_token.clone(),
log.clone(),
implem.clone(),
);
});
let subcompositor = display.create_global(&token, 1, move |_version, new_subcompositor| {
self::handlers::implement_subcompositor::<U, R>(new_subcompositor, sub_token.clone());
});
(CompositorToken::make(), compositor, subcompositor)
} }
/// Sub-implementation for surface event handling /// User-handled events for surfaces
/// ///
/// 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
/// are forwarded directly to this implementation that you must provide /// they are forwarded directly via your provided implementation, and are
/// at creation of the compositor global. /// described by this global.
pub struct SurfaceUserImplementation<U, R, ID> { pub enum SurfaceEvent {
/// 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
@ -575,12 +543,7 @@ pub struct SurfaceUserImplementation<U, R, ID> {
/// ///
/// 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) /// 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
pub commit: fn( Commit,
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
@ -588,18 +551,8 @@ pub struct SurfaceUserImplementation<U, R, ID> {
/// ///
/// 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) /// 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
pub frame: fn( Frame {
evlh: &mut EventLoopHandle, /// The created `WlCallback`
idata: &mut ID, callback: NewResource<wl_callback::WlCallback>,
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

@ -10,29 +10,33 @@ 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: &Resource<wl_region::WlRegion>) {
region.set_user_data(Box::into_raw(Box::new(Mutex::new(RegionData::default()))) as *mut _) region.set_user_data(Box::into_raw(Box::new(Mutex::new(RegionData::default()))) 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
pub unsafe fn cleanup(region: &wl_region::WlRegion) { pub unsafe fn cleanup(region: &Resource<wl_region::WlRegion>) {
let ptr = region.get_user_data(); let ptr = region.get_user_data();
region.set_user_data(::std::ptr::null_mut()); region.set_user_data(::std::ptr::null_mut());
let _my_data_mutex: Box<Mutex<RegionData>> = Box::from_raw(ptr as *mut _); let _my_data_mutex: Box<Mutex<RegionData>> = Box::from_raw(ptr as *mut _);
} }
unsafe fn get_data(region: &wl_region::WlRegion) -> &Mutex<RegionData> { unsafe fn get_data(region: &Resource<wl_region::WlRegion>) -> &Mutex<RegionData> {
let ptr = region.get_user_data(); let ptr = region.get_user_data();
&*(ptr as *mut _) &*(ptr as *mut _)
} }
pub unsafe fn get_attributes(region: &wl_region::WlRegion) -> RegionAttributes { pub unsafe fn get_attributes(region: &Resource<wl_region::WlRegion>) -> RegionAttributes {
let data_mutex = Self::get_data(region); let data_mutex = Self::get_data(region);
let data_guard = data_mutex.lock().unwrap(); let data_guard = data_mutex.lock().unwrap();
data_guard.attributes.clone() data_guard.attributes.clone()
} }
pub unsafe fn add_rectangle(region: &wl_region::WlRegion, kind: RectangleKind, rect: Rectangle) { pub unsafe fn add_rectangle(
region: &Resource<wl_region::WlRegion>,
kind: RectangleKind,
rect: Rectangle,
) {
let data_mutex = Self::get_data(region); let data_mutex = Self::get_data(region);
let mut data_guard = data_mutex.lock().unwrap(); let mut data_guard = data_mutex.lock().unwrap();
data_guard.attributes.rects.push((kind, rect)); data_guard.attributes.rects.push((kind, rect));

View File

@ -206,7 +206,11 @@ macro_rules! define_roles(
} }
} }
fn data(&self) -> ::std::result::Result<&$role_data, $crate::wayland::compositor::roles::WrongRole> { fn data(&self) -> ::std::result::Result<
&$role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
if let $enum_name::$role_name(ref data) = *self { if let $enum_name::$role_name(ref data) = *self {
Ok(data) Ok(data)
} else { } else {
@ -214,7 +218,11 @@ macro_rules! define_roles(
} }
} }
fn data_mut(&mut self) -> ::std::result::Result<&mut $role_data, $crate::wayland::compositor::roles::WrongRole> { fn data_mut(&mut self) -> ::std::result::Result<
&mut $role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
if let $enum_name::$role_name(ref mut data) = *self { if let $enum_name::$role_name(ref mut data) = *self {
Ok(data) Ok(data)
} else { } else {
@ -222,7 +230,11 @@ macro_rules! define_roles(
} }
} }
fn unset(&mut self) -> ::std::result::Result<$role_data, $crate::wayland::compositor::roles::WrongRole> { fn unset(&mut self) -> ::std::result::Result<
$role_data,
$crate::wayland::compositor::roles::WrongRole
>
{
// remove self to make borrow checker happy // remove self to make borrow checker happy
let temp = ::std::mem::replace(self, $enum_name::NoRole); let temp = ::std::mem::replace(self, $enum_name::NoRole);
if let $enum_name::$role_name(data) = temp { if let $enum_name::$role_name(data) = temp {

View File

@ -1,8 +1,8 @@
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::Resource;
use wayland_server::protocol::wl_surface; use wayland_server::protocol::wl_surface::WlSurface;
/// Node of a subsurface tree, holding some user specified data type U /// Node of a subsurface tree, holding some user specified data type U
/// at each node /// at each node
@ -24,8 +24,8 @@ use wayland_server::protocol::wl_surface;
/// All the methods here are unsafe, because they assume the provided `wl_surface` object /// All the methods here are unsafe, because they assume the provided `wl_surface` object
/// is correctly initialized regarding its `user_data`. /// is correctly initialized regarding its `user_data`.
pub struct SurfaceData<U, R> { pub struct SurfaceData<U, R> {
parent: Option<wl_surface::WlSurface>, parent: Option<Resource<WlSurface>>,
children: Vec<wl_surface::WlSurface>, children: Vec<Resource<WlSurface>>,
role: R, role: R,
attributes: SurfaceAttributes<U>, attributes: SurfaceAttributes<U>,
} }
@ -56,7 +56,7 @@ impl<U: Default, R: Default> SurfaceData<U, R> {
} }
/// Initialize the user_data of a surface, must be called right when the surface is created /// Initialize the user_data of a surface, must be called right when the surface is created
pub unsafe fn init(surface: &wl_surface::WlSurface) { pub unsafe fn init(surface: &Resource<WlSurface>) {
surface.set_user_data(Box::into_raw(Box::new(Mutex::new(SurfaceData::<U, R>::new()))) as *mut _) surface.set_user_data(Box::into_raw(Box::new(Mutex::new(SurfaceData::<U, R>::new()))) as *mut _)
} }
} }
@ -66,13 +66,13 @@ where
U: 'static, U: 'static,
R: 'static, R: 'static,
{ {
unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex<SurfaceData<U, R>> { unsafe fn get_data(surface: &Resource<WlSurface>) -> &Mutex<SurfaceData<U, R>> {
let ptr = surface.get_user_data(); let ptr = surface.get_user_data();
&*(ptr as *mut _) &*(ptr 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
pub unsafe fn cleanup(surface: &wl_surface::WlSurface) { pub unsafe fn cleanup(surface: &Resource<WlSurface>) {
let ptr = surface.get_user_data(); let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut()); surface.set_user_data(::std::ptr::null_mut());
let my_data_mutex: Box<Mutex<SurfaceData<U, R>>> = Box::from_raw(ptr as *mut _); let my_data_mutex: Box<Mutex<SurfaceData<U, R>>> = Box::from_raw(ptr as *mut _);
@ -99,31 +99,31 @@ where
} }
impl<U: 'static, R: RoleType + 'static> 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: &Resource<WlSurface>) -> bool {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let data_guard = data_mutex.lock().unwrap(); let data_guard = data_mutex.lock().unwrap();
<R as RoleType>::has_role(&data_guard.role) <R as RoleType>::has_role(&data_guard.role)
} }
/// Check wether a surface has a given role /// Check wether a surface has a given role
pub unsafe fn has_role<RoleData>(surface: &wl_surface::WlSurface) -> bool pub unsafe fn has_role<RoleData>(surface: &Resource<WlSurface>) -> bool
where where
R: Role<RoleData>, R: Role<RoleData>,
{ {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let data_guard = data_mutex.lock().unwrap(); let data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::has(&data_guard.role) <R as Role<RoleData>>::has(&data_guard.role)
} }
/// Register that this surface has a role, fails if it already has one /// Register that this surface has a role, fails if it already has one
pub unsafe fn give_role<RoleData>(surface: &wl_surface::WlSurface) -> Result<(), ()> pub unsafe fn give_role<RoleData>(surface: &Resource<WlSurface>) -> Result<(), ()>
where where
R: Role<RoleData>, R: Role<RoleData>,
RoleData: Default, RoleData: Default,
{ {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap(); let mut data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::set(&mut data_guard.role) <R as Role<RoleData>>::set(&mut data_guard.role)
@ -133,12 +133,13 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
/// ///
/// Fails if it already has one and returns the data /// Fails if it already has one and returns the data
pub unsafe fn give_role_with<RoleData>( pub unsafe fn give_role_with<RoleData>(
surface: &wl_surface::WlSurface, data: RoleData surface: &Resource<WlSurface>,
data: RoleData,
) -> Result<(), RoleData> ) -> Result<(), RoleData>
where where
R: Role<RoleData>, R: Role<RoleData>,
{ {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap(); let mut data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::set_with(&mut data_guard.role, data) <R as Role<RoleData>>::set_with(&mut data_guard.role, data)
@ -148,25 +149,23 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
/// ///
/// It is a noop if this surface already didn't have one, but fails if /// It is a noop if this surface already didn't have one, but fails if
/// the role was "subsurface", it must be removed by the `unset_parent` method. /// the role was "subsurface", it must be removed by the `unset_parent` method.
pub unsafe fn remove_role<RoleData>(surface: &wl_surface::WlSurface) -> Result<RoleData, WrongRole> pub unsafe fn remove_role<RoleData>(surface: &Resource<WlSurface>) -> Result<RoleData, WrongRole>
where where
R: Role<RoleData>, R: Role<RoleData>,
{ {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap(); let mut data_guard = data_mutex.lock().unwrap();
<R as Role<RoleData>>::unset(&mut data_guard.role) <R as Role<RoleData>>::unset(&mut data_guard.role)
} }
/// Access to the role data /// Access to the role data
pub unsafe fn with_role_data<RoleData, F, T>( pub unsafe fn with_role_data<RoleData, F, T>(surface: &Resource<WlSurface>, f: F) -> Result<T, WrongRole>
surface: &wl_surface::WlSurface, f: F
) -> Result<T, WrongRole>
where where
R: Role<RoleData>, R: Role<RoleData>,
F: FnOnce(&mut RoleData) -> T, F: FnOnce(&mut RoleData) -> T,
{ {
debug_assert!(surface.status() == Liveness::Alive); debug_assert!(surface.is_alive());
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let mut data_guard = data_mutex.lock().unwrap(); let mut data_guard = data_mutex.lock().unwrap();
let data = <R as Role<RoleData>>::data_mut(&mut data_guard.role)?; let data = <R as Role<RoleData>>::data_mut(&mut data_guard.role)?;
@ -179,11 +178,9 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
/// ///
/// if this surface already has a role, does nothing and fails, otherwise /// if this surface already has a role, does nothing and fails, otherwise
/// its role is now to be a subsurface /// its role is now to be a subsurface
pub unsafe fn set_parent( pub unsafe fn set_parent(child: &Resource<WlSurface>, parent: &Resource<WlSurface>) -> Result<(), ()> {
child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface debug_assert!(child.is_alive());
) -> Result<(), ()> { debug_assert!(parent.is_alive());
debug_assert!(child.status() == Liveness::Alive);
debug_assert!(parent.status() == Liveness::Alive);
// change child's parent // change child's parent
{ {
@ -192,14 +189,14 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
// if surface already has a role, it cannot become a subsurface // if surface already has a role, it cannot become a subsurface
<R as Role<SubsurfaceRole>>::set(&mut child_guard.role)?; <R as Role<SubsurfaceRole>>::set(&mut child_guard.role)?;
debug_assert!(child_guard.parent.is_none()); debug_assert!(child_guard.parent.is_none());
child_guard.parent = Some(parent.clone_unchecked()); child_guard.parent = Some(parent.clone());
} }
// register child to new parent // register child to new parent
// double scoping is to be robust to have a child be its own parent // double scoping is to be robust to have a child be its own parent
{ {
let parent_mutex = Self::get_data(parent); let parent_mutex = Self::get_data(parent);
let mut parent_guard = parent_mutex.lock().unwrap(); let mut parent_guard = parent_mutex.lock().unwrap();
parent_guard.children.push(child.clone_unchecked()) parent_guard.children.push(child.clone())
} }
Ok(()) Ok(())
} }
@ -207,8 +204,8 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
/// Remove a pre-existing parent of this child /// Remove a pre-existing parent of this child
/// ///
/// Does nothing if it has no parent /// Does nothing if it has no parent
pub unsafe fn unset_parent(child: &wl_surface::WlSurface) { pub unsafe fn unset_parent(child: &Resource<WlSurface>) {
debug_assert!(child.status() == Liveness::Alive); debug_assert!(child.is_alive());
let old_parent = { let old_parent = {
let child_mutex = Self::get_data(child); let child_mutex = Self::get_data(child);
let mut child_guard = child_mutex.lock().unwrap(); let mut child_guard = child_mutex.lock().unwrap();
@ -229,44 +226,38 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
} }
/// Retrieve the parent surface (if any) of this surface /// Retrieve the parent surface (if any) of this surface
pub unsafe fn get_parent(child: &wl_surface::WlSurface) -> Option<wl_surface::WlSurface> { pub unsafe fn get_parent(child: &Resource<WlSurface>) -> Option<Resource<WlSurface>> {
let child_mutex = Self::get_data(child); let child_mutex = Self::get_data(child);
let child_guard = child_mutex.lock().unwrap(); let child_guard = child_mutex.lock().unwrap();
child_guard.parent.as_ref().map(|p| p.clone_unchecked()) child_guard.parent.as_ref().map(|p| p.clone())
} }
/// Retrieve the parent surface (if any) of this surface /// Retrieve the parent surface (if any) of this surface
pub unsafe fn get_children(child: &wl_surface::WlSurface) -> Vec<wl_surface::WlSurface> { pub unsafe fn get_children(child: &Resource<WlSurface>) -> Vec<Resource<WlSurface>> {
let child_mutex = Self::get_data(child); let child_mutex = Self::get_data(child);
let child_guard = child_mutex.lock().unwrap(); let child_guard = child_mutex.lock().unwrap();
child_guard child_guard.children.iter().map(|p| p.clone()).collect()
.children
.iter()
.map(|p| p.clone_unchecked())
.collect()
} }
/// Reorders a surface relative to one of its sibling /// Reorders a surface relative to one of its sibling
/// ///
/// Fails if `relative_to` is not a sibling or parent of `surface`. /// Fails if `relative_to` is not a sibling or parent of `surface`.
pub unsafe fn reorder( pub unsafe fn reorder(
surface: &wl_surface::WlSurface, to: Location, relative_to: &wl_surface::WlSurface surface: &Resource<WlSurface>,
to: Location,
relative_to: &Resource<WlSurface>,
) -> Result<(), ()> { ) -> Result<(), ()> {
let parent = { let parent = {
let data_mutex = Self::get_data(surface); let data_mutex = Self::get_data(surface);
let data_guard = data_mutex.lock().unwrap(); let data_guard = data_mutex.lock().unwrap();
data_guard data_guard.parent.as_ref().map(|p| p.clone()).unwrap()
.parent
.as_ref()
.map(|p| p.clone_unchecked())
.unwrap()
}; };
if parent.equals(relative_to) { if parent.equals(relative_to) {
// TODO: handle positioning relative to parent // TODO: handle positioning relative to parent
return Ok(()); return Ok(());
} }
fn index_of(surface: &wl_surface::WlSurface, slice: &[wl_surface::WlSurface]) -> Option<usize> { fn index_of(surface: &Resource<WlSurface>, slice: &[Resource<WlSurface>]) -> Option<usize> {
for (i, s) in slice.iter().enumerate() { for (i, s) in slice.iter().enumerate() {
if s.equals(surface) { if s.equals(surface) {
return Some(i); return Some(i);
@ -300,7 +291,7 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
/// ///
/// Note that an internal lock is taken during access of this data, /// Note that an internal lock is taken during access of this data,
/// so the tree cannot be manipulated at the same time /// so the tree cannot be manipulated at the same time
pub unsafe fn with_data<T, F>(surface: &wl_surface::WlSurface, f: F) -> T pub unsafe fn with_data<T, F>(surface: &Resource<WlSurface>, f: F) -> T
where where
F: FnOnce(&mut SurfaceAttributes<U>) -> T, F: FnOnce(&mut SurfaceAttributes<U>) -> T,
{ {
@ -317,17 +308,20 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
/// ///
/// The callback returns wether the traversal should continue or not. Returning /// The callback returns wether the traversal should continue or not. Returning
/// false will cause an early-stopping. /// false will cause an early-stopping.
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool) pub unsafe fn map_tree<F, T>(root: &Resource<WlSurface>, initial: T, mut f: F, reverse: bool)
where where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>, F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{ {
// helper function for recursion // helper function for recursion
unsafe fn map<U: 'static, R: 'static, F, T>( unsafe fn map<U: 'static, R: 'static, F, T>(
surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, f: &mut F, surface: &Resource<WlSurface>,
root: &Resource<WlSurface>,
initial: &T,
f: &mut F,
reverse: bool, reverse: bool,
) -> bool ) -> bool
where where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>, F: FnMut(&Resource<WlSurface>, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
{ {
// stop if we met the root, so to not deadlock/inifinte loop // stop if we met the root, so to not deadlock/inifinte loop
if surface.equals(root) { if surface.equals(root) {

View File

@ -10,9 +10,9 @@
//! You need to instanciate an `Output` for each output global you want //! You need to instanciate an `Output` for each output global you want
//! to advertize to clients. //! to advertize to clients.
//! //!
//! Just insert it in your event loop using the `Output::new(..)` method. //! Just add it to your Display using the `Output::new(..)` method.
//! It returns a state token that gives you access to the `Output` in order //! You can use the returned `Output` to change the properties of your
//! to change it if needed (if the current resolution mode changes for example), //! output (if the current resolution mode changes for example),
//! it'll automatically forward any changes to the clients. //! it'll automatically forward any changes to the clients.
//! //!
//! ``` //! ```
@ -22,10 +22,11 @@
//! use wayland_server::protocol::wl_output; //! use wayland_server::protocol::wl_output;
//! //!
//! # fn main() { //! # fn main() {
//! # let (display, mut event_loop) = wayland_server::create_display(); //! # let (mut display, event_loop) = wayland_server::Display::new();
//! // Insert the Output with given name and physical properties //! // Create the Output with given name and physical properties
//! let (output_state_token, _output_global) = Output::new( //! let (output, _output_global) = Output::new(
//! &mut event_loop, // the event loop //! &mut display, // the display
//! event_loop.token(), // the LoopToken
//! "output-0".into(), // the name of this output, //! "output-0".into(), // the name of this output,
//! PhysicalProperties { //! PhysicalProperties {
//! width: 200, // width in mm //! width: 200, // width in mm
@ -37,33 +38,33 @@
//! None // insert a logger here //! None // insert a logger here
//! ); //! );
//! // Now you can configure it //! // Now you can configure it
//! { //! output.change_current_state(
//! let output = event_loop.state().get_mut(&output_state_token); //! Some(Mode { width: 1902, height: 1080, refresh: 60000 }), // the resolution mode,
//! // set the current state //! Some(wl_output::Transform::Normal), // global screen transformation
//! output.change_current_state( //! Some(1), // global screen scaling factor
//! Some(Mode { width: 1902, height: 1080, refresh: 60000 }), // the resolution mode, //! );
//! Some(wl_output::Transform::Normal), // global screen transformation //! // set the prefered mode
//! Some(1), // global screen scaling factor //! output.set_preferred(Mode { width: 1920, height: 1080, refresh: 60000 });
//! ); //! // add other supported modes
//! // set the prefered mode //! output.add_mode(Mode { width: 800, height: 600, refresh: 60000 });
//! output.set_preferred(Mode { width: 1920, height: 1080, refresh: 60000 }); //! output.add_mode(Mode { width: 1024, height: 768, refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { width: 800, height: 600, refresh: 60000 });
//! output.add_mode(Mode { width: 1024, height: 768, refresh: 60000 });
//! }
//! # } //! # }
//! ``` //! ```
use wayland_server::{Client, EventLoopHandle, Global, Liveness, Resource, StateToken}; use std::sync::{Arc, Mutex};
use wayland_server::protocol::wl_output;
use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::commons::{downcast_impl, Implementation};
use wayland_server::protocol::wl_output::{Event, Mode as WMode, Request, WlOutput};
pub use wayland_server::protocol::wl_output::{Subpixel, Transform};
#[derive(Copy, Clone, PartialEq)]
/// An output mode /// An output mode
/// ///
/// A possible combination of dimensions and refresh rate for an output. /// A possible combination of dimensions and refresh rate for an output.
/// ///
/// This should only describe the characteristics of the video driver, /// This should only describe the characteristics of the video driver,
/// not taking into account any global scaling. /// not taking into account any global scaling.
#[derive(Copy, Clone, PartialEq)]
pub struct Mode { pub struct Mode {
/// The width in pixels /// The width in pixels
pub width: i32, pub width: i32,
@ -82,68 +83,28 @@ pub struct PhysicalProperties {
/// The height in milimeters /// The height in milimeters
pub height: i32, pub height: i32,
/// The subpixel geometry /// The subpixel geometry
pub subpixel: wl_output::Subpixel, pub subpixel: Subpixel,
/// Textual representation of the manufacturer /// Textual representation of the manufacturer
pub maker: String, pub maker: String,
/// Textual representation of the model /// Textual representation of the model
pub model: String, pub model: String,
} }
/// An output as seen by the clients struct Inner {
///
/// This handle is stored in the events loop, and allows you to notify clients
/// about any change in the properties of this output.
pub struct Output {
name: String, name: String,
log: ::slog::Logger, log: ::slog::Logger,
instances: Vec<wl_output::WlOutput>, instances: Vec<Resource<WlOutput>>,
physical: PhysicalProperties, physical: PhysicalProperties,
location: (i32, i32), location: (i32, i32),
transform: wl_output::Transform, transform: Transform,
scale: i32, scale: i32,
modes: Vec<Mode>, modes: Vec<Mode>,
current_mode: Option<Mode>, current_mode: Option<Mode>,
preferred_mode: Option<Mode>, preferred_mode: Option<Mode>,
} }
impl Output { impl Inner {
/// Create a new output global with given name and physical properties fn new_global(&mut self, output: Resource<WlOutput>) {
///
/// The global is directly registered into the eventloop, and this function
/// returns the state token allowing you to access it, as well as the global handle,
/// in case you whish to remove this global in the future.
pub fn new<L>(
evlh: &mut EventLoopHandle, name: String, physical: PhysicalProperties, logger: L
) -> (
StateToken<Output>,
Global<wl_output::WlOutput, StateToken<Output>>,
)
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "output_handler"));
info!(log, "Creating new wl_output"; "name" => &name);
let token = evlh.state().insert(Output {
name: name,
log: log,
instances: Vec::new(),
physical: physical,
location: (0, 0),
transform: wl_output::Transform::Normal,
scale: 1,
modes: Vec::new(),
current_mode: None,
preferred_mode: None,
});
let global = evlh.register_global(3, output_bind, token.clone());
(token, global)
}
fn new_global(&mut self, output: wl_output::WlOutput) {
trace!(self.log, "New global instanciated."); trace!(self.log, "New global instanciated.");
if self.modes.is_empty() { if self.modes.is_empty() {
@ -158,51 +119,140 @@ impl Output {
self.send_geometry(&output); self.send_geometry(&output);
for &mode in &self.modes { for &mode in &self.modes {
let mut flags = wl_output::Mode::empty(); let mut flags = WMode::empty();
if Some(mode) == self.current_mode { if Some(mode) == self.current_mode {
flags |= wl_output::Mode::Current; flags |= WMode::Current;
} }
if Some(mode) == self.preferred_mode { if Some(mode) == self.preferred_mode {
flags |= wl_output::Mode::Preferred; flags |= WMode::Preferred;
} }
output.mode(flags, mode.width, mode.height, mode.refresh); output.send(Event::Mode {
flags: flags,
width: mode.width,
height: mode.height,
refresh: mode.refresh,
});
} }
if output.version() >= 2 { if output.version() >= 2 {
output.scale(self.scale); output.send(Event::Scale { factor: self.scale });
output.done(); output.send(Event::Done);
} }
self.instances.push(output); self.instances.push(output);
} }
fn send_geometry(&self, output: &wl_output::WlOutput) { fn send_geometry(&self, output: &Resource<WlOutput>) {
output.geometry( output.send(Event::Geometry {
self.location.0, x: self.location.0,
self.location.1, y: self.location.1,
self.physical.width, physical_width: self.physical.width,
self.physical.height, physical_height: self.physical.height,
self.physical.subpixel, subpixel: self.physical.subpixel,
self.physical.maker.clone(), make: self.physical.maker.clone(),
self.physical.model.clone(), model: self.physical.model.clone(),
self.transform, transform: self.transform,
); });
}
}
struct InnerWrapper {
inner: Arc<Mutex<Inner>>,
}
// This implementation does nothing, we just use it as a stable type to downcast the
// implementation in the destructor of wl_output, in order to retrieve the Arc to the
// inner and remove this output from the list
impl Implementation<Resource<WlOutput>, Request> for InnerWrapper {
fn receive(&mut self, req: Request, _res: Resource<WlOutput>) {
// this will break if new variants are added :)
let Request::Release = req;
}
}
/// An output as seen by the clients
///
/// This handle is stored in the events loop, and allows you to notify clients
/// about any change in the properties of this output.
pub struct Output {
inner: Arc<Mutex<Inner>>,
}
impl Output {
/// Create a new output global with given name and physical properties
///
/// The global is directly registered into the eventloop, and this function
/// returns the state token allowing you to access it, as well as the global handle,
/// in case you whish to remove this global in the future.
pub fn new<L>(
display: &mut Display,
token: LoopToken,
name: String,
physical: PhysicalProperties,
logger: L,
) -> (Output, Global<WlOutput>)
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "output_handler"));
info!(log, "Creating new wl_output"; "name" => &name);
let inner = Arc::new(Mutex::new(Inner {
name: name,
log: log,
instances: Vec::new(),
physical: physical,
location: (0, 0),
transform: Transform::Normal,
scale: 1,
modes: Vec::new(),
current_mode: None,
preferred_mode: None,
}));
let output = Output {
inner: inner.clone(),
};
let global = display.create_global(&token, 3, move |_version, new_output: NewResource<_>| {
let output = new_output.implement(
InnerWrapper {
inner: inner.clone(),
},
Some(|output, boxed_impl| {
let wrapper: Box<InnerWrapper> =
downcast_impl(boxed_impl).unwrap_or_else(|_| unreachable!());
wrapper
.inner
.lock()
.unwrap()
.instances
.retain(|o| !o.equals(&output));
}),
);
inner.lock().unwrap().new_global(output);
});
(output, global)
} }
/// Sets the preferred mode of this output /// Sets the preferred mode of this output
/// ///
/// If the provided mode was not previously known to this output, it is added to its /// If the provided mode was not previously known to this output, it is added to its
/// internal list. /// internal list.
pub fn set_preferred(&mut self, mode: Mode) { pub fn set_preferred(&self, mode: Mode) {
self.preferred_mode = Some(mode); let mut inner = self.inner.lock().unwrap();
if self.modes.iter().find(|&m| *m == mode).is_none() { inner.preferred_mode = Some(mode);
self.modes.push(mode); if inner.modes.iter().find(|&m| *m == mode).is_none() {
inner.modes.push(mode);
} }
} }
/// Adds a mode to the list of known modes to this output /// Adds a mode to the list of known modes to this output
pub fn add_mode(&mut self, mode: Mode) { pub fn add_mode(&self, mode: Mode) {
if self.modes.iter().find(|&m| *m == mode).is_none() { let mut inner = self.inner.lock().unwrap();
self.modes.push(mode); if inner.modes.iter().find(|&m| *m == mode).is_none() {
inner.modes.push(mode);
} }
} }
@ -210,13 +260,14 @@ impl Output {
/// ///
/// It will not de-advertize it from existing clients (the protocol does not /// It will not de-advertize it from existing clients (the protocol does not
/// allow it), but it won't be advertized to now clients from now on. /// allow it), but it won't be advertized to now clients from now on.
pub fn delete_mode(&mut self, mode: Mode) { pub fn delete_mode(&self, mode: Mode) {
self.modes.retain(|&m| m != mode); let mut inner = self.inner.lock().unwrap();
if self.current_mode == Some(mode) { inner.modes.retain(|&m| m != mode);
self.current_mode = None; if inner.current_mode == Some(mode) {
inner.current_mode = None;
} }
if self.preferred_mode == Some(mode) { if inner.preferred_mode == Some(mode) {
self.preferred_mode = None; inner.preferred_mode = None;
} }
} }
@ -230,75 +281,58 @@ impl Output {
/// ///
/// By default, transform status is `Normal`, and scale is `1`. /// By default, transform status is `Normal`, and scale is `1`.
pub fn change_current_state( pub fn change_current_state(
&mut self, new_mode: Option<Mode>, new_transform: Option<wl_output::Transform>, &self,
new_mode: Option<Mode>,
new_transform: Option<Transform>,
new_scale: Option<i32>, new_scale: Option<i32>,
) { ) {
let mut inner = self.inner.lock().unwrap();
if let Some(mode) = new_mode { if let Some(mode) = new_mode {
if self.modes.iter().find(|&m| *m == mode).is_none() { if inner.modes.iter().find(|&m| *m == mode).is_none() {
self.modes.push(mode); inner.modes.push(mode);
} }
self.current_mode = new_mode; inner.current_mode = new_mode;
} }
if let Some(transform) = new_transform { if let Some(transform) = new_transform {
self.transform = transform; inner.transform = transform;
} }
if let Some(scale) = new_scale { if let Some(scale) = new_scale {
self.scale = scale; inner.scale = scale;
} }
let mut flags = wl_output::Mode::Current; let mut flags = WMode::Current;
if self.preferred_mode == new_mode { if inner.preferred_mode == new_mode {
flags |= wl_output::Mode::Preferred; flags |= WMode::Preferred;
} }
for output in &self.instances { for output in &inner.instances {
if let Some(mode) = new_mode { if let Some(mode) = new_mode {
output.mode(flags, mode.width, mode.height, mode.refresh); output.send(Event::Mode {
flags: flags,
width: mode.width,
height: mode.height,
refresh: mode.refresh,
});
} }
if new_transform.is_some() { if new_transform.is_some() {
self.send_geometry(output); inner.send_geometry(output);
} }
if let Some(scale) = new_scale { if let Some(scale) = new_scale {
if output.version() >= 2 { if output.version() >= 2 {
output.scale(scale); output.send(Event::Scale { factor: scale });
} }
} }
if output.version() >= 2 { if output.version() >= 2 {
output.done(); output.send(Event::Done);
} }
} }
} }
/// Chech is given wl_output instance is managed by this `Output`. /// Chech is given wl_output instance is managed by this `Output`.
pub fn owns(&self, output: &wl_output::WlOutput) -> bool { pub fn owns(&self, output: &Resource<WlOutput>) -> bool {
self.instances.iter().any(|o| o.equals(output)) self.inner
} .lock()
.unwrap()
/// Cleanup internal `wl_output` instances list .instances
/// .iter()
/// Clients do not necessarily notify the server on the destruction .any(|o| o.equals(output))
/// of their `wl_output` instances. This can lead to accumulation of
/// stale values in the internal instances list. This methods delete
/// them.
///
/// It can be good to call this regularly (but not necessarily very often).
pub fn cleanup(&mut self) {
self.instances.retain(|o| o.status() == Liveness::Alive);
}
}
fn output_bind(
evlh: &mut EventLoopHandle, token: &mut StateToken<Output>, _: &Client, global: wl_output::WlOutput
) {
evlh.register(&global, output_implementation(), token.clone(), None);
evlh.state().get_mut(token).new_global(global);
}
fn output_implementation() -> wl_output::Implementation<StateToken<Output>> {
wl_output::Implementation {
release: |evlh, token, _, output| {
evlh.state()
.get_mut(token)
.instances
.retain(|o| !o.equals(output));
},
} }
} }

View File

@ -3,8 +3,9 @@ use std::io::{Error as IoError, Write};
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tempfile::tempfile; use tempfile::tempfile;
use wayland_server::{Liveness, Resource}; use wayland_server::{NewResource, Resource};
use wayland_server::protocol::{wl_keyboard, wl_surface}; use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::protocol::wl_keyboard::{Event, KeyState as WlKeyState, KeymapFormat, Request, WlKeyboard};
use xkbcommon::xkb; use xkbcommon::xkb;
pub use xkbcommon::xkb::{keysyms, Keysym}; pub use xkbcommon::xkb::{keysyms, Keysym};
@ -55,8 +56,8 @@ impl ModifiersState {
} }
struct KbdInternal { struct KbdInternal {
known_kbds: Vec<wl_keyboard::WlKeyboard>, known_kbds: Vec<Resource<WlKeyboard>>,
focus: Option<wl_surface::WlSurface>, focus: Option<Resource<WlSurface>>,
pressed_keys: Vec<u32>, pressed_keys: Vec<u32>,
mods_state: ModifiersState, mods_state: ModifiersState,
keymap: xkb::Keymap, keymap: xkb::Keymap,
@ -65,9 +66,18 @@ struct KbdInternal {
repeat_delay: i32, repeat_delay: i32,
} }
// This is OK because all parts of `xkb` will remain on the
// same thread
unsafe impl Send for KbdInternal {}
impl KbdInternal { impl KbdInternal {
fn new( fn new(
rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_rate: i32, rules: &str,
model: &str,
layout: &str,
variant: &str,
options: Option<String>,
repeat_rate: i32,
repeat_delay: i32, repeat_delay: i32,
) -> Result<KbdInternal, ()> { ) -> Result<KbdInternal, ()> {
// we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe // we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe
@ -147,7 +157,7 @@ impl KbdInternal {
fn with_focused_kbds<F>(&self, mut f: F) fn with_focused_kbds<F>(&self, mut f: F)
where where
F: FnMut(&wl_keyboard::WlKeyboard, &wl_surface::WlSurface), F: FnMut(&Resource<WlKeyboard>, &Resource<WlSurface>),
{ {
if let Some(ref surface) = self.focus { if let Some(ref surface) = self.focus {
for kbd in &self.known_kbds { for kbd in &self.known_kbds {
@ -170,8 +180,14 @@ pub enum Error {
/// Create a keyboard handler from a set of RMLVO rules /// Create a keyboard handler from a set of RMLVO rules
pub(crate) fn create_keyboard_handler( pub(crate) fn create_keyboard_handler(
rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_delay: i32, rules: &str,
repeat_rate: i32, logger: &::slog::Logger, model: &str,
layout: &str,
variant: &str,
options: Option<String>,
repeat_delay: i32,
repeat_rate: i32,
logger: &::slog::Logger,
) -> Result<KeyboardHandle, Error> { ) -> Result<KeyboardHandle, Error> {
let log = logger.new(o!("smithay_module" => "xkbcommon_handler")); let log = logger.new(o!("smithay_module" => "xkbcommon_handler"));
info!(log, "Initializing a xkbcommon handler with keymap query"; info!(log, "Initializing a xkbcommon handler with keymap query";
@ -252,7 +268,7 @@ impl KeyboardHandle {
/// ///
/// The module `smithay::keyboard::keysyms` exposes definitions of all possible keysyms /// The module `smithay::keyboard::keysyms` exposes definitions of all possible keysyms
/// to be compared against. This includes non-characted keysyms, such as XF86 special keys. /// to be compared against. This includes non-characted keysyms, such as XF86 special keys.
pub fn input<F>(&self, keycode: u32, state: KeyState, serial: u32, filter: F) pub fn input<F>(&self, keycode: u32, state: KeyState, serial: u32, time: u32, filter: F)
where where
F: FnOnce(&ModifiersState, Keysym) -> bool, F: FnOnce(&ModifiersState, Keysym) -> bool,
{ {
@ -282,14 +298,25 @@ impl KeyboardHandle {
None None
}; };
let wl_state = match state { let wl_state = match state {
KeyState::Pressed => wl_keyboard::KeyState::Pressed, KeyState::Pressed => WlKeyState::Pressed,
KeyState::Released => wl_keyboard::KeyState::Released, KeyState::Released => WlKeyState::Released,
}; };
guard.with_focused_kbds(|kbd, _| { guard.with_focused_kbds(|kbd, _| {
if let Some((dep, la, lo, gr)) = modifiers { if let Some((dep, la, lo, gr)) = modifiers {
kbd.modifiers(serial, dep, la, lo, gr); kbd.send(Event::Modifiers {
serial,
mods_depressed: dep,
mods_latched: la,
mods_locked: lo,
group: gr,
});
} }
kbd.key(serial, 0, keycode, wl_state); kbd.send(Event::Key {
serial,
time,
key: keycode,
state: wl_state,
});
}); });
if guard.focus.is_some() { if guard.focus.is_some() {
trace!(self.arc.logger, "Input forwarded to client"); trace!(self.arc.logger, "Input forwarded to client");
@ -303,7 +330,7 @@ impl KeyboardHandle {
/// If the ne focus is different from the previous one, any previous focus /// If the ne focus is different from the previous one, any previous focus
/// will be sent a `wl_keyboard::leave` event, and if the new focus is not `None`, /// will be sent a `wl_keyboard::leave` event, and if the new focus is not `None`,
/// a `wl_keyboard::enter` event will be sent. /// a `wl_keyboard::enter` event will be sent.
pub fn set_focus(&self, focus: Option<&wl_surface::WlSurface>, serial: u32) { pub fn set_focus(&self, focus: Option<&Resource<WlSurface>>, serial: u32) {
let mut guard = self.arc.internal.lock().unwrap(); let mut guard = self.arc.internal.lock().unwrap();
let same = guard let same = guard
@ -315,16 +342,29 @@ impl KeyboardHandle {
if !same { if !same {
// unset old focus // unset old focus
guard.with_focused_kbds(|kbd, s| { guard.with_focused_kbds(|kbd, s| {
kbd.leave(serial, s); kbd.send(Event::Leave {
serial,
surface: s.clone(),
});
}); });
// set new focus // set new focus
guard.focus = focus.and_then(|s| s.clone()); guard.focus = focus.map(|s| s.clone());
let (dep, la, lo, gr) = guard.serialize_modifiers(); let (dep, la, lo, gr) = guard.serialize_modifiers();
let keys = guard.serialize_pressed_keys(); let keys = guard.serialize_pressed_keys();
guard.with_focused_kbds(|kbd, s| { guard.with_focused_kbds(|kbd, surface| {
kbd.modifiers(serial, dep, la, lo, gr); kbd.send(Event::Modifiers {
kbd.enter(serial, s, keys.clone()); serial,
mods_depressed: dep,
mods_latched: la,
mods_locked: lo,
group: gr,
});
kbd.send(Event::Enter {
serial,
surface: surface.clone(),
keys: keys.clone(),
});
}); });
if guard.focus.is_some() { if guard.focus.is_some() {
trace!(self.arc.logger, "Focus set to new surface"); trace!(self.arc.logger, "Focus set to new surface");
@ -341,16 +381,19 @@ impl KeyboardHandle {
/// The keymap will automatically be sent to it /// The keymap will automatically be sent to it
/// ///
/// This should be done first, before anything else is done with this keyboard. /// This should be done first, before anything else is done with this keyboard.
pub(crate) fn new_kbd(&self, kbd: wl_keyboard::WlKeyboard) { pub(crate) fn new_kbd(&self, kbd: Resource<WlKeyboard>) {
trace!(self.arc.logger, "Sending keymap to client"); trace!(self.arc.logger, "Sending keymap to client");
kbd.keymap( kbd.send(Event::Keymap {
wl_keyboard::KeymapFormat::XkbV1, format: KeymapFormat::XkbV1,
self.arc.keymap_file.as_raw_fd(), fd: self.arc.keymap_file.as_raw_fd(),
self.arc.keymap_len, size: self.arc.keymap_len,
); });
let mut guard = self.arc.internal.lock().unwrap(); let mut guard = self.arc.internal.lock().unwrap();
if kbd.version() >= 4 { if kbd.version() >= 4 {
kbd.repeat_info(guard.repeat_rate, guard.repeat_delay); kbd.send(Event::RepeatInfo {
rate: guard.repeat_rate,
delay: guard.repeat_delay,
});
} }
guard.known_kbds.push(kbd); guard.known_kbds.push(kbd);
} }
@ -361,17 +404,36 @@ impl KeyboardHandle {
guard.repeat_delay = delay; guard.repeat_delay = delay;
guard.repeat_rate = rate; guard.repeat_rate = rate;
for kbd in &guard.known_kbds { for kbd in &guard.known_kbds {
kbd.repeat_info(rate, delay); kbd.send(Event::RepeatInfo { rate, delay });
} }
} }
}
/// Performs an internal cleanup of known kbds
/// pub(crate) fn implement_keyboard(
/// Drops any wl_keyboard that is no longer alive new_keyboard: NewResource<WlKeyboard>,
pub(crate) fn cleanup_old_kbds(&self) { handle: Option<&KeyboardHandle>,
let mut guard = self.arc.internal.lock().unwrap(); ) -> Resource<WlKeyboard> {
guard let destructor = match handle {
.known_kbds Some(h) => {
.retain(|kbd| kbd.status() != Liveness::Dead); let arc = h.arc.clone();
} Some(move |keyboard: Resource<_>, _| {
arc.internal
.lock()
.unwrap()
.known_kbds
.retain(|k| !k.equals(&keyboard))
})
}
None => None,
};
new_keyboard.implement(
|request, _keyboard| {
match request {
Request::Release => {
// Our destructors already handle it
}
}
},
destructor,
)
} }

View File

@ -10,14 +10,14 @@
//! ``` //! ```
//! # extern crate wayland_server; //! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay; //! # #[macro_use] extern crate smithay;
//!
//! use smithay::wayland::seat::Seat; //! use smithay::wayland::seat::Seat;
//! //!
//! # fn main(){ //! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display(); //! # let (mut display, event_loop) = wayland_server::Display::new();
//! // insert the seat: //! // insert the seat:
//! let (seat_state_token, seat_global) = Seat::new( //! let (seat, seat_global) = Seat::new(
//! &mut event_loop, //! &mut display, // the display
//! event_loop.token(), // a LoopToken
//! "seat-0".into(), // the name of the seat, will be advertize to clients //! "seat-0".into(), // the name of the seat, will be advertize to clients
//! None /* insert a logger here*/ //! None /* insert a logger here*/
//! ); //! );
@ -31,8 +31,7 @@
//! Currently, only pointer and keyboard capabilities are supported by //! Currently, only pointer and keyboard capabilities are supported by
//! smithay. //! smithay.
//! //!
//! You can add these capabilities via methods of the `Seat` struct that was //! You can add these capabilities via methods of the `Seat` struct:
//! inserted in the event loop, that you can retreive via its token:
//! //!
//! ``` //! ```
//! # extern crate wayland_server; //! # extern crate wayland_server;
@ -41,28 +40,59 @@
//! # use smithay::wayland::seat::Seat; //! # use smithay::wayland::seat::Seat;
//! # //! #
//! # fn main(){ //! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display(); //! # let (mut display, event_loop) = wayland_server::Display::new();
//! # let (seat_state_token, seat_global) = Seat::new( //! # let (mut seat, seat_global) = Seat::new(
//! # &mut event_loop, //! # &mut display,
//! # event_loop.token(),
//! # "seat-0".into(), // the name of the seat, will be advertize to clients //! # "seat-0".into(), // the name of the seat, will be advertize to clients
//! # None /* insert a logger here*/ //! # None /* insert a logger here*/
//! # ); //! # );
//! let pointer_handle = event_loop.state().get_mut(&seat_state_token).add_pointer(); //! let pointer_handle = seat.add_pointer();
//! # } //! # }
//! ``` //! ```
//! //!
//! These handles can be cloned and sent accross thread, so you can keep one around //! These handles can be cloned and sent accross thread, so you can keep one around
//! in your event-handling code to forward inputs to your clients. //! in your event-handling code to forward inputs to your clients.
use std::sync::{Arc, Mutex};
mod keyboard; mod keyboard;
mod pointer; mod pointer;
pub use self::keyboard::{Error as KeyboardError, KeyboardHandle, ModifiersState}; pub use self::keyboard::{Error as KeyboardError, KeyboardHandle, ModifiersState};
pub use self::pointer::{PointerAxisHandle, PointerHandle}; pub use self::pointer::{PointerAxisHandle, PointerHandle};
use wayland_server::{Client, EventLoopHandle, Global, Liveness, Resource, StateToken}; use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat}; use wayland_server::protocol::wl_seat;
/// Internal data of a seat global struct Inner {
log: ::slog::Logger,
name: String,
pointer: Option<PointerHandle>,
keyboard: Option<KeyboardHandle>,
known_seats: Vec<Resource<wl_seat::WlSeat>>,
}
impl Inner {
fn compute_caps(&self) -> wl_seat::Capability {
let mut caps = wl_seat::Capability::empty();
if self.pointer.is_some() {
caps |= wl_seat::Capability::Pointer;
}
if self.keyboard.is_some() {
caps |= wl_seat::Capability::Keyboard;
}
caps
}
fn send_all_caps(&self) {
let capabilities = self.compute_caps();
for seat in &self.known_seats {
seat.send(wl_seat::Event::Capabilities { capabilities });
}
}
}
/// A Seat handle
/// ///
/// This struct gives you access to the control of the /// This struct gives you access to the control of the
/// capabilities of the associated seat. /// capabilities of the associated seat.
@ -71,11 +101,7 @@ use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat};
/// ///
/// See module-level documentation for details of use. /// See module-level documentation for details of use.
pub struct Seat { pub struct Seat {
log: ::slog::Logger, inner: Arc<Mutex<Inner>>,
name: String,
pointer: Option<PointerHandle>,
keyboard: Option<KeyboardHandle>,
known_seats: Vec<wl_seat::WlSeat>,
} }
impl Seat { impl Seat {
@ -88,22 +114,39 @@ impl Seat {
/// you to add or remove capabilities from it), and the global handle, /// you to add or remove capabilities from it), and the global handle,
/// in case you want to remove it. /// in case you want to remove it.
pub fn new<L>( pub fn new<L>(
evlh: &mut EventLoopHandle, name: String, logger: L display: &mut Display,
) -> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>) token: LoopToken,
name: String,
logger: L,
) -> (Seat, Global<wl_seat::WlSeat>)
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
let log = ::slog_or_stdlog(logger); let log = ::slog_or_stdlog(logger);
let seat = Seat { let inner = Arc::new(Mutex::new(Inner {
log: log.new(o!("smithay_module" => "seat_handler", "seat_name" => name.clone())), log: log.new(o!("smithay_module" => "seat_handler", "seat_name" => name.clone())),
name: name, name: name,
pointer: None, pointer: None,
keyboard: None, keyboard: None,
known_seats: Vec::new(), known_seats: Vec::new(),
}));
let seat = Seat {
inner: inner.clone(),
}; };
let token = evlh.state().insert(seat); let global = display.create_global(&token, 5, move |_version, new_seat| {
let global = evlh.register_global(5, seat_global_bind, token.clone()); let seat = implement_seat(new_seat, inner.clone());
(token, global) let mut inner = inner.lock().unwrap();
if seat.version() >= 2 {
seat.send(wl_seat::Event::Name {
name: inner.name.clone(),
});
}
seat.send(wl_seat::Event::Capabilities {
capabilities: inner.compute_caps(),
});
inner.known_seats.push(seat);
});
(seat, global)
} }
/// Adds the pointer capability to this seat /// Adds the pointer capability to this seat
@ -115,21 +158,16 @@ impl Seat {
/// will overwrite it, and will be seen by the clients as if the /// will overwrite it, and will be seen by the clients as if the
/// mouse was unplugged and a new one was plugged. /// mouse was unplugged and a new one was plugged.
pub fn add_pointer(&mut self) -> PointerHandle { pub fn add_pointer(&mut self) -> PointerHandle {
let mut inner = self.inner.lock().unwrap();
let pointer = self::pointer::create_pointer_handler(); let pointer = self::pointer::create_pointer_handler();
if self.pointer.is_some() { if inner.pointer.is_some() {
// there is already a pointer, remove it and notify the clients // there is already a pointer, remove it and notify the clients
// of the change // of the change
self.pointer = None; inner.pointer = None;
let caps = self.compute_caps(); inner.send_all_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
}
}
self.pointer = Some(pointer.clone());
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
} }
inner.pointer = Some(pointer.clone());
inner.send_all_caps();
pointer pointer
} }
@ -137,12 +175,10 @@ impl Seat {
/// ///
/// Clients will be appropriately notified. /// Clients will be appropriately notified.
pub fn remove_pointer(&mut self) { pub fn remove_pointer(&mut self) {
if self.pointer.is_some() { let mut inner = self.inner.lock().unwrap();
self.pointer = None; if inner.pointer.is_some() {
let caps = self.compute_caps(); inner.pointer = None;
for seat in &self.known_seats { inner.send_all_caps();
seat.capabilities(caps);
}
} }
} }
@ -159,9 +195,15 @@ impl Seat {
/// will overwrite it, and will be seen by the clients as if the /// will overwrite it, and will be seen by the clients as if the
/// keyboard was unplugged and a new one was plugged. /// keyboard was unplugged and a new one was plugged.
pub fn add_keyboard( pub fn add_keyboard(
&mut self, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_delay: i32, &mut self,
model: &str,
layout: &str,
variant: &str,
options: Option<String>,
repeat_delay: i32,
repeat_rate: i32, repeat_rate: i32,
) -> Result<KeyboardHandle, KeyboardError> { ) -> Result<KeyboardHandle, KeyboardError> {
let mut inner = self.inner.lock().unwrap();
let keyboard = self::keyboard::create_keyboard_handler( let keyboard = self::keyboard::create_keyboard_handler(
"evdev", // we need this one "evdev", // we need this one
model, model,
@ -170,22 +212,16 @@ impl Seat {
options, options,
repeat_delay, repeat_delay,
repeat_rate, repeat_rate,
&self.log, &inner.log,
)?; )?;
if self.keyboard.is_some() { if inner.keyboard.is_some() {
// there is already a keyboard, remove it and notify the clients // there is already a keyboard, remove it and notify the clients
// of the change // of the change
self.keyboard = None; inner.keyboard = None;
let caps = self.compute_caps(); inner.send_all_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
}
}
self.keyboard = Some(keyboard.clone());
let caps = self.compute_caps();
for seat in &self.known_seats {
seat.capabilities(caps);
} }
inner.keyboard = Some(keyboard.clone());
inner.send_all_caps();
Ok(keyboard) Ok(keyboard)
} }
@ -193,95 +229,60 @@ impl Seat {
/// ///
/// Clients will be appropriately notified. /// Clients will be appropriately notified.
pub fn remove_keyboard(&mut self) { pub fn remove_keyboard(&mut self) {
if self.keyboard.is_some() { let mut inner = self.inner.lock().unwrap();
self.keyboard = None; if inner.keyboard.is_some() {
let caps = self.compute_caps(); inner.keyboard = None;
for seat in &self.known_seats { inner.send_all_caps();
seat.capabilities(caps);
}
} }
} }
/// Checks wether a given `WlSeat` is associated with this `Seat` /// Checks wether a given `WlSeat` is associated with this `Seat`
pub fn owns(&self, seat: &wl_seat::WlSeat) -> bool { pub fn owns(&self, seat: &Resource<wl_seat::WlSeat>) -> bool {
self.known_seats.iter().any(|s| s.equals(seat)) let inner = self.inner.lock().unwrap();
} inner.known_seats.iter().any(|s| s.equals(seat))
/// Cleanup internal states from old resources
///
/// Deletes all remnnant of ressources from clients that
/// are now disconnected.
///
/// It can be wise to run this from time to time.
pub fn cleanup(&mut self) {
if let Some(ref pointer) = self.pointer {
pointer.cleanup_old_pointers();
}
if let Some(ref kbd) = self.keyboard {
kbd.cleanup_old_kbds();
}
self.known_seats.retain(|s| s.status() == Liveness::Alive);
}
fn compute_caps(&self) -> wl_seat::Capability {
let mut caps = wl_seat::Capability::empty();
if self.pointer.is_some() {
caps |= wl_seat::Capability::Pointer;
}
if self.keyboard.is_some() {
caps |= wl_seat::Capability::Keyboard;
}
caps
} }
} }
fn seat_global_bind( fn implement_seat(
evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client, seat: wl_seat::WlSeat new_seat: NewResource<wl_seat::WlSeat>,
) { inner: Arc<Mutex<Inner>>,
evlh.register(&seat, seat_implementation(), token.clone(), None); ) -> Resource<wl_seat::WlSeat> {
let seat_mgr = evlh.state().get_mut(token); let dest_inner = inner.clone();
if seat.version() >= 2 { new_seat.implement(
seat.name(seat_mgr.name.clone()); move |request, _seat| {
} let inner = inner.lock().unwrap();
seat.capabilities(seat_mgr.compute_caps()); match request {
seat_mgr.known_seats.push(seat); wl_seat::Request::GetPointer { id } => {
} let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref());
if let Some(ref ptr_handle) = inner.pointer {
fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> { ptr_handle.new_pointer(pointer);
wl_seat::Implementation { } else {
get_pointer: |evlh, token, _, _, pointer| { // we should send a protocol error... but the protocol does not allow
evlh.register(&pointer, pointer_implementation(), (), None); // us, so this pointer will just remain inactive ¯\_(ツ)_/¯
if let Some(ref ptr_handle) = evlh.state().get(token).pointer { }
ptr_handle.new_pointer(pointer); }
} else { wl_seat::Request::GetKeyboard { id } => {
// we should send a protocol error... but the protocol does not allow let keyboard = self::keyboard::implement_keyboard(id, inner.keyboard.as_ref());
// us, so this pointer will just remain inactive ¯\_(ツ)_/¯ if let Some(ref kbd_handle) = inner.keyboard {
kbd_handle.new_kbd(keyboard);
} else {
// same as pointer, should error but cannot
}
}
wl_seat::Request::GetTouch { id: _ } => {
// TODO
}
wl_seat::Request::Release => {
// Our destructors already handle it
}
} }
}, },
get_keyboard: |evlh, token, _, _, keyboard| { Some(move |seat, _| {
evlh.register(&keyboard, keyboard_implementation(), (), None); dest_inner
if let Some(ref kbd_handle) = evlh.state().get(token).keyboard { .lock()
kbd_handle.new_kbd(keyboard); .unwrap()
} else { .known_seats
// same, should error but cant .retain(|s| !s.equals(&seat));
} }),
}, )
get_touch: |_evlh, _token, _, _, _touch| {
// TODO
},
release: |_, _, _, _| {},
}
}
fn pointer_implementation() -> wl_pointer::Implementation<()> {
wl_pointer::Implementation {
set_cursor: |_, _, _, _, _, _, _, _| {},
release: |_, _, _, _| {},
}
}
fn keyboard_implementation() -> wl_keyboard::Implementation<()> {
wl_keyboard::Implementation {
release: |_, _, _, _| {},
}
} }

View File

@ -1,12 +1,13 @@
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
use wayland_server::{Liveness, Resource}; use wayland_server::{NewResource, Resource};
use wayland_server::protocol::{wl_pointer, wl_surface}; use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::protocol::wl_pointer::{Axis, AxisSource, ButtonState, Event, Request, WlPointer};
// TODO: handle pointer surface role // TODO: handle pointer surface role
struct PointerInternal { struct PointerInternal {
known_pointers: Vec<wl_pointer::WlPointer>, known_pointers: Vec<Resource<WlPointer>>,
focus: Option<wl_surface::WlSurface>, focus: Option<Resource<WlSurface>>,
} }
impl PointerInternal { impl PointerInternal {
@ -19,7 +20,7 @@ impl PointerInternal {
fn with_focused_pointers<F>(&self, mut f: F) fn with_focused_pointers<F>(&self, mut f: F)
where where
F: FnMut(&wl_pointer::WlPointer, &wl_surface::WlSurface), F: FnMut(&Resource<WlPointer>, &Resource<WlSurface>),
{ {
if let Some(ref focus) = self.focus { if let Some(ref focus) = self.focus {
for ptr in &self.known_pointers { for ptr in &self.known_pointers {
@ -44,7 +45,7 @@ pub struct PointerHandle {
} }
impl PointerHandle { impl PointerHandle {
pub(crate) fn new_pointer(&self, pointer: wl_pointer::WlPointer) { pub(crate) fn new_pointer(&self, pointer: Resource<WlPointer>) {
let mut guard = self.inner.lock().unwrap(); let mut guard = self.inner.lock().unwrap();
guard.known_pointers.push(pointer); guard.known_pointers.push(pointer);
} }
@ -59,7 +60,7 @@ impl PointerHandle {
/// ///
/// This will internally take care of notifying the appropriate client objects /// This will internally take care of notifying the appropriate client objects
/// of enter/motion/leave events. /// of enter/motion/leave events.
pub fn motion(&self, location: Option<(&wl_surface::WlSurface, f64, f64)>, serial: u32, time: u32) { pub fn motion(&self, location: Option<(&Resource<WlSurface>, f64, f64)>, serial: u32, time: u32) {
let mut guard = self.inner.lock().unwrap(); let mut guard = self.inner.lock().unwrap();
// do we leave a surface ? // do we leave a surface ?
let mut leave = true; let mut leave = true;
@ -72,9 +73,12 @@ impl PointerHandle {
} }
if leave { if leave {
guard.with_focused_pointers(|pointer, surface| { guard.with_focused_pointers(|pointer, surface| {
pointer.leave(serial, surface); pointer.send(Event::Leave {
serial,
surface: surface.clone(),
});
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.frame(); pointer.send(Event::Frame);
} }
}); });
guard.focus = None; guard.focus = None;
@ -83,19 +87,28 @@ impl PointerHandle {
// do we enter one ? // do we enter one ?
if let Some((surface, x, y)) = location { if let Some((surface, x, y)) = location {
if guard.focus.is_none() { if guard.focus.is_none() {
guard.focus = surface.clone(); guard.focus = Some(surface.clone());
guard.with_focused_pointers(|pointer, surface| { guard.with_focused_pointers(|pointer, surface| {
pointer.enter(serial, surface, x, y); pointer.send(Event::Enter {
serial,
surface: surface.clone(),
surface_x: x,
surface_y: y,
});
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.frame(); pointer.send(Event::Frame);
} }
}) })
} else { } else {
// we were on top of a surface and remained on it // we were on top of a surface and remained on it
guard.with_focused_pointers(|pointer, _| { guard.with_focused_pointers(|pointer, _| {
pointer.motion(time, x, y); pointer.send(Event::Motion {
time,
surface_x: x,
surface_y: y,
});
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.frame(); pointer.send(Event::Frame);
} }
}) })
} }
@ -106,12 +119,17 @@ impl PointerHandle {
/// ///
/// This will internally send the appropriate button event to the client /// This will internally send the appropriate button event to the client
/// objects matching with the currently focused surface. /// objects matching with the currently focused surface.
pub fn button(&self, button: u32, state: wl_pointer::ButtonState, serial: u32, time: u32) { pub fn button(&self, button: u32, state: ButtonState, serial: u32, time: u32) {
let guard = self.inner.lock().unwrap(); let guard = self.inner.lock().unwrap();
guard.with_focused_pointers(|pointer, _| { guard.with_focused_pointers(|pointer, _| {
pointer.button(serial, time, button, state); pointer.send(Event::Button {
serial,
time,
button,
state,
});
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.frame(); pointer.send(Event::Frame);
} }
}) })
} }
@ -125,18 +143,12 @@ impl PointerHandle {
inner: self.inner.lock().unwrap(), inner: self.inner.lock().unwrap(),
} }
} }
pub(crate) fn cleanup_old_pointers(&self) {
let mut guard = self.inner.lock().unwrap();
guard
.known_pointers
.retain(|p| p.status() != Liveness::Dead);
}
} }
/// A frame of pointer axis events. /// A frame of pointer axis events.
/// ///
/// Can be used with the builder pattern, e.g.: /// Can be used with the builder pattern, e.g.:
///
/// ```ignore /// ```ignore
/// pointer.axis() /// pointer.axis()
/// .source(AxisSource::Wheel) /// .source(AxisSource::Wheel)
@ -156,10 +168,12 @@ impl<'a> PointerAxisHandle<'a> {
/// ///
/// Using the `AxisSource::Finger` requires a stop event to be send, /// Using the `AxisSource::Finger` requires a stop event to be send,
/// when the user lifts off the finger (not necessarily in the same frame). /// when the user lifts off the finger (not necessarily in the same frame).
pub fn source(&mut self, source: wl_pointer::AxisSource) -> &mut Self { pub fn source(&mut self, source: AxisSource) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| { self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.axis_source(source); pointer.send(Event::AxisSource {
axis_source: source,
});
} }
}); });
self self
@ -170,10 +184,13 @@ impl<'a> PointerAxisHandle<'a> {
/// This event is optional and gives the client additional information about /// This event is optional and gives the client additional information about
/// the nature of the axis event. E.g. a scroll wheel might issue separate steps, /// the nature of the axis event. E.g. a scroll wheel might issue separate steps,
/// while a touchpad may never issue this event as it has no steps. /// while a touchpad may never issue this event as it has no steps.
pub fn discrete(&mut self, axis: wl_pointer::Axis, steps: i32) -> &mut Self { pub fn discrete(&mut self, axis: Axis, steps: i32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| { self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.axis_discrete(axis, steps); pointer.send(Event::AxisDiscrete {
axis,
discrete: steps,
});
} }
}); });
self self
@ -181,9 +198,9 @@ impl<'a> PointerAxisHandle<'a> {
/// The actual scroll value. This event is the only required one, but can also /// The actual scroll value. This event is the only required one, but can also
/// be send multiple times. The values off one frame will be accumulated by the client. /// be send multiple times. The values off one frame will be accumulated by the client.
pub fn value(&mut self, axis: wl_pointer::Axis, value: f64, time: u32) -> &mut Self { pub fn value(&mut self, axis: Axis, value: f64, time: u32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| { self.inner.with_focused_pointers(|pointer, _| {
pointer.axis(time, axis, value); pointer.send(Event::Axis { time, axis, value });
}); });
self self
} }
@ -192,10 +209,10 @@ impl<'a> PointerAxisHandle<'a> {
/// ///
/// This event is required for sources of the `AxisSource::Finger` type /// This event is required for sources of the `AxisSource::Finger` type
/// and otherwise optional. /// and otherwise optional.
pub fn stop(&mut self, axis: wl_pointer::Axis, time: u32) -> &mut Self { pub fn stop(&mut self, axis: Axis, time: u32) -> &mut Self {
self.inner.with_focused_pointers(|pointer, _| { self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.axis_stop(time, axis); pointer.send(Event::AxisStop { time, axis });
} }
}); });
self self
@ -209,7 +226,7 @@ impl<'a> PointerAxisHandle<'a> {
pub fn done(&mut self) { pub fn done(&mut self) {
self.inner.with_focused_pointers(|pointer, _| { self.inner.with_focused_pointers(|pointer, _| {
if pointer.version() >= 5 { if pointer.version() >= 5 {
pointer.frame(); pointer.send(Event::Frame);
} }
}) })
} }
@ -220,3 +237,35 @@ pub(crate) fn create_pointer_handler() -> PointerHandle {
inner: Arc::new(Mutex::new(PointerInternal::new())), inner: Arc::new(Mutex::new(PointerInternal::new())),
} }
} }
pub(crate) fn implement_pointer(
new_pointer: NewResource<WlPointer>,
handle: Option<&PointerHandle>,
) -> Resource<WlPointer> {
let destructor = match handle {
Some(h) => {
let inner = h.inner.clone();
Some(move |pointer: Resource<_>, _| {
inner
.lock()
.unwrap()
.known_pointers
.retain(|p| !p.equals(&pointer))
})
}
None => None,
};
new_pointer.implement(
|request, _pointer| {
match request {
Request::SetCursor { .. } => {
// TODO
}
Request::Release => {
// Our destructors already handle it
}
}
},
destructor,
)
}

View File

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

View File

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

View File

@ -1,940 +1,2 @@
//! Utilities for handling shell surfaces, toplevel and popups pub mod legacy;
//! pub mod xdg;
//! This module provides automatic handling of shell surfaces objects, by being registered
//! as a global handler for `wl_shell` and `xdg_shell`.
//!
//! ## Why use this implementation
//!
//! This implementation can track for you the various shell surfaces defined by the
//! clients by handling the `xdg_shell` protocol. It also includes a compatibility
//! layer for the deprecated `wl_shell` global.
//!
//! It allows you to easily access a list of all shell surfaces defined by your clients
//! access their associated metadata and underlying `wl_surface`s.
//!
//! This handler only handles the protocol exchanges with the client to present you the
//! information in a coherent and relatively easy to use maneer. All the actual drawing
//! and positioning logic of windows is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simple use the `shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the `CompositorHandler` provided by smithay.
//!
//! ```no_run
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols;
//! #
//! use smithay::wayland::compositor::roles::*;
//! use smithay::wayland::compositor::CompositorToken;
//! use smithay::wayland::shell::{shell_init, ShellSurfaceRole, ShellSurfaceUserImplementation};
//! use wayland_server::protocol::wl_shell::WlShell;
//! use wayland_protocols::unstable::xdg_shell::v6::server::zxdg_shell_v6::ZxdgShellV6;
//! use wayland_server::{EventLoop, EventLoopHandle};
//! # use wayland_server::protocol::{wl_seat, wl_output};
//! # use wayland_protocols::unstable::xdg_shell::v6::server::zxdg_toplevel_v6;
//! # #[derive(Default)] struct MySurfaceData;
//!
//! // define the roles type. You need to integrate the ShellSurface role:
//! define_roles!(MyRoles =>
//! [ShellSurface, ShellSurfaceRole]
//! );
//!
//! // define the metadata you want associated with the shell clients
//! #[derive(Default)]
//! struct MyShellData {
//! /* ... */
//! }
//!
//! # fn main() {
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<(), MyRoles, _, _>(
//! # &mut event_loop,
//! # unimplemented!(),
//! # (),
//! # None
//! # );
//! // define your implementation for shell
//! let my_shell_implementation = ShellSurfaceUserImplementation {
//! new_client: |evlh, idata, client| { unimplemented!() },
//! client_pong: |evlh, idata, client| { unimplemented!() },
//! new_toplevel: |evlh, idata, toplevel| { unimplemented!() },
//! new_popup: |evlh, idata, popup| { unimplemented!() },
//! move_: |evlh, idata, toplevel, seat, serial| { unimplemented!() },
//! resize: |evlh, idata, toplevel, seat, serial, edges| { unimplemented!() },
//! grab: |evlh, idata, popup, seat, serial| { unimplemented!() },
//! change_display_state: |evlh, idata, toplevel, maximized, minimized, fullscreen, output| {
//! unimplemented!()
//! },
//! show_window_menu: |evlh, idata, toplevel, seat, serial, x, y| { unimplemented!() },
//! };
//!
//! // define your implementation data
//! let my_shell_implementation_data = ();
//!
//! let (shell_state_token, _, _) = shell_init::<_, _, _, _, MyShellData, _>(
//! &mut event_loop,
//! compositor_token, // token from the compositor implementation
//! my_shell_implementation, // instance of shell::ShellSurfaceUserImplementation
//! my_shell_implementation_data, // whatever data you need here
//! None // put a logger if you want
//! );
//!
//! // You're now ready to go!
//! # }
//! ```
//!
//! ### Access to shell surface and clients data
//!
//! There are mainly 3 kind of objects that you'll manipulate from this implementation:
//!
//! - `ShellClient`: This is a handle representing an isntanciation of a shell global
//! you can associate client-wise metadata to it (this is the `MyShellData` type in
//! the example above).
//! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can
//! retrive a list of all currently alive toplevel surface from the `ShellState`.
//! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly,
//! you can get a list of all currently alive popup surface from the `ShellState`.
//!
//! You'll obtain these objects though two means: either via the callback methods of
//! the subhandler you provided, or via methods on the `ShellState` that you can
//! access from the `state()` of the event loop and the token returned by the init
//! function.
use std::cell::RefCell;
use std::rc::Rc;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6,
zxdg_positioner_v6 as xdg_positioner,
zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken};
use wayland_server::protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface};
mod wl_handlers;
mod xdg_handlers;
/// Metadata associated with the `shell_surface` role
pub struct ShellSurfaceRole {
/// Pending state as requested by the client
///
/// The data in this field are double-buffered, you should
/// apply them on a surface commit.
pub pending_state: ShellSurfacePendingState,
/// Geometry of the surface
///
/// Defines, in surface relative coordinates, what should
/// be considered as "the surface itself", regarding focus,
/// window alignment, etc...
///
/// By default, you should consider the full contents of the
/// buffers of this surface and its subsurfaces.
pub window_geometry: Option<Rectangle>,
/// List of non-acked configures pending
///
/// Whenever a configure is acked by the client, all configure
/// older than it are discarded as well. As such, this vec contains
/// the serials of all the configure send to this surface that are
/// newer than the last ack received.
pub pending_configures: Vec<u32>,
/// Has this surface acked at least one configure?
///
/// xdg_shell defines it as illegal to commit on a surface that has
/// not yet acked a configure.
pub configured: bool,
}
#[derive(Copy, Clone, Debug)]
/// The state of a positioner, as set by the client
pub struct PositionerState {
/// Size of the rectangle that needs to be positioned
pub rect_size: (i32, i32),
/// Anchor rectangle in the parent surface coordinates
/// relative to which the surface must be positioned
pub anchor_rect: Rectangle,
/// Edges defining the anchor point
pub anchor_edges: xdg_positioner::Anchor,
/// Gravity direction for positioning the child surface
/// relative to its anchor point
pub gravity: xdg_positioner::Gravity,
/// Adjustments to do if previous criterias constraint the
/// surface
pub constraint_adjustment: xdg_positioner::ConstraintAdjustment,
/// Offset placement relative to the anchor point
pub offset: (i32, i32),
}
/// Contents of the pending state of a shell surface, depending on its role
pub enum ShellSurfacePendingState {
/// This a regular, toplevel surface
///
/// This corresponds to either the `xdg_toplevel` role from the
/// `xdg_shell` protocol, or the result of `set_toplevel` using the
/// `wl_shell` protocol.
///
/// This is what you'll generaly interpret as "a window".
Toplevel(ToplevelState),
/// This is a popup surface
///
/// This corresponds to either the `xdg_popup` role from the
/// `xdg_shell` protocol, or the result of `set_popup` using the
/// `wl_shell` protocol.
///
/// This are mostly for small tooltips and similar short-lived
/// surfaces.
Popup(PopupState),
/// This surface was not yet assigned a kind
None,
}
/// State of a regular toplevel surface
pub struct ToplevelState {
/// Parent of this surface
///
/// If this surface has a parent, it should be hidden
/// or displayed, brought up at the same time as it.
pub parent: Option<wl_surface::WlSurface>,
/// Title of this shell surface
pub title: String,
/// App id for this shell surface
///
/// This identifier can be used to group surface together
/// as being several instance of the same app. This can
/// also be used as the D-Bus name for the app.
pub app_id: String,
/// Minimum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub min_size: (i32, i32),
/// Maximum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub max_size: (i32, i32),
}
impl ToplevelState {
/// Clone this ToplevelState
///
/// If the parent surface refers to a surface that no longer
/// exists, it is replaced by `None` in the process.
pub fn clone(&self) -> ToplevelState {
ToplevelState {
parent: self.parent.as_ref().and_then(|p| p.clone()),
title: self.title.clone(),
app_id: self.app_id.clone(),
min_size: self.min_size,
max_size: self.max_size,
}
}
}
/// The pending state of a popup surface
pub struct PopupState {
/// Parent of this popup surface
pub parent: wl_surface::WlSurface,
/// The positioner specifying how this tooltip should
/// be placed relative to its parent.
pub positioner: PositionerState,
}
impl PopupState {
/// Clone this PopupState
///
/// If the parent surface refers to a surface that no longer
/// exists, this will return `None`, as the popup can no
/// longer be meaningfully displayed.
pub fn clone(&self) -> Option<PopupState> {
if let Some(p) = self.parent.clone() {
Some(PopupState {
parent: p,
positioner: self.positioner,
})
} else {
// the parent surface does no exist any longer,
// this popup does not make any sense now
None
}
}
}
impl Default for ShellSurfacePendingState {
fn default() -> ShellSurfacePendingState {
ShellSurfacePendingState::None
}
}
/// Internal implementation data of shell surfaces
///
/// This type is only visible as type parameter of
/// the `Global` handle you are provided.
pub struct ShellSurfaceIData<U, R, CID, SID, SD> {
log: ::slog::Logger,
compositor_token: CompositorToken<U, R, CID>,
implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>,
idata: Rc<RefCell<SID>>,
state_token: StateToken<ShellState<U, R, CID, SD>>,
}
impl<U, R, CID, SID, SD> Clone for ShellSurfaceIData<U, R, CID, SID, SD> {
fn clone(&self) -> ShellSurfaceIData<U, R, CID, SID, SD> {
ShellSurfaceIData {
log: self.log.clone(),
compositor_token: self.compositor_token,
implementation: self.implementation,
idata: self.idata.clone(),
state_token: self.state_token.clone(),
}
}
}
/// Create new `xdg_shell` and `wl_shell` globals.
///
/// The globals are directly registered into the eventloop, and this function
/// returns a `StateToken<_>` which you'll need access the list of shell
/// surfaces created by your clients.
///
/// It also returns the two global handles, in case you whish to remove these
/// globals from the event loop in the future.
pub fn shell_init<U, R, CID, SID, SD, L>(
evlh: &mut EventLoopHandle, 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 = evlh.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 = evlh.register_global(
1,
self::wl_handlers::wl_shell_bind::<U, R, CID, SID, SD>,
shell_surface_idata.clone(),
);
let xdg_shell_global = evlh.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
pub fn cleanup_surfaces(&mut self) {
self.known_toplevels.retain(|s| s.alive());
self.known_popups.retain(|s| s.alive());
}
/// Access all the shell surfaces known by this handler
pub fn toplevel_surfaces(&self) -> &[ToplevelSurface<U, R, CID, SD>] {
&self.known_toplevels[..]
}
/// Access all the popup surfaces known by this handler
pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, CID, SD>] {
&self.known_popups[..]
}
}
/*
* User interaction
*/
enum ShellClientKind {
Wl(wl_shell::WlShell),
Xdg(zxdg_shell_v6::ZxdgShellV6),
}
pub(crate) struct ShellClientData<SD> {
pending_ping: u32,
data: SD,
}
fn make_shell_client_data<SD: Default>() -> ShellClientData<SD> {
ShellClientData {
pending_ping: 0,
data: Default::default(),
}
}
/// A shell client
///
/// This represents an instanciation of a shell
/// global (be it `wl_shell` or `xdg_shell`).
///
/// Most of the time, you can consider that a
/// wayland client will be a single shell client.
///
/// You can use this handle to access a storage for any
/// client-specific data you wish to associate with it.
pub struct ShellClient<SD> {
kind: ShellClientKind,
_data: ::std::marker::PhantomData<*mut SD>,
}
impl<SD> ShellClient<SD> {
/// Is the shell client represented by this handle still connected?
pub fn alive(&self) -> bool {
match self.kind {
ShellClientKind::Wl(ref s) => s.status() == Liveness::Alive,
ShellClientKind::Xdg(ref s) => s.status() == Liveness::Alive,
}
}
/// Checks if this handle and the other one actually refer to the
/// same shell client
pub fn equals(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(&ShellClientKind::Wl(ref s1), &ShellClientKind::Wl(ref s2)) => s1.equals(s2),
(&ShellClientKind::Xdg(ref s1), &ShellClientKind::Xdg(ref s2)) => s1.equals(s2),
_ => false,
}
}
/// Send a ping request to this shell client
///
/// You'll receive the reply in the `Handler::cient_pong()` method.
///
/// A typical use is to start a timer at the same time you send this ping
/// request, and cancel it when you receive the pong. If the timer runs
/// down to 0 before a pong is received, mark the client as unresponsive.
///
/// Fails if this shell client already has a pending ping or is already dead.
pub fn send_ping(&self, serial: u32) -> Result<(), ()> {
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Wl(ref shell) => {
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.0.pending_ping == 0 {
return Err(());
}
guard.0.pending_ping = serial;
if let Some(surface) = guard.1.first() {
// there is at least one surface, send the ping
// if there is no surface, the ping will remain pending
// and will be sent when the client creates a surface
surface.ping(serial);
}
}
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == 0 {
return Err(());
}
guard.pending_ping = serial;
shell.ping(serial);
}
}
Ok(())
}
/// Access the user data associated with this shell client
pub fn with_data<F, T>(&self, f: F) -> Result<T, ()>
where
F: FnOnce(&mut SD) -> T,
{
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Wl(ref shell) => {
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.0.data))
}
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.data))
}
}
}
}
enum SurfaceKind {
Wl(wl_shell_surface::WlShellSurface),
XdgToplevel(zxdg_toplevel_v6::ZxdgToplevelV6),
XdgPopup(zxdg_popup_v6::ZxdgPopupV6),
}
/// A handle to a toplevel surface
///
/// This is an unified abstraction over the toplevel surfaces
/// of both `wl_shell` and `xdg_shell`.
pub struct ToplevelSurface<U, R, CID, SD> {
wl_surface: wl_surface::WlSurface,
shell_surface: SurfaceKind,
token: CompositorToken<U, R, CID>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, CID, SD> ToplevelSurface<U, R, CID, SD>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
/// Is the toplevel surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_surface_alive = match self.shell_surface {
SurfaceKind::Wl(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgToplevel(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgPopup(_) => unreachable!(),
};
shell_surface_alive && self.wl_surface.status() == Liveness::Alive
}
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this toplevel surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => {
let &(_, ref shell) =
unsafe { &*(s.get_user_data() as *mut self::wl_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Wl(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgToplevel(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Xdg(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgPopup(_) => unreachable!(),
}
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: ToplevelConfigure) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => self::wl_handlers::send_toplevel_configure(s, cfg),
SurfaceKind::XdgToplevel(ref s) => {
self::xdg_handlers::send_toplevel_configure(self.token, s, cfg)
}
SurfaceKind::XdgPopup(_) => unreachable!(),
}
EventResult::Sent(())
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
if let SurfaceKind::XdgToplevel(ref s) = self.shell_surface {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
} else {
unreachable!();
}
}
configured
}
/// Send a "close" event to the client
pub fn send_close(&self) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(_) => EventResult::Sent(()),
SurfaceKind::XdgToplevel(ref s) => s.close(),
SurfaceKind::XdgPopup(_) => unreachable!(),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this toplevel surface
///
/// Returns `None` of the toplevel surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<ToplevelState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref state) => Some(state.clone()),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
/// A handle to a popup surface
///
/// This is an unified abstraction over the popup surfaces
/// of both `wl_shell` and `xdg_shell`.
pub struct PopupSurface<U, R, CID, SD> {
wl_surface: wl_surface::WlSurface,
shell_surface: SurfaceKind,
token: CompositorToken<U, R, CID>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, CID, SD> PopupSurface<U, R, CID, SD>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
/// Is the popup surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_surface_alive = match self.shell_surface {
SurfaceKind::Wl(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgPopup(ref s) => s.status() == Liveness::Alive,
SurfaceKind::XdgToplevel(_) => unreachable!(),
};
shell_surface_alive && self.wl_surface.status() == Liveness::Alive
}
/// Do this handle and the other one actually refer to the same popup surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this popup surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => {
let &(_, ref shell) =
unsafe { &*(s.get_user_data() as *mut self::wl_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Wl(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgPopup(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
Some(ShellClient {
kind: ShellClientKind::Xdg(unsafe { shell.clone_unchecked() }),
_data: ::std::marker::PhantomData,
})
}
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: PopupConfigure) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => self::wl_handlers::send_popup_configure(s, cfg),
SurfaceKind::XdgPopup(ref s) => self::xdg_handlers::send_popup_configure(self.token, s, cfg),
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
EventResult::Sent(())
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
if let SurfaceKind::XdgPopup(ref s) = self.shell_surface {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
} else {
unreachable!();
}
}
configured
}
/// Send a 'popup_done' event to the popup surface
///
/// It means that the use has dismissed the popup surface, or that
/// the pointer has left the area of popup grab if there was a grab.
pub fn send_popup_done(&self) -> EventResult<()> {
if !self.alive() {
return EventResult::Destroyed;
}
match self.shell_surface {
SurfaceKind::Wl(ref s) => s.popup_done(),
SurfaceKind::XdgPopup(ref s) => s.popup_done(),
SurfaceKind::XdgToplevel(_) => unreachable!(),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this popup surface
///
/// Returns `None` of the popup surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<PopupState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<ShellSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
ShellSurfacePendingState::Popup(ref state) => state.clone(),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
/// A configure message for toplevel surfaces
pub struct ToplevelConfigure {
/// A suggestion for a new size for the surface
pub size: Option<(i32, i32)>,
/// A notification of what are the current states of this surface
///
/// A surface can be any combination of these possible states
/// at the same time.
pub states: Vec<zxdg_toplevel_v6::State>,
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// A configure message for popup surface
pub struct PopupConfigure {
/// The position chosen for this popup relative to
/// its parent
pub position: (i32, i32),
/// A suggested size for the popup
pub size: (i32, i32),
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// A sub-implementation for the shell
///
/// You need to provide this to handle events that the provided implementation
/// cannot process for you directly.
///
/// Depending on what you want to do, you might implement some of these functions
/// as doing nothing.
pub struct ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
/// A new shell client was instanciated
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 ShellHandler already checked for you that the serial matches the one
/// from the pending ping.
pub client_pong: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient<SD>),
/// A new toplevel surface was created
///
/// You need to return a `ToplevelConfigure` from this function, which will be sent
/// to the client to configure this surface
pub new_toplevel:
fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: ToplevelSurface<U, R, CID, SD>)
-> ToplevelConfigure,
/// A new popup surface was created
///
/// You need to return a `PopupConfigure` from this function, which will be sent
/// to the client to configure this surface
pub new_popup: fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: PopupSurface<U, R, CID, SD>)
-> PopupConfigure,
/// The client requested the start of an interactive move for this surface
pub move_: fn(
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 `edges` argument specifies which part of the window's border is being dragged.
pub resize: fn(
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 means it requests to be sent a `popup_done` event when the pointer leaves
/// the grab area.
pub grab: fn(
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
///
/// Each field represents the request of the client for a specific property:
///
/// - `None`: no request is made to change this property
/// - `Some(true)`: this property should be enabled
/// - `Some(false)`: this property should be disabled
///
/// For fullscreen/maximization, the client can also optionnaly request a specific
/// output.
///
/// You are to answer with a `ToplevelConfigure` that will be sent to the client in
/// response.
pub change_display_state: fn(
evlh: &mut EventLoopHandle,
idata: &mut SID,
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
///
/// This menu belongs to the compositor. It is typically expected to contain options for
/// control of the window (maximize/minimize/close/move/etc...).
pub show_window_menu: fn(
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,446 +0,0 @@
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelConfigure, ToplevelState};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::unstable::xdg_shell::v6::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};
pub(crate) fn wl_shell_bind<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>, _: &Client,
shell: wl_shell::WlShell,
) where
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
*/
pub(crate) type ShellUserData<SD> = Mutex<(ShellClientData<SD>, Vec<wl_shell_surface::WlShellSurface>)>;
fn destroy_shell<SD>(shell: &wl_shell::WlShell) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicitly call drop to not forget what we're doing here
::std::mem::drop(data);
}
pub fn make_shell_client<SD>(resource: &wl_shell::WlShell) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::Wl(unsafe { resource.clone_unchecked() }),
_data: ::std::marker::PhantomData,
}
}
fn shell_implementation<U, R, CID, SID, SD>(
) -> wl_shell::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
wl_shell::Implementation {
get_shell_surface: |evlh, idata, _, shell, shell_surface, surface| {
let role_data = ShellSurfaceRole {
pending_state: ShellSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: true,
};
if idata
.compositor_token
.give_role_with(surface, role_data)
.is_err()
{
shell.post_error(
wl_shell::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
shell_surface.set_user_data(Box::into_raw(Box::new(unsafe {
(surface.clone_unchecked(), shell.clone_unchecked())
})) as *mut _);
evlh.register(
&shell_surface,
shell_surface_implementation(),
idata.clone(),
Some(destroy_shell_surface),
);
// register ourselves to the wl_shell for ping handling
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.1.is_empty() && guard.0.pending_ping != 0 {
// there is a pending ping that no surface could receive yet, send it
// note this is not possible that it was received and then a wl_shell_surface was
// destroyed, because wl_shell_surface has no destructor!
shell_surface.ping(guard.0.pending_ping);
}
guard.1.push(shell_surface);
},
}
}
/*
* wl_shell_surface
*/
pub type ShellSurfaceUserData = (wl_surface::WlSurface, wl_shell::WlShell);
fn destroy_shell_surface(shell_surface: &wl_shell_surface::WlShellSurface) {
let ptr = shell_surface.get_user_data();
shell_surface.set_user_data(::std::ptr::null_mut());
// drop the WlSurface object
let surface = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
// explicitly call drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn make_toplevel_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &wl_shell_surface::WlShellSurface
) -> super::ToplevelSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::Wl(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
fn make_popup_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &wl_shell_surface::WlShellSurface
) -> super::PopupSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::Wl(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
pub fn send_toplevel_configure(resource: &wl_shell_surface::WlShellSurface, configure: ToplevelConfigure) {
let (w, h) = configure.size.unwrap_or((0, 0));
resource.configure(wl_shell_surface::Resize::empty(), w, h);
}
pub fn send_popup_configure(resource: &wl_shell_surface::WlShellSurface, configure: PopupConfigure) {
let (w, h) = configure.size;
resource.configure(wl_shell_surface::Resize::empty(), w, h);
}
fn wl_handle_display_state_change<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
shell_surface: &wl_shell_surface::WlShellSurface, maximized: Option<bool>, minimized: Option<bool>,
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>,
) {
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
// handler callback
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.change_display_state)(
evlh,
&mut *user_idata,
handle,
maximized,
minimized,
fullscreen,
output,
);
// send the configure response to client
let (w, h) = configure.size.unwrap_or((0, 0));
shell_surface.configure(wl_shell_surface::Resize::None, w, h);
}
fn wl_set_parent<U, R, CID, SID, SD>(
idata: &ShellSurfaceIData<U, R, CID, SID, SD>, shell_surface: &wl_shell_surface::WlShellSurface,
parent: Option<wl_surface::WlSurface>,
) 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) };
idata
.compositor_token
.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
.get_surface()
.map(|s| !s.equals(wl_surface))
.unwrap_or(false)
});
}
ShellSurfacePendingState::None => {}
}
// This was not previously toplevel, need to make it toplevel
data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
true
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
// we need to notify about this new toplevel surface
if need_send {
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.push(make_toplevel_handle(idata.compositor_token, shell_surface));
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle);
send_toplevel_configure(shell_surface, configure);
}
}
fn shell_surface_implementation<U, R, CID, SID, SD>(
) -> wl_shell_surface::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
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| {
if let ShellSurfacePendingState::Toplevel(ref mut state) = data.pending_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?!");
},
}
}

View File

@ -0,0 +1,916 @@
//! Utilities for handling shell surfaces with the `xdg_shell` protocol
//!
//! This module provides automatic handling of shell surfaces objects, by being registered
//! as a global handler for `xdg_shell`.
//!
//! ## Why use this implementation
//!
//! This implementation can track for you the various shell surfaces defined by the
//! clients by handling the `xdg_shell` protocol. It also contains a compatibility
//! layer handling its precursor, the unstable `zxdg_shell_v6` protocol, which is
//! mostly identical.
//!
//! It allows you to easily access a list of all shell surfaces defined by your clients
//! access their associated metadata and underlying `wl_surface`s.
//!
//! This handler only handles the protocol exchanges with the client to present you the
//! information in a coherent and relatively easy to use maneer. All the actual drawing
//! and positioning logic of windows is out of its scope.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this handler, simple use the `xdg_shell_init` function provided in this
//! module. You will need to provide it the `CompositorToken` you retrieved from an
//! instanciation of the compositor global provided by smithay.
//!
//! ```no_run
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # extern crate wayland_protocols;
//! #
//! use smithay::wayland::compositor::roles::*;
//! use smithay::wayland::compositor::CompositorToken;
//! use smithay::wayland::shell::xdg::{xdg_shell_init, XdgSurfaceRole, XdgRequest};
//! use wayland_protocols::unstable::xdg_shell::v6::server::zxdg_shell_v6::ZxdgShellV6;
//! use wayland_server::{EventLoop, LoopToken};
//! # use wayland_server::protocol::{wl_seat, wl_output};
//! # #[derive(Default)] struct MySurfaceData;
//!
//! // define the roles type. You need to integrate the XdgSurface role:
//! define_roles!(MyRoles =>
//! [XdgSurface, XdgSurfaceRole]
//! );
//!
//! // define the metadata you want associated with the shell clients
//! #[derive(Default)]
//! struct MyShellData {
//! /* ... */
//! }
//!
//! # fn main() {
//! # let (mut display, event_loop) = wayland_server::Display::new();
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<(), MyRoles, _, _>(
//! # &mut display,
//! # event_loop.token(),
//! # |_, _| {},
//! # None
//! # );
//! let (shell_state, _, _) = xdg_shell_init(
//! &mut display,
//! event_loop.token(),
//! // token from the compositor implementation
//! compositor_token,
//! // your implementation, can also be a strucy implementing the
//! // appropriate Implementation<(), XdgRequest<_, _, _>> trait
//! |event: XdgRequest<_, _, MyShellData>, ()| { /* ... */ },
//! None // put a logger if you want
//! );
//!
//! // You're now ready to go!
//! # }
//! ```
//!
//! ### Access to shell surface and clients data
//!
//! There are mainly 3 kind of objects that you'll manipulate from this implementation:
//!
//! - `ShellClient`: This is a handle representing an isntanciation of a shell global
//! you can associate client-wise metadata to it (this is the `MyShellData` type in
//! the example above).
//! - `ToplevelSurface`: This is a handle representing a toplevel surface, you can
//! retrive a list of all currently alive toplevel surface from the `ShellState`.
//! - `PopupSurface`: This is a handle representing a popup/tooltip surface. Similarly,
//! you can get a list of all currently alive popup surface from the `ShellState`.
//!
//! You'll obtain these objects though two means: either via the callback methods of
//! the subhandler you provided, or via methods on the `ShellState` that you are given
//! (in an `Arc<Mutex<_>>`) as return value of the init function.
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::Role;
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_shell_v6, zxdg_surface_v6,
zxdg_toplevel_v6};
use wayland_server::{Display, Global, LoopToken, Resource};
use wayland_server::commons::Implementation;
use wayland_server::protocol::{wl_output, wl_seat, wl_surface};
// handlers for the xdg_shell protocol
mod xdg_handlers;
// compatibility handlers for the zxdg_shell_v6 protocol, its earlier version
mod zxdgv6_handlers;
/// Metadata associated with the `xdg_surface` role
pub struct XdgSurfaceRole {
/// Pending state as requested by the client
///
/// The data in this field are double-buffered, you should
/// apply them on a surface commit.
pub pending_state: XdgSurfacePendingState,
/// Geometry of the surface
///
/// Defines, in surface relative coordinates, what should
/// be considered as "the surface itself", regarding focus,
/// window alignment, etc...
///
/// By default, you should consider the full contents of the
/// buffers of this surface and its subsurfaces.
pub window_geometry: Option<Rectangle>,
/// List of non-acked configures pending
///
/// Whenever a configure is acked by the client, all configure
/// older than it are discarded as well. As such, this vec contains
/// the serials of all the configure send to this surface that are
/// newer than the last ack received.
pub pending_configures: Vec<u32>,
/// Has this surface acked at least one configure?
///
/// xdg_shell defines it as illegal to commit on a surface that has
/// not yet acked a configure.
pub configured: bool,
}
#[derive(Clone, Debug)]
/// The state of a positioner, as set by the client
pub struct PositionerState {
/// Size of the rectangle that needs to be positioned
pub rect_size: (i32, i32),
/// Anchor rectangle in the parent surface coordinates
/// relative to which the surface must be positioned
pub anchor_rect: Rectangle,
/// Edges defining the anchor point
pub anchor_edges: xdg_positioner::Anchor,
/// Gravity direction for positioning the child surface
/// relative to its anchor point
pub gravity: xdg_positioner::Gravity,
/// Adjustments to do if previous criterias constraint the
/// surface
pub constraint_adjustment: xdg_positioner::ConstraintAdjustment,
/// Offset placement relative to the anchor point
pub offset: (i32, i32),
}
impl PositionerState {
pub(crate) fn new() -> PositionerState {
PositionerState {
rect_size: (0, 0),
anchor_rect: Rectangle {
x: 0,
y: 0,
width: 0,
height: 0,
},
anchor_edges: xdg_positioner::Anchor::None,
gravity: xdg_positioner::Gravity::None,
constraint_adjustment: xdg_positioner::ConstraintAdjustment::None,
offset: (0, 0),
}
}
}
/// Contents of the pending state of a shell surface, depending on its role
pub enum XdgSurfacePendingState {
/// This a regular, toplevel surface
///
/// This corresponds to the `xdg_toplevel` role
///
/// This is what you'll generaly interpret as "a window".
Toplevel(ToplevelState),
/// This is a popup surface
///
/// This corresponds to the `xdg_popup` role
///
/// This are mostly for small tooltips and similar short-lived
/// surfaces.
Popup(PopupState),
/// This surface was not yet assigned a kind
None,
}
/// State of a regular toplevel surface
pub struct ToplevelState {
/// Parent of this surface
///
/// If this surface has a parent, it should be hidden
/// or displayed, brought up at the same time as it.
pub parent: Option<Resource<wl_surface::WlSurface>>,
/// Title of this shell surface
pub title: String,
/// App id for this shell surface
///
/// This identifier can be used to group surface together
/// as being several instance of the same app. This can
/// also be used as the D-Bus name for the app.
pub app_id: String,
/// Minimum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub min_size: (i32, i32),
/// Maximum size requested for this surface
///
/// A value of 0 on an axis means this axis is not constrained
pub max_size: (i32, i32),
}
impl Clone for ToplevelState {
fn clone(&self) -> ToplevelState {
ToplevelState {
parent: self.parent.as_ref().map(|p| p.clone()),
title: self.title.clone(),
app_id: self.app_id.clone(),
min_size: self.min_size,
max_size: self.max_size,
}
}
}
/// The pending state of a popup surface
pub struct PopupState {
/// Parent of this popup surface
pub parent: Option<Resource<wl_surface::WlSurface>>,
/// The positioner specifying how this tooltip should
/// be placed relative to its parent.
pub positioner: PositionerState,
}
impl Clone for PopupState {
fn clone(&self) -> PopupState {
PopupState {
parent: self.parent.as_ref().map(|p| p.clone()),
positioner: self.positioner.clone(),
}
}
}
impl Default for XdgSurfacePendingState {
fn default() -> XdgSurfacePendingState {
XdgSurfacePendingState::None
}
}
pub(crate) struct ShellImplementation<U, R, SD> {
log: ::slog::Logger,
compositor_token: CompositorToken<U, R>,
loop_token: LoopToken,
user_impl: Rc<RefCell<Implementation<(), XdgRequest<U, R, SD>>>>,
shell_state: Arc<Mutex<ShellState<U, R, SD>>>,
}
impl<U, R, SD> Clone for ShellImplementation<U, R, SD> {
fn clone(&self) -> Self {
ShellImplementation {
log: self.log.clone(),
compositor_token: self.compositor_token.clone(),
loop_token: self.loop_token.clone(),
user_impl: self.user_impl.clone(),
shell_state: self.shell_state.clone(),
}
}
}
/// Create a new `xdg_shell` globals
pub fn xdg_shell_init<U, R, SD, L, Impl>(
display: &mut Display,
ltoken: LoopToken,
ctoken: CompositorToken<U, R>,
implementation: Impl,
logger: L,
) -> (
Arc<Mutex<ShellState<U, R, SD>>>,
Global<xdg_wm_base::XdgWmBase>,
Global<zxdg_shell_v6::ZxdgShellV6>,
)
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: Default + 'static,
L: Into<Option<::slog::Logger>>,
Impl: Implementation<(), XdgRequest<U, R, SD>>,
{
let log = ::slog_or_stdlog(logger);
let shell_state = Arc::new(Mutex::new(ShellState {
known_toplevels: Vec::new(),
known_popups: Vec::new(),
}));
let shell_impl = ShellImplementation {
log: log.new(o!("smithay_module" => "xdg_shell_handler")),
loop_token: ltoken.clone(),
compositor_token: ctoken,
user_impl: Rc::new(RefCell::new(implementation)),
shell_state: shell_state.clone(),
};
let shell_impl_z = shell_impl.clone();
let xdg_shell_global = display.create_global(&ltoken, 1, move |_version, shell| {
self::xdg_handlers::implement_wm_base(shell, &shell_impl);
});
let zxdgv6_shell_global = display.create_global(&ltoken, 1, move |_version, shell| {
self::zxdgv6_handlers::implement_shell(shell, &shell_impl_z);
});
(shell_state, xdg_shell_global, zxdgv6_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, SD> {
known_toplevels: Vec<ToplevelSurface<U, R, SD>>,
known_popups: Vec<PopupSurface<U, R, SD>>,
}
impl<U, R, SD> ShellState<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
/// Access all the shell surfaces known by this handler
pub fn toplevel_surfaces(&self) -> &[ToplevelSurface<U, R, SD>] {
&self.known_toplevels[..]
}
/// Access all the popup surfaces known by this handler
pub fn popup_surfaces(&self) -> &[PopupSurface<U, R, SD>] {
&self.known_popups[..]
}
}
/*
* User interaction
*/
enum ShellClientKind {
Xdg(Resource<xdg_wm_base::XdgWmBase>),
ZxdgV6(Resource<zxdg_shell_v6::ZxdgShellV6>),
}
pub(crate) struct ShellClientData<SD> {
pending_ping: u32,
data: SD,
}
fn make_shell_client_data<SD: Default>() -> ShellClientData<SD> {
ShellClientData {
pending_ping: 0,
data: Default::default(),
}
}
/// A shell client
///
/// This represents an instanciation of a shell
/// global (be it `wl_shell` or `xdg_shell`).
///
/// Most of the time, you can consider that a
/// wayland client will be a single shell client.
///
/// You can use this handle to access a storage for any
/// client-specific data you wish to associate with it.
pub struct ShellClient<SD> {
kind: ShellClientKind,
_data: ::std::marker::PhantomData<*mut SD>,
}
impl<SD> ShellClient<SD> {
/// Is the shell client represented by this handle still connected?
pub fn alive(&self) -> bool {
match self.kind {
ShellClientKind::Xdg(ref s) => s.is_alive(),
ShellClientKind::ZxdgV6(ref s) => s.is_alive(),
}
}
/// Checks if this handle and the other one actually refer to the
/// same shell client
pub fn equals(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(&ShellClientKind::Xdg(ref s1), &ShellClientKind::Xdg(ref s2)) => s1.equals(s2),
(&ShellClientKind::ZxdgV6(ref s1), &ShellClientKind::ZxdgV6(ref s2)) => s1.equals(s2),
_ => false,
}
}
/// Send a ping request to this shell client
///
/// You'll receive the reply as a `XdgRequest::ClientPong` request.
///
/// A typical use is to start a timer at the same time you send this ping
/// request, and cancel it when you receive the pong. If the timer runs
/// down to 0 before a pong is received, mark the client as unresponsive.
///
/// Fails if this shell client already has a pending ping or is already dead.
pub fn send_ping(&self, serial: u32) -> Result<(), ()> {
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == 0 {
return Err(());
}
guard.pending_ping = serial;
shell.send(xdg_wm_base::Event::Ping { serial });
}
ShellClientKind::ZxdgV6(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::zxdgv6_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == 0 {
return Err(());
}
guard.pending_ping = serial;
shell.send(zxdg_shell_v6::Event::Ping { serial });
}
}
Ok(())
}
/// Access the user data associated with this shell client
pub fn with_data<F, T>(&self, f: F) -> Result<T, ()>
where
F: FnOnce(&mut SD) -> T,
{
if !self.alive() {
return Err(());
}
match self.kind {
ShellClientKind::Xdg(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::xdg_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.data))
}
ShellClientKind::ZxdgV6(ref shell) => {
let mutex =
unsafe { &*(shell.get_user_data() as *mut self::zxdgv6_handlers::ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
Ok(f(&mut guard.data))
}
}
}
}
pub(crate) enum ToplevelKind {
Xdg(Resource<xdg_toplevel::XdgToplevel>),
ZxdgV6(Resource<zxdg_toplevel_v6::ZxdgToplevelV6>),
}
/// A handle to a toplevel surface
pub struct ToplevelSurface<U, R, SD> {
wl_surface: Resource<wl_surface::WlSurface>,
shell_surface: ToplevelKind,
token: CompositorToken<U, R>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, SD> ToplevelSurface<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
/// Is the toplevel surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_alive = match self.shell_surface {
ToplevelKind::Xdg(ref s) => s.is_alive(),
ToplevelKind::ZxdgV6(ref s) => s.is_alive(),
};
shell_alive && self.wl_surface.is_alive()
}
/// Do this handle and the other one actually refer to the same toplevel surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this toplevel surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
let shell = match self.shell_surface {
ToplevelKind::Xdg(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
ShellClientKind::Xdg(shell.clone())
}
ToplevelKind::ZxdgV6(ref s) => {
let &(_, ref shell, _) =
unsafe { &*(s.get_user_data() as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
ShellClientKind::ZxdgV6(shell.clone())
}
};
Some(ShellClient {
kind: shell,
_data: ::std::marker::PhantomData,
})
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: ToplevelConfigure) {
if !self.alive() {
return;
}
match self.shell_surface {
ToplevelKind::Xdg(ref s) => self::xdg_handlers::send_toplevel_configure(self.token, s, cfg),
ToplevelKind::ZxdgV6(ref s) => self::zxdgv6_handlers::send_toplevel_configure(self.token, s, cfg),
}
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
match self.shell_surface {
ToplevelKind::Xdg(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
xdg_surface::Error::NotConstructed as u32,
"Surface has not been configured yet.".into(),
);
}
ToplevelKind::ZxdgV6(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been configured yet.".into(),
);
}
}
}
configured
}
/// Send a "close" event to the client
pub fn send_close(&self) {
match self.shell_surface {
ToplevelKind::Xdg(ref s) => s.send(xdg_toplevel::Event::Close),
ToplevelKind::ZxdgV6(ref s) => s.send(zxdg_toplevel_v6::Event::Close),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this toplevel surface
///
/// Returns `None` of the toplevel surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<ToplevelState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
XdgSurfacePendingState::Toplevel(ref state) => Some(state.clone()),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
pub(crate) enum PopupKind {
Xdg(Resource<xdg_popup::XdgPopup>),
ZxdgV6(Resource<zxdg_popup_v6::ZxdgPopupV6>),
}
/// A handle to a popup surface
///
/// This is an unified abstraction over the popup surfaces
/// of both `wl_shell` and `xdg_shell`.
pub struct PopupSurface<U, R, SD> {
wl_surface: Resource<wl_surface::WlSurface>,
shell_surface: PopupKind,
token: CompositorToken<U, R>,
_shell_data: ::std::marker::PhantomData<SD>,
}
impl<U, R, SD> PopupSurface<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
/// Is the popup surface refered by this handle still alive?
pub fn alive(&self) -> bool {
let shell_alive = match self.shell_surface {
PopupKind::Xdg(ref p) => p.is_alive(),
PopupKind::ZxdgV6(ref p) => p.is_alive(),
};
shell_alive && self.wl_surface.is_alive()
}
/// Do this handle and the other one actually refer to the same popup surface?
pub fn equals(&self, other: &Self) -> bool {
self.alive() && other.alive() && self.wl_surface.equals(&other.wl_surface)
}
/// Retrieve the shell client owning this popup surface
///
/// Returns `None` if the surface does actually no longer exist.
pub fn client(&self) -> Option<ShellClient<SD>> {
if !self.alive() {
return None;
}
let shell = match self.shell_surface {
PopupKind::Xdg(ref p) => {
let &(_, ref shell, _) =
unsafe { &*(p.get_user_data() as *mut self::xdg_handlers::ShellSurfaceUserData) };
ShellClientKind::Xdg(shell.clone())
}
PopupKind::ZxdgV6(ref p) => {
let &(_, ref shell, _) =
unsafe { &*(p.get_user_data() as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
ShellClientKind::ZxdgV6(shell.clone())
}
};
Some(ShellClient {
kind: shell,
_data: ::std::marker::PhantomData,
})
}
/// Send a configure event to this toplevel surface to suggest it a new configuration
///
/// The serial of this configure will be tracked waiting for the client to ACK it.
pub fn send_configure(&self, cfg: PopupConfigure) {
if !self.alive() {
return;
}
match self.shell_surface {
PopupKind::Xdg(ref p) => {
self::xdg_handlers::send_popup_configure(self.token, p, cfg);
}
PopupKind::ZxdgV6(ref p) => {
self::zxdgv6_handlers::send_popup_configure(self.token, p, cfg);
}
}
}
/// Make sure this surface was configured
///
/// Returns `true` if it was, if not, returns `false` and raise
/// a protocol error to the associated client. Also returns `false`
/// if the surface is already destroyed.
///
/// xdg_shell mandates that a client acks a configure before commiting
/// anything.
pub fn ensure_configured(&self) -> bool {
if !self.alive() {
return false;
}
let configured = self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| data.configured)
.expect("A shell surface object exists but the surface does not have the shell_surface role ?!");
if !configured {
match self.shell_surface {
PopupKind::Xdg(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::xdg_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
xdg_surface::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
}
PopupKind::ZxdgV6(ref s) => {
let ptr = s.get_user_data();
let &(_, _, ref xdg_surface) =
unsafe { &*(ptr as *mut self::zxdgv6_handlers::ShellSurfaceUserData) };
xdg_surface.post_error(
zxdg_surface_v6::Error::NotConstructed as u32,
"Surface has not been confgured yet.".into(),
);
}
}
}
configured
}
/// Send a 'popup_done' event to the popup surface
///
/// It means that the use has dismissed the popup surface, or that
/// the pointer has left the area of popup grab if there was a grab.
pub fn send_popup_done(&self) {
match self.shell_surface {
PopupKind::Xdg(ref p) => p.send(xdg_popup::Event::PopupDone),
PopupKind::ZxdgV6(ref p) => p.send(zxdg_popup_v6::Event::PopupDone),
}
}
/// Access the underlying `wl_surface` of this toplevel surface
///
/// Returns `None` if the toplevel surface actually no longer exists.
pub fn get_surface(&self) -> Option<&Resource<wl_surface::WlSurface>> {
if self.alive() {
Some(&self.wl_surface)
} else {
None
}
}
/// Retrieve a copy of the pending state of this popup surface
///
/// Returns `None` of the popup surface actually no longer exists.
pub fn get_pending_state(&self) -> Option<PopupState> {
if !self.alive() {
return None;
}
self.token
.with_role_data::<XdgSurfaceRole, _, _>(&self.wl_surface, |data| match data.pending_state {
XdgSurfacePendingState::Popup(ref state) => Some(state.clone()),
_ => None,
})
.ok()
.and_then(|x| x)
}
}
/// A configure message for toplevel surfaces
pub struct ToplevelConfigure {
/// A suggestion for a new size for the surface
pub size: Option<(i32, i32)>,
/// A notification of what are the current states of this surface
///
/// A surface can be any combination of these possible states
/// at the same time.
pub states: Vec<xdg_toplevel::State>,
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// A configure message for popup surface
pub struct PopupConfigure {
/// The position chosen for this popup relative to
/// its parent
pub position: (i32, i32),
/// A suggested size for the popup
pub size: (i32, i32),
/// A serial number to track ACK from the client
///
/// This should be an ever increasing number, as the ACK-ing
/// from a client for a serial will validate all pending lower
/// serials.
pub serial: u32,
}
/// Events generated by xdg shell surfaces
///
/// These are events that the provided implementation cannot process
/// for you directly.
///
/// Depending on what you want to do, you might ignore some of them
pub enum XdgRequest<U, R, SD> {
/// A new shell client was instanciated
NewClient {
/// the client
client: ShellClient<SD>,
},
/// The pong for a pending ping of this shell client was received
///
/// The ShellHandler already checked for you that the serial matches the one
/// from the pending ping.
ClientPong {
/// the client
client: ShellClient<SD>,
},
/// A new toplevel surface was created
///
/// You likely need to send a `ToplevelConfigure` to the surface, to hint the
/// client as to how its window should be
NewToplevel {
/// the surface
surface: ToplevelSurface<U, R, SD>,
},
/// A new popup surface was created
///
/// You likely need to send a `PopupConfigure` to the surface, to hint the
/// client as to how its popup should be
NewPopup {
/// the surface
surface: PopupSurface<U, R, SD>,
},
/// The client requested the start of an interactive move for this surface
Move {
/// the surface
surface: ToplevelSurface<U, R, SD>,
/// the seat associated to this move
seat: Resource<wl_seat::WlSeat>,
/// the grab serial
serial: u32,
},
/// The client requested the start of an interactive resize for this surface
Resize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
/// The seat associated with this resize
seat: Resource<wl_seat::WlSeat>,
/// The grab serial
serial: u32,
/// Specification of which part of the window's border is being dragged
edges: xdg_toplevel::ResizeEdge,
},
/// This popup requests a grab of the pointer
///
/// This means it requests to be sent a `popup_done` event when the pointer leaves
/// the grab area.
Grab {
/// The surface
surface: PopupSurface<U, R, SD>,
/// The seat to grab
seat: Resource<wl_seat::WlSeat>,
/// The grab serial
serial: u32,
},
/// A toplevel surface requested to be maximized
Maximize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// A toplevel surface requested to stop being maximized
UnMaximize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// A toplevel surface requested to be set fullscreen
Fullscreen {
/// The surface
surface: ToplevelSurface<U, R, SD>,
/// The output (if any) on which the fullscreen is requested
output: Option<Resource<wl_output::WlOutput>>,
},
/// A toplevel surface request to stop being fullscreen
UnFullscreen {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// A toplevel surface requested to be minimized
Minimize {
/// The surface
surface: ToplevelSurface<U, R, SD>,
},
/// The client requests the window menu to be displayed on this surface at this location
///
/// This menu belongs to the compositor. It is typically expected to contain options for
/// control of the window (maximize/minimize/close/move/etc...).
ShowWindowMenu {
/// The surface
surface: ToplevelSurface<U, R, SD>,
/// The seat associated with this input grab
seat: Resource<wl_seat::WlSeat>,
/// the grab serial
serial: u32,
/// location of the menu request
location: (i32, i32),
},
}

View File

@ -0,0 +1,720 @@
use super::{make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
ShellClientData, ShellImplementation, ToplevelConfigure, ToplevelKind, ToplevelState,
XdgRequest, XdgSurfacePendingState, XdgSurfaceRole};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::xdg_shell::server::{xdg_popup, xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::{downcast_impl, Implementation};
use wayland_server::protocol::wl_surface;
pub(crate) fn implement_wm_base<U, R, SD>(
shell: NewResource<xdg_wm_base::XdgWmBase>,
implem: &ShellImplementation<U, R, SD>,
) -> Resource<xdg_wm_base::XdgWmBase>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: Default + 'static,
{
let shell = shell.implement_nonsend(
implem.clone(),
Some(|shell, _| destroy_shell::<SD>(&shell)),
&implem.loop_token,
);
shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::<SD>()))) as *mut _);
let mut user_impl = implem.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::NewClient {
client: make_shell_client(&shell),
},
(),
);
shell
}
/*
* xdg_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
fn destroy_shell<SD>(shell: &Resource<xdg_wm_base::XdgWmBase>) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
pub(crate) fn make_shell_client<SD>(resource: &Resource<xdg_wm_base::XdgWmBase>) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::Xdg(resource.clone()),
_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<xdg_wm_base::XdgWmBase>, xdg_wm_base::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_wm_base::Request, shell: Resource<xdg_wm_base::XdgWmBase>) {
match request {
xdg_wm_base::Request::Destroy => {
// all is handled by destructor
}
xdg_wm_base::Request::CreatePositioner { id } => {
implement_positioner(id, &self.loop_token);
}
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
let role_data = XdgSurfaceRole {
pending_state: XdgSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: false,
};
if self.compositor_token
.give_role_with(&surface, role_data)
.is_err()
{
shell.post_error(
xdg_wm_base::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
let xdg_surface = id.implement_nonsend(
self.clone(),
Some(destroy_surface::<U, R, SD>),
&self.loop_token,
);
xdg_surface
.set_user_data(Box::into_raw(Box::new((surface.clone(), shell.clone()))) as *mut _);
}
xdg_wm_base::Request::Pong { serial } => {
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == serial {
guard.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ClientPong {
client: make_shell_client(&shell),
},
(),
);
}
}
}
}
}
/*
* xdg_positioner
*/
fn destroy_positioner(positioner: &Resource<xdg_positioner::XdgPositioner>) {
let ptr = positioner.get_user_data();
positioner.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn implement_positioner(
positioner: NewResource<xdg_positioner::XdgPositioner>,
token: &LoopToken,
) -> Resource<xdg_positioner::XdgPositioner> {
let positioner = positioner.implement_nonsend(
|request, positioner: Resource<_>| {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
match request {
xdg_positioner::Request::Destroy => {
// handled by destructor
}
xdg_positioner::Request::SetSize { width, height } => {
if width < 1 || height < 1 {
positioner.post_error(
xdg_positioner::Error::InvalidInput as u32,
"Invalid size for positioner.".into(),
);
} else {
state.rect_size = (width, height);
}
}
xdg_positioner::Request::SetAnchorRect {
x,
y,
width,
height,
} => {
if width < 1 || height < 1 {
positioner.post_error(
xdg_positioner::Error::InvalidInput as u32,
"Invalid size for positioner's anchor rectangle.".into(),
);
} else {
state.anchor_rect = Rectangle {
x,
y,
width,
height,
};
}
}
xdg_positioner::Request::SetAnchor { anchor } => {
state.anchor_edges = anchor;
}
xdg_positioner::Request::SetGravity { gravity } => {
state.gravity = gravity;
}
xdg_positioner::Request::SetConstraintAdjustment {
constraint_adjustment,
} => {
let constraint_adjustment =
xdg_positioner::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
state.constraint_adjustment = constraint_adjustment;
}
xdg_positioner::Request::SetOffset { x, y } => {
state.offset = (x, y);
}
}
},
Some(|positioner, _| destroy_positioner(&positioner)),
token,
);
let data = PositionerState::new();
positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _);
positioner
}
/*
* xdg_surface
*/
type XdgSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<xdg_wm_base::XdgWmBase>,
);
fn destroy_surface<U, R, SD>(
surface: Resource<xdg_surface::XdgSurface>,
implem: Box<Implementation<Resource<xdg_surface::XdgSurface>, xdg_surface::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = unsafe { Box::from_raw(ptr as *mut XdgSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
}
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |rdata| {
if let XdgSurfacePendingState::None = rdata.pending_state {
// all is good
} else {
data.1.post_error(
xdg_wm_base::Error::Role as u32,
"xdg_surface was destroyed before its role object".into(),
);
}
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
impl<U, R, SD> Implementation<Resource<xdg_surface::XdgSurface>, xdg_surface::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_surface::Request, xdg_surface: Resource<xdg_surface::XdgSurface>) {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) = unsafe { &*(ptr as *mut XdgSurfaceUserData) };
match request {
xdg_surface::Request::Destroy => {
// all is handled by our destructor
}
xdg_surface::Request::GetToplevel { id } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let toplevel = id.implement_nonsend(
self.clone(),
Some(destroy_toplevel::<U, R, SD>),
&self.loop_token,
);
toplevel.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_toplevels
.push(make_toplevel_handle(self.compositor_token, &toplevel));
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewToplevel { surface: handle }, ());
}
xdg_surface::Request::GetPopup {
id,
parent,
positioner,
} => {
let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) };
let parent_surface = parent.map(|parent| {
let parent_ptr = parent.get_user_data();
let &(ref parent_surface, _) = unsafe { &*(parent_ptr as *mut XdgSurfaceUserData) };
parent_surface.clone()
});
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
parent: parent_surface,
positioner: positioner_data.clone(),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let popup = id.implement_nonsend(
self.clone(),
Some(destroy_popup::<U, R, SD>),
&self.loop_token,
);
popup.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_popups
.push(make_popup_handle(self.compositor_token, &popup));
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewPopup { surface: handle }, ());
}
xdg_surface::Request::SetWindowGeometry {
x,
y,
width,
height,
} => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.window_geometry = Some(Rectangle {
x,
y,
width,
height,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
xdg_surface::Request::AckConfigure { serial } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
let mut found = false;
data.pending_configures.retain(|&s| {
if s == serial {
found = true;
}
s > serial
});
if !found {
// client responded to a non-existing configure
shell.post_error(
xdg_wm_base::Error::InvalidSurfaceState as u32,
format!("Wrong configure serial: {}", serial),
);
}
data.configured = true;
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
}
}
}
/*
* xdg_toplevel
*/
pub type ShellSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<xdg_wm_base::XdgWmBase>,
Resource<xdg_surface::XdgSurface>,
);
// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_data<U, R, SD, F>(
implem: &ShellImplementation<U, R, SD>,
toplevel: &Resource<xdg_toplevel::XdgToplevel>,
f: F,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
F: FnOnce(&mut ToplevelState),
{
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| match data.pending_state {
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
_ => unreachable!(),
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
pub fn send_toplevel_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_toplevel::XdgToplevel>,
configure: ToplevelConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (width, height) = configure.size.unwrap_or((0, 0));
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
let states = {
let mut states = configure.states;
let ptr = states.as_mut_ptr();
let len = states.len();
let cap = states.capacity();
::std::mem::forget(states);
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
};
let serial = configure.serial;
resource.send(xdg_toplevel::Event::Configure {
width,
height,
states,
});
shell_surface.send(xdg_surface::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_toplevel_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_toplevel::XdgToplevel>,
) -> super::ToplevelSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: wl_surface.clone(),
shell_surface: ToplevelKind::Xdg(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<xdg_toplevel::XdgToplevel>, xdg_toplevel::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_toplevel::Request, toplevel: Resource<xdg_toplevel::XdgToplevel>) {
match request {
xdg_toplevel::Request::Destroy => {
// all it done by the destructor
}
xdg_toplevel::Request::SetParent { parent } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
let parent_ptr = toplevel_surface_parent.get_user_data();
let &(ref parent_surface, _, _) =
unsafe { &*(parent_ptr as *mut ShellSurfaceUserData) };
parent_surface.clone()
})
});
}
xdg_toplevel::Request::SetTitle { title } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.title = title;
});
}
xdg_toplevel::Request::SetAppId { app_id } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.app_id = app_id;
});
}
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ShowWindowMenu {
surface: handle,
seat,
serial,
location: (x, y),
},
(),
);
}
xdg_toplevel::Request::Move { seat, serial } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Move {
surface: handle,
seat,
serial,
},
(),
);
}
xdg_toplevel::Request::Resize {
seat,
serial,
edges,
} => {
let edges =
xdg_toplevel::ResizeEdge::from_raw(edges).unwrap_or(xdg_toplevel::ResizeEdge::None);
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Resize {
surface: handle,
seat,
serial,
edges,
},
(),
);
}
xdg_toplevel::Request::SetMaxSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
xdg_toplevel::Request::SetMinSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
xdg_toplevel::Request::SetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Maximize { surface: handle }, ());
}
xdg_toplevel::Request::UnsetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnMaximize { surface: handle }, ());
}
xdg_toplevel::Request::SetFullscreen { output } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Fullscreen {
surface: handle,
output,
},
(),
);
}
xdg_toplevel::Request::UnsetFullscreen => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnFullscreen { surface: handle }, ());
}
xdg_toplevel::Request::SetMinimized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Minimize { surface: handle }, ());
}
}
}
}
fn destroy_toplevel<U, R, SD>(
toplevel: Resource<xdg_toplevel::XdgToplevel>,
implem: Box<Implementation<Resource<xdg_toplevel::XdgToplevel>, xdg_toplevel::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = toplevel.get_user_data();
toplevel.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_toplevels
.retain(|other| other.alive());
}
/*
* xdg_popup
*/
pub(crate) fn send_popup_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_popup::XdgPopup>,
configure: PopupConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (x, y) = configure.position;
let (width, height) = configure.size;
let serial = configure.serial;
resource.send(xdg_popup::Event::Configure {
x,
y,
width,
height,
});
shell_surface.send(xdg_surface::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_popup_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<xdg_popup::XdgPopup>,
) -> super::PopupSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: wl_surface.clone(),
shell_surface: PopupKind::Xdg(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<xdg_popup::XdgPopup>, xdg_popup::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: xdg_popup::Request, popup: Resource<xdg_popup::XdgPopup>) {
match request {
xdg_popup::Request::Destroy => {
// all is handled by our destructor
}
xdg_popup::Request::Grab { seat, serial } => {
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Grab {
surface: handle,
seat,
serial,
},
(),
);
}
}
}
}
fn destroy_popup<U, R, SD>(
popup: Resource<xdg_popup::XdgPopup>,
implem: Box<Implementation<Resource<xdg_popup::XdgPopup>, xdg_popup::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = popup.get_user_data();
popup.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_popup exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_popups
.retain(|other| other.alive());
}

View File

@ -0,0 +1,791 @@
use super::{make_shell_client_data, PopupConfigure, PopupKind, PopupState, PositionerState, ShellClient,
ShellClientData, ShellImplementation, ToplevelConfigure, ToplevelKind, ToplevelState,
XdgRequest, XdgSurfacePendingState, XdgSurfaceRole};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::xdg_shell::server::{xdg_positioner, xdg_toplevel};
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{LoopToken, NewResource, Resource};
use wayland_server::commons::{downcast_impl, Implementation};
use wayland_server::protocol::wl_surface;
pub(crate) fn implement_shell<U, R, SD>(
shell: NewResource<zxdg_shell_v6::ZxdgShellV6>,
implem: &ShellImplementation<U, R, SD>,
) -> Resource<zxdg_shell_v6::ZxdgShellV6>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: Default + 'static,
{
let shell = shell.implement_nonsend(
implem.clone(),
Some(|shell, _| destroy_shell::<SD>(&shell)),
&implem.loop_token,
);
shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::<SD>()))) as *mut _);
let mut user_impl = implem.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::NewClient {
client: make_shell_client(&shell),
},
(),
);
shell
}
/*
* xdg_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
fn destroy_shell<SD>(shell: &Resource<zxdg_shell_v6::ZxdgShellV6>) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
pub(crate) fn make_shell_client<SD>(resource: &Resource<zxdg_shell_v6::ZxdgShellV6>) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::ZxdgV6(resource.clone()),
_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<zxdg_shell_v6::ZxdgShellV6>, zxdg_shell_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: zxdg_shell_v6::Request, shell: Resource<zxdg_shell_v6::ZxdgShellV6>) {
match request {
zxdg_shell_v6::Request::Destroy => {
// all is handled by destructor
}
zxdg_shell_v6::Request::CreatePositioner { id } => {
implement_positioner(id, &self.loop_token);
}
zxdg_shell_v6::Request::GetXdgSurface { id, surface } => {
let role_data = XdgSurfaceRole {
pending_state: XdgSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: false,
};
if self.compositor_token
.give_role_with(&surface, role_data)
.is_err()
{
shell.post_error(
zxdg_shell_v6::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
let xdg_surface = id.implement_nonsend(
self.clone(),
Some(destroy_surface::<U, R, SD>),
&self.loop_token,
);
xdg_surface
.set_user_data(Box::into_raw(Box::new((surface.clone(), shell.clone()))) as *mut _);
}
zxdg_shell_v6::Request::Pong { serial } => {
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == serial {
guard.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ClientPong {
client: make_shell_client(&shell),
},
(),
);
}
}
}
}
}
/*
* xdg_positioner
*/
fn destroy_positioner(positioner: &Resource<zxdg_positioner_v6::ZxdgPositionerV6>) {
let ptr = positioner.get_user_data();
positioner.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn implement_positioner(
positioner: NewResource<zxdg_positioner_v6::ZxdgPositionerV6>,
token: &LoopToken,
) -> Resource<zxdg_positioner_v6::ZxdgPositionerV6> {
let positioner = positioner.implement_nonsend(
|request, positioner: Resource<_>| {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
match request {
zxdg_positioner_v6::Request::Destroy => {
// handled by destructor
}
zxdg_positioner_v6::Request::SetSize { width, height } => {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner.".into(),
);
} else {
state.rect_size = (width, height);
}
}
zxdg_positioner_v6::Request::SetAnchorRect {
x,
y,
width,
height,
} => {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner's anchor rectangle.".into(),
);
} else {
state.anchor_rect = Rectangle {
x,
y,
width,
height,
};
}
}
zxdg_positioner_v6::Request::SetAnchor { anchor } => {
if let Some(anchor) = zxdg_anchor_to_xdg(anchor) {
state.anchor_edges = anchor;
} else {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid anchor for positioner.".into(),
);
}
}
zxdg_positioner_v6::Request::SetGravity { gravity } => {
if let Some(gravity) = zxdg_gravity_to_xdg(gravity) {
state.gravity = gravity;
} else {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid gravity for positioner.".into(),
);
}
}
zxdg_positioner_v6::Request::SetConstraintAdjustment {
constraint_adjustment,
} => {
let constraint_adjustment =
zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
state.constraint_adjustment = zxdg_constraints_adg_to_xdg(constraint_adjustment);
}
zxdg_positioner_v6::Request::SetOffset { x, y } => {
state.offset = (x, y);
}
}
},
Some(|positioner, _| destroy_positioner(&positioner)),
token,
);
let data = PositionerState::new();
positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _);
positioner
}
/*
* xdg_surface
*/
type XdgSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<zxdg_shell_v6::ZxdgShellV6>,
);
fn destroy_surface<U, R, SD>(
surface: Resource<zxdg_surface_v6::ZxdgSurfaceV6>,
implem: Box<Implementation<Resource<zxdg_surface_v6::ZxdgSurfaceV6>, zxdg_surface_v6::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = unsafe { Box::from_raw(ptr as *mut XdgSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
}
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |rdata| {
if let XdgSurfacePendingState::None = rdata.pending_state {
// all is good
} else {
data.1.post_error(
zxdg_shell_v6::Error::Role as u32,
"xdg_surface was destroyed before its role object".into(),
);
}
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
impl<U, R, SD> Implementation<Resource<zxdg_surface_v6::ZxdgSurfaceV6>, zxdg_surface_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(
&mut self,
request: zxdg_surface_v6::Request,
xdg_surface: Resource<zxdg_surface_v6::ZxdgSurfaceV6>,
) {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) = unsafe { &*(ptr as *mut XdgSurfaceUserData) };
match request {
zxdg_surface_v6::Request::Destroy => {
// all is handled by our destructor
}
zxdg_surface_v6::Request::GetToplevel { id } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let toplevel = id.implement_nonsend(
self.clone(),
Some(destroy_toplevel::<U, R, SD>),
&self.loop_token,
);
toplevel.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_toplevels
.push(make_toplevel_handle(self.compositor_token, &toplevel));
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewToplevel { surface: handle }, ());
}
zxdg_surface_v6::Request::GetPopup {
id,
parent,
positioner,
} => {
let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) };
let parent_ptr = parent.get_user_data();
let &(ref parent_surface, _) = unsafe { &*(parent_ptr as *mut XdgSurfaceUserData) };
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.pending_state = XdgSurfacePendingState::Popup(PopupState {
parent: Some(parent_surface.clone()),
positioner: positioner_data.clone(),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
let popup = id.implement_nonsend(
self.clone(),
Some(destroy_popup::<U, R, SD>),
&self.loop_token,
);
popup.set_user_data(Box::into_raw(Box::new((
surface.clone(),
shell.clone(),
xdg_surface.clone(),
))) as *mut _);
self.shell_state
.lock()
.unwrap()
.known_popups
.push(make_popup_handle(self.compositor_token, &popup));
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::NewPopup { surface: handle }, ());
}
zxdg_surface_v6::Request::SetWindowGeometry {
x,
y,
width,
height,
} => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
data.window_geometry = Some(Rectangle {
x,
y,
width,
height,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
zxdg_surface_v6::Request::AckConfigure { serial } => {
self.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| {
let mut found = false;
data.pending_configures.retain(|&s| {
if s == serial {
found = true;
}
s > serial
});
if !found {
// client responded to a non-existing configure
shell.post_error(
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
format!("Wrong configure serial: {}", serial),
);
}
data.configured = true;
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
}
}
}
}
/*
* xdg_toplevel
*/
pub type ShellSurfaceUserData = (
Resource<wl_surface::WlSurface>,
Resource<zxdg_shell_v6::ZxdgShellV6>,
Resource<zxdg_surface_v6::ZxdgSurfaceV6>,
);
// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_data<U, R, SD, F>(
implem: &ShellImplementation<U, R, SD>,
toplevel: &Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
f: F,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
F: FnOnce(&mut ToplevelState),
{
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| match data.pending_state {
XdgSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
_ => unreachable!(),
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
pub fn send_toplevel_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
configure: ToplevelConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (width, height) = configure.size.unwrap_or((0, 0));
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
let states = {
let mut states = configure.states;
let ptr = states.as_mut_ptr();
let len = states.len();
let cap = states.capacity();
::std::mem::forget(states);
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
};
let serial = configure.serial;
resource.send(zxdg_toplevel_v6::Event::Configure {
width,
height,
states,
});
shell_surface.send(zxdg_surface_v6::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_toplevel_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
) -> super::ToplevelSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: wl_surface.clone(),
shell_surface: ToplevelKind::ZxdgV6(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<zxdg_toplevel_v6::ZxdgToplevelV6>, zxdg_toplevel_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(
&mut self,
request: zxdg_toplevel_v6::Request,
toplevel: Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
) {
match request {
zxdg_toplevel_v6::Request::Destroy => {
// all it done by the destructor
}
zxdg_toplevel_v6::Request::SetParent { parent } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
let parent_ptr = toplevel_surface_parent.get_user_data();
let &(ref parent_surface, _, _) =
unsafe { &*(parent_ptr as *mut ShellSurfaceUserData) };
parent_surface.clone()
})
});
}
zxdg_toplevel_v6::Request::SetTitle { title } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.title = title;
});
}
zxdg_toplevel_v6::Request::SetAppId { app_id } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.app_id = app_id;
});
}
zxdg_toplevel_v6::Request::ShowWindowMenu { seat, serial, x, y } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::ShowWindowMenu {
surface: handle,
seat,
serial,
location: (x, y),
},
(),
);
}
zxdg_toplevel_v6::Request::Move { seat, serial } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Move {
surface: handle,
seat,
serial,
},
(),
);
}
zxdg_toplevel_v6::Request::Resize {
seat,
serial,
edges,
} => {
let edges = zxdg_toplevel_v6::ResizeEdge::from_raw(edges)
.unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Resize {
surface: handle,
seat,
serial,
edges: zxdg_edges_to_xdg(edges),
},
(),
);
}
zxdg_toplevel_v6::Request::SetMaxSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
zxdg_toplevel_v6::Request::SetMinSize { width, height } => {
with_surface_toplevel_data(self, &toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
});
}
zxdg_toplevel_v6::Request::SetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Maximize { surface: handle }, ());
}
zxdg_toplevel_v6::Request::UnsetMaximized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnMaximize { surface: handle }, ());
}
zxdg_toplevel_v6::Request::SetFullscreen { output } => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Fullscreen {
surface: handle,
output,
},
(),
);
}
zxdg_toplevel_v6::Request::UnsetFullscreen => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::UnFullscreen { surface: handle }, ());
}
zxdg_toplevel_v6::Request::SetMinimized => {
let handle = make_toplevel_handle(self.compositor_token, &toplevel);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(XdgRequest::Minimize { surface: handle }, ());
}
}
}
}
fn destroy_toplevel<U, R, SD>(
toplevel: Resource<zxdg_toplevel_v6::ZxdgToplevelV6>,
implem: Box<Implementation<Resource<zxdg_toplevel_v6::ZxdgToplevelV6>, zxdg_toplevel_v6::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = toplevel.get_user_data();
toplevel.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_toplevels
.retain(|other| other.alive());
}
/*
* xdg_popup
*/
pub(crate) fn send_popup_configure<U, R>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_popup_v6::ZxdgPopupV6>,
configure: PopupConfigure,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (x, y) = configure.position;
let (width, height) = configure.size;
let serial = configure.serial;
resource.send(zxdg_popup_v6::Event::Configure {
x,
y,
width,
height,
});
shell_surface.send(zxdg_surface_v6::Event::Configure { serial });
// Add the configure as pending
token
.with_role_data::<XdgSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_popup_handle<U, R, SD>(
token: CompositorToken<U, R>,
resource: &Resource<zxdg_popup_v6::ZxdgPopupV6>,
) -> super::PopupSurface<U, R, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: wl_surface.clone(),
shell_surface: PopupKind::ZxdgV6(resource.clone()),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
impl<U, R, SD> Implementation<Resource<zxdg_popup_v6::ZxdgPopupV6>, zxdg_popup_v6::Request>
for ShellImplementation<U, R, SD>
where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
fn receive(&mut self, request: zxdg_popup_v6::Request, popup: Resource<zxdg_popup_v6::ZxdgPopupV6>) {
match request {
zxdg_popup_v6::Request::Destroy => {
// all is handled by our destructor
}
zxdg_popup_v6::Request::Grab { seat, serial } => {
let handle = make_popup_handle(self.compositor_token, &popup);
let mut user_impl = self.user_impl.borrow_mut();
user_impl.receive(
XdgRequest::Grab {
surface: handle,
seat,
serial,
},
(),
);
}
}
}
}
fn destroy_popup<U, R, SD>(
popup: Resource<zxdg_popup_v6::ZxdgPopupV6>,
implem: Box<Implementation<Resource<zxdg_popup_v6::ZxdgPopupV6>, zxdg_popup_v6::Request>>,
) where
U: 'static,
R: Role<XdgSurfaceRole> + 'static,
SD: 'static,
{
let implem: ShellImplementation<U, R, SD> = *downcast_impl(implem).unwrap_or_else(|_| unreachable!());
let ptr = popup.get_user_data();
popup.set_user_data(::std::ptr::null_mut());
// take back ownership of the userdata
let data = *unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
if !data.0.is_alive() {
// the wl_surface is destroyed, this means the client is not
// trying to change the role but it's a cleanup (possibly a
// disconnecting client), ignore the protocol check.
return;
} else {
implem
.compositor_token
.with_role_data::<XdgSurfaceRole, _, _>(&data.0, |data| {
data.pending_state = XdgSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_popup exists but surface has not shell_surface role?!");
}
// remove this surface from the known ones (as well as any leftover dead surface)
implem
.shell_state
.lock()
.unwrap()
.known_popups
.retain(|other| other.alive());
}
fn zxdg_edges_to_xdg(e: zxdg_toplevel_v6::ResizeEdge) -> xdg_toplevel::ResizeEdge {
match e {
zxdg_toplevel_v6::ResizeEdge::None => xdg_toplevel::ResizeEdge::None,
zxdg_toplevel_v6::ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
zxdg_toplevel_v6::ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
zxdg_toplevel_v6::ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
zxdg_toplevel_v6::ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
zxdg_toplevel_v6::ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
zxdg_toplevel_v6::ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
zxdg_toplevel_v6::ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
zxdg_toplevel_v6::ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
}
}
fn zxdg_constraints_adg_to_xdg(
c: zxdg_positioner_v6::ConstraintAdjustment,
) -> xdg_positioner::ConstraintAdjustment {
xdg_positioner::ConstraintAdjustment::from_bits_truncate(c.bits())
}
fn zxdg_gravity_to_xdg(c: zxdg_positioner_v6::Gravity) -> Option<xdg_positioner::Gravity> {
match c.bits() {
0b0000 => Some(xdg_positioner::Gravity::None),
0b0001 => Some(xdg_positioner::Gravity::Top),
0b0010 => Some(xdg_positioner::Gravity::Bottom),
0b0100 => Some(xdg_positioner::Gravity::Left),
0b0101 => Some(xdg_positioner::Gravity::TopLeft),
0b0110 => Some(xdg_positioner::Gravity::BottomLeft),
0b1000 => Some(xdg_positioner::Gravity::Right),
0b1001 => Some(xdg_positioner::Gravity::TopRight),
0b1010 => Some(xdg_positioner::Gravity::BottomRight),
_ => None,
}
}
fn zxdg_anchor_to_xdg(c: zxdg_positioner_v6::Anchor) -> Option<xdg_positioner::Anchor> {
match c.bits() {
0b0000 => Some(xdg_positioner::Anchor::None),
0b0001 => Some(xdg_positioner::Anchor::Top),
0b0010 => Some(xdg_positioner::Anchor::Bottom),
0b0100 => Some(xdg_positioner::Anchor::Left),
0b0101 => Some(xdg_positioner::Anchor::TopLeft),
0b0110 => Some(xdg_positioner::Anchor::BottomLeft),
0b1000 => Some(xdg_positioner::Anchor::Right),
0b1001 => Some(xdg_positioner::Anchor::TopRight),
0b1010 => Some(xdg_positioner::Anchor::BottomRight),
_ => None,
}
}

View File

@ -1,697 +0,0 @@
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelConfigure, ToplevelState};
use std::sync::Mutex;
use utils::Rectangle;
use wayland::compositor::CompositorToken;
use wayland::compositor::roles::*;
use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle, Resource};
use wayland_server::protocol::{wl_output, wl_surface};
pub(crate) fn xdg_shell_bind<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>, _: &Client,
shell: zxdg_shell_v6::ZxdgShellV6,
) where
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>()))) 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));
}
/*
* xdg_shell
*/
pub(crate) type ShellUserData<SD> = Mutex<ShellClientData<SD>>;
fn destroy_shell<SD>(shell: &zxdg_shell_v6::ZxdgShellV6) {
let ptr = shell.get_user_data();
shell.set_user_data(::std::ptr::null_mut());
let data = unsafe { Box::from_raw(ptr as *mut ShellUserData<SD>) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
pub(crate) fn make_shell_client<SD>(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient<SD> {
ShellClient {
kind: super::ShellClientKind::Xdg(unsafe { resource.clone_unchecked() }),
_data: ::std::marker::PhantomData,
}
}
fn shell_implementation<U, R, CID, SID, SD>(
) -> zxdg_shell_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_shell_v6::Implementation {
destroy: |_, _, _, _| {},
create_positioner: |evlh, _, _, _, positioner| {
let data = PositionerState {
rect_size: (0, 0),
anchor_rect: Rectangle {
x: 0,
y: 0,
width: 0,
height: 0,
},
anchor_edges: zxdg_positioner_v6::Anchor::empty(),
gravity: zxdg_positioner_v6::Gravity::empty(),
constraint_adjustment: zxdg_positioner_v6::ConstraintAdjustment::empty(),
offset: (0, 0),
};
positioner.set_user_data(Box::into_raw(Box::new(data)) as *mut _);
evlh.register(
&positioner,
positioner_implementation(),
(),
Some(destroy_positioner),
);
},
get_xdg_surface: |evlh, idata, _, shell, xdg_surface, wl_surface| {
let role_data = ShellSurfaceRole {
pending_state: ShellSurfacePendingState::None,
window_geometry: None,
pending_configures: Vec::new(),
configured: false,
};
if idata
.compositor_token
.give_role_with(wl_surface, role_data)
.is_err()
{
shell.post_error(
zxdg_shell_v6::Error::Role as u32,
"Surface already has a role.".into(),
);
return;
}
xdg_surface.set_user_data(Box::into_raw(Box::new((
unsafe { wl_surface.clone_unchecked() },
unsafe { shell.clone_unchecked() },
))) as *mut _);
evlh.register(
&xdg_surface,
surface_implementation(),
idata.clone(),
Some(destroy_surface),
);
},
pong: |evlh, idata, _, shell, serial| {
let valid = {
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
let mut guard = mutex.lock().unwrap();
if guard.pending_ping == serial {
guard.pending_ping = 0;
true
} else {
false
}
};
if valid {
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.client_pong)(evlh, &mut *user_idata, make_shell_client(shell));
}
},
}
}
/*
* xdg_positioner
*/
fn destroy_positioner(positioner: &zxdg_positioner_v6::ZxdgPositionerV6) {
let ptr = positioner.get_user_data();
positioner.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let surface = unsafe { Box::from_raw(ptr as *mut PositionerState) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(surface);
}
fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> {
zxdg_positioner_v6::Implementation {
destroy: |_, _, _, _| {},
set_size: |_, _, _, positioner, width, height| {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.rect_size = (width, height);
}
},
set_anchor_rect: |_, _, _, positioner, x, y, width, height| {
if width < 1 || height < 1 {
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid size for positioner's anchor rectangle.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.anchor_rect = Rectangle {
x,
y,
width,
height,
};
}
},
set_anchor: |_, _, _, positioner, anchor| {
use self::zxdg_positioner_v6::Anchor;
if anchor.contains(Anchor::Left | Anchor::Right) || anchor.contains(Anchor::Top | Anchor::Bottom)
{
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid anchor for positioner.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.anchor_edges = anchor;
}
},
set_gravity: |_, _, _, positioner, gravity| {
use self::zxdg_positioner_v6::Gravity;
if gravity.contains(Gravity::Left | Gravity::Right)
|| gravity.contains(Gravity::Top | Gravity::Bottom)
{
positioner.post_error(
zxdg_positioner_v6::Error::InvalidInput as u32,
"Invalid gravity for positioner.".into(),
);
} else {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.gravity = gravity;
}
},
set_constraint_adjustment: |_, _, _, positioner, constraint_adjustment| {
let constraint_adjustment =
zxdg_positioner_v6::ConstraintAdjustment::from_bits_truncate(constraint_adjustment);
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.constraint_adjustment = constraint_adjustment;
},
set_offset: |_, _, _, positioner, x, y| {
let ptr = positioner.get_user_data();
let state = unsafe { &mut *(ptr as *mut PositionerState) };
state.offset = (x, y);
},
}
}
/*
* xdg_surface
*/
fn destroy_surface(surface: &zxdg_surface_v6::ZxdgSurfaceV6) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// drop the state
let data =
unsafe { Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6)) };
// explicit call to drop to not forget what we're doing here
::std::mem::drop(data);
}
fn surface_implementation<U, R, CID, SID, SD>(
) -> zxdg_surface_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_surface_v6::Implementation {
destroy: |_, idata, _, xdg_surface| {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
if let ShellSurfacePendingState::None = data.pending_state {
// all is good
} else {
shell.post_error(
zxdg_shell_v6::Error::Role as u32,
"xdg_surface was destroyed before its role object".into(),
);
}
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
},
get_toplevel: |evlh, idata, _, xdg_surface, toplevel| {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::Toplevel(ToplevelState {
parent: None,
title: String::new(),
app_id: String::new(),
min_size: (0, 0),
max_size: (0, 0),
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
toplevel.set_user_data(Box::into_raw(Box::new(unsafe {
(
surface.clone_unchecked(),
shell.clone_unchecked(),
xdg_surface.clone_unchecked(),
)
})) as *mut _);
evlh.register(
&toplevel,
toplevel_implementation(),
idata.clone(),
Some(destroy_toplevel),
);
// register to self
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.push(make_toplevel_handle(idata.compositor_token, &toplevel));
// intial configure event
let handle = make_toplevel_handle(idata.compositor_token, &toplevel);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_toplevel)(evlh, &mut *user_idata, handle);
send_toplevel_configure(idata.compositor_token, &toplevel, configure);
},
get_popup: |evlh, idata, _, xdg_surface, popup, parent, positioner| {
let ptr = xdg_surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
let positioner_data = unsafe { &*(positioner.get_user_data() as *const PositionerState) };
let parent_ptr = parent.get_user_data();
let &(ref parent_surface, _) =
unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::Popup(PopupState {
parent: unsafe { parent_surface.clone_unchecked() },
positioner: *positioner_data,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
popup.set_user_data(Box::into_raw(Box::new(unsafe {
(
surface.clone_unchecked(),
shell.clone_unchecked(),
xdg_surface.clone_unchecked(),
)
})) as *mut _);
evlh.register(
&popup,
popup_implementation(),
idata.clone(),
Some(destroy_popup),
);
// register to self
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.push(make_popup_handle(idata.compositor_token, &popup));
// intial configure event
let handle = make_popup_handle(idata.compositor_token, &popup);
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.new_popup)(evlh, &mut *user_idata, handle);
send_popup_configure(idata.compositor_token, &popup, configure);
},
set_window_geometry: |_, idata, _, surface, x, y, width, height| {
let ptr = surface.get_user_data();
let &(ref surface, _) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.window_geometry = Some(Rectangle {
x,
y,
width,
height,
});
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
},
ack_configure: |_, idata, _, surface, serial| {
let ptr = surface.get_user_data();
let &(ref surface, ref shell) =
unsafe { &*(ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
let mut found = false;
data.pending_configures.retain(|&s| {
if s == serial {
found = true;
}
s > serial
});
if !found {
// client responded to a non-existing configure
shell.post_error(
zxdg_shell_v6::Error::InvalidSurfaceState as u32,
format!("Wrong configure serial: {}", serial),
);
}
data.configured = true;
})
.expect("xdg_surface exists but surface has not shell_surface role?!");
},
}
}
/*
* xdg_toplevel
*/
pub type ShellSurfaceUserData = (
wl_surface::WlSurface,
zxdg_shell_v6::ZxdgShellV6,
zxdg_surface_v6::ZxdgSurfaceV6,
);
fn destroy_toplevel(surface: &zxdg_toplevel_v6::ZxdgToplevelV6) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
// explicit call to drop to not forget what we're doing there
::std::mem::drop(data);
}
// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_data<U, R, CID, SID, SD, F>(
idata: &ShellSurfaceIData<U, R, CID, SID, SD>, toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
F: FnOnce(&mut ToplevelState),
{
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| match data.pending_state {
ShellSurfacePendingState::Toplevel(ref mut toplevel_data) => f(toplevel_data),
_ => unreachable!(),
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn xdg_handle_display_state_change<U, R, CID, SID, SD>(
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, maximized: Option<bool>, minimized: Option<bool>,
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
// handler callback
let mut user_idata = idata.idata.borrow_mut();
let configure = (idata.implementation.change_display_state)(
evlh,
&mut *user_idata,
handle,
maximized,
minimized,
fullscreen,
output,
);
// send the configure response to client
send_toplevel_configure(idata.compositor_token, toplevel, configure);
}
pub fn send_toplevel_configure<U, R, ID>(
token: CompositorToken<U, R, ID>, resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
configure: ToplevelConfigure,
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
ID: 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (w, h) = configure.size.unwrap_or((0, 0));
// convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
let states = {
let mut states = configure.states;
let ptr = states.as_mut_ptr();
let len = states.len();
let cap = states.capacity();
::std::mem::forget(states);
unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
};
let serial = configure.serial;
resource.configure(w, h, states);
shell_surface.configure(serial);
// Add the configure as pending
token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_toplevel_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &zxdg_toplevel_v6::ZxdgToplevelV6
) -> super::ToplevelSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::ToplevelSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::XdgToplevel(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
fn toplevel_implementation<U, R, CID, SID, SD>(
) -> zxdg_toplevel_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_toplevel_v6::Implementation {
destroy: |evlh, idata, _, toplevel| {
let ptr = toplevel.get_user_data();
let &(ref surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
// remove this surface from the known ones (as well as any leftover dead surface)
evlh.state()
.get_mut(&idata.state_token)
.known_toplevels
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(surface))
.unwrap_or(false)
});
},
set_parent: |_, idata, _, toplevel, parent| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
let parent_ptr = toplevel_surface_parent.get_user_data();
let &(ref parent_surface, _) =
unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
unsafe { parent_surface.clone_unchecked() }
})
});
},
set_title: |_, idata, _, toplevel, title| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.title = title;
});
},
set_app_id: |_, idata, _, toplevel, app_id| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.app_id = app_id;
});
},
show_window_menu: |evlh, idata, _, toplevel, seat, serial, x, y| {
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.show_window_menu)(evlh, &mut *user_idata, handle, seat, serial, x, y)
},
move_: |evlh, idata, _, toplevel, seat, serial| {
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.move_)(evlh, &mut *user_idata, handle, seat, serial)
},
resize: |evlh, idata, _, toplevel, seat, serial, edges| {
let edges =
zxdg_toplevel_v6::ResizeEdge::from_raw(edges).unwrap_or(zxdg_toplevel_v6::ResizeEdge::None);
let handle = make_toplevel_handle(idata.compositor_token, toplevel);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.resize)(evlh, &mut *user_idata, handle, seat, serial, edges)
},
set_max_size: |_, idata, _, toplevel, width, height| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.max_size = (width, height);
})
},
set_min_size: |_, idata, _, toplevel, width, height| {
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
toplevel_data.min_size = (width, height);
})
},
set_maximized: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, Some(true), None, None, None);
},
unset_maximized: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, Some(false), None, None, None);
},
set_fullscreen: |evlh, idata, _, toplevel, seat| {
xdg_handle_display_state_change(evlh, idata, toplevel, None, None, Some(true), seat);
},
unset_fullscreen: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, None, None, Some(false), None);
},
set_minimized: |evlh, idata, _, toplevel| {
xdg_handle_display_state_change(evlh, idata, toplevel, None, Some(true), None, None);
},
}
}
/*
* xdg_popup
*/
fn destroy_popup(surface: &zxdg_popup_v6::ZxdgPopupV6) {
let ptr = surface.get_user_data();
surface.set_user_data(::std::ptr::null_mut());
// drop the PositionerState
let data = unsafe { Box::from_raw(ptr as *mut ShellSurfaceUserData) };
// explicit call to drop to not forget what we're doing
::std::mem::drop(data);
}
pub(crate) fn send_popup_configure<U, R, ID>(
token: CompositorToken<U, R, ID>, resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure
) where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
ID: 'static,
{
let &(ref surface, _, ref shell_surface) =
unsafe { &*(resource.get_user_data() as *mut ShellSurfaceUserData) };
let (x, y) = configure.position;
let (w, h) = configure.size;
let serial = configure.serial;
resource.configure(x, y, w, h);
shell_surface.configure(serial);
// Add the configure as pending
token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| data.pending_configures.push(serial))
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
}
fn make_popup_handle<U, R, H, SD>(
token: CompositorToken<U, R, H>, resource: &zxdg_popup_v6::ZxdgPopupV6
) -> super::PopupSurface<U, R, H, SD> {
let ptr = resource.get_user_data();
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
super::PopupSurface {
wl_surface: unsafe { wl_surface.clone_unchecked() },
shell_surface: super::SurfaceKind::XdgPopup(unsafe { resource.clone_unchecked() }),
token: token,
_shell_data: ::std::marker::PhantomData,
}
}
fn popup_implementation<U, R, CID, SID, SD>(
) -> zxdg_popup_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
where
U: 'static,
R: Role<ShellSurfaceRole> + 'static,
CID: 'static,
SID: 'static,
SD: 'static,
{
zxdg_popup_v6::Implementation {
destroy: |evlh, idata, _, popup| {
let ptr = popup.get_user_data();
let &(ref surface, _, _) = unsafe {
&*(ptr
as *mut (
wl_surface::WlSurface,
zxdg_shell_v6::ZxdgShellV6,
zxdg_surface_v6::ZxdgSurfaceV6,
))
};
idata
.compositor_token
.with_role_data::<ShellSurfaceRole, _, _>(surface, |data| {
data.pending_state = ShellSurfacePendingState::None;
data.configured = false;
})
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
// remove this surface from the known ones (as well as any leftover dead surface)
evlh.state()
.get_mut(&idata.state_token)
.known_popups
.retain(|other| {
other
.get_surface()
.map(|s| !s.equals(surface))
.unwrap_or(false)
});
},
grab: |evlh, idata, _, popup, seat, serial| {
let handle = make_popup_handle(idata.compositor_token, popup);
let mut user_idata = idata.idata.borrow_mut();
(idata.implementation.grab)(evlh, &mut *user_idata, handle, seat, serial)
},
}
}

View File

@ -11,7 +11,7 @@
//! [the similar helpers](https://cgit.freedesktop.org/wayland/wayland/tree/src/wayland-shm.c) //! [the similar helpers](https://cgit.freedesktop.org/wayland/wayland/tree/src/wayland-shm.c)
//! of the wayland C libraries. //! of the wayland C libraries.
//! //!
//! To use it, first add a `ShmGlobal` to your event loop, specifying the formats //! To use it, first add a `ShmGlobal` to your display, specifying the formats
//! you want to support (ARGB8888 and XRGB8888 are always considered as supported, //! you want to support (ARGB8888 and XRGB8888 are always considered as supported,
//! as specified by the wayland protocol) and obtain its `ShmToken`. //! as specified by the wayland protocol) and obtain its `ShmToken`.
//! //!
@ -23,12 +23,13 @@
//! use wayland_server::protocol::wl_shm::Format; //! use wayland_server::protocol::wl_shm::Format;
//! //!
//! # fn main() { //! # fn main() {
//! # let (display, mut event_loop) = wayland_server::create_display(); //! # let (mut display, mut event_loop) = wayland_server::Display::new();
//! // Insert the ShmGlobal into your event loop //! // Insert the ShmGlobal into 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 shm_global = init_shm_global( //! let shm_global = init_shm_global(
//! &mut event_loop, //! &mut display,
//! event_loop.token(),
//! vec![Format::Yuyv, Format::C8], //! vec![Format::Yuyv, Format::C8],
//! None // we don't provide a logger here //! None // we don't provide a logger here
//! ); //! );
@ -42,7 +43,8 @@
//! # extern crate wayland_server; //! # extern crate wayland_server;
//! # extern crate smithay; //! # extern crate smithay;
//! # use wayland_server::protocol::wl_buffer::WlBuffer; //! # use wayland_server::protocol::wl_buffer::WlBuffer;
//! # fn wrap(buffer: &WlBuffer) { //! # use wayland_server::Resource;
//! # fn wrap(buffer: &Resource<WlBuffer>) {
//! use smithay::wayland::shm::{with_buffer_contents, BufferData}; //! use smithay::wayland::shm::{with_buffer_contents, BufferData};
//! //!
//! with_buffer_contents(&buffer, //! with_buffer_contents(&buffer,
@ -64,7 +66,8 @@
use self::pool::{Pool, ResizeError}; use self::pool::{Pool, ResizeError};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use wayland_server::{resource_is_registered, Client, EventLoopHandle, Global, Resource}; use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
use wayland_server::commons::Implementation;
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;
@ -77,6 +80,7 @@ mod pool;
pub struct ShmGlobalData { pub struct ShmGlobalData {
formats: Rc<Vec<wl_shm::Format>>, formats: Rc<Vec<wl_shm::Format>>,
log: ::slog::Logger, log: ::slog::Logger,
token: LoopToken,
} }
/// Create a new SHM global advertizing given supported formats. /// Create a new SHM global advertizing given supported formats.
@ -85,12 +89,15 @@ pub struct ShmGlobalData {
/// as they are required by the protocol. Formats given as argument /// as they are required by the protocol. Formats given as argument
/// as additionnaly advertized. /// as additionnaly advertized.
/// ///
/// The global is directly registered into the eventloop, and this function /// The global is directly created on the provided `Display`, and this function
/// returns the global handle, in case you whish to remove this global in /// returns the global handle, in case you whish to remove this global in
/// the future. /// the future.
pub fn init_shm_global<L>( pub fn init_shm_global<L>(
evlh: &mut EventLoopHandle, mut formats: Vec<wl_shm::Format>, logger: L display: &mut Display,
) -> Global<wl_shm::WlShm, ShmGlobalData> token: LoopToken,
mut formats: Vec<wl_shm::Format>,
logger: L,
) -> Global<wl_shm::WlShm>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -102,9 +109,16 @@ where
let data = ShmGlobalData { let data = ShmGlobalData {
formats: Rc::new(formats), formats: Rc::new(formats),
log: log.new(o!("smithay_module" => "shm_handler")), log: log.new(o!("smithay_module" => "shm_handler")),
token: token.clone(),
}; };
evlh.register_global::<wl_shm::WlShm, _>(1, shm_global_bind, data) display.create_global::<wl_shm::WlShm, _>(&token, 1, move |_version, shm_new: NewResource<_>| {
let shm = shm_new.implement_nonsend(data.clone(), None::<fn(_, _)>, &data.token);
// send the formats
for f in &data.formats[..] {
shm.send(wl_shm::Event::Format { format: *f });
}
})
} }
/// Error that can occur when accessing an SHM buffer /// Error that can occur when accessing an SHM buffer
@ -131,11 +145,11 @@ pub enum BufferAccessError {
/// ///
/// If the buffer is not managed by the provided `ShmGlobal`, the closure is not called /// If the buffer is not managed by the provided `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>(buffer: &Resource<wl_buffer::WlBuffer>, f: F) -> Result<(), BufferAccessError>
where where
F: FnOnce(&[u8], BufferData), F: FnOnce(&[u8], BufferData),
{ {
if !resource_is_registered(buffer, &buffer_implementation()) { if !buffer.is_implemented_with::<ShmGlobalData>() {
return Err(BufferAccessError::NotManaged); return Err(BufferAccessError::NotManaged);
} }
let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) }; let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) };
@ -151,52 +165,43 @@ where
Ok(()) Ok(())
} }
fn shm_global_bind(evlh: &mut EventLoopHandle, data: &mut ShmGlobalData, _: &Client, global: wl_shm::WlShm) { impl Implementation<Resource<wl_shm::WlShm>, wl_shm::Request> for ShmGlobalData {
// register an handler for this shm fn receive(&mut self, request: wl_shm::Request, shm: Resource<wl_shm::WlShm>) {
evlh.register(&global, shm_implementation(), data.clone(), None); use self::wl_shm::{Error, Request};
// and then the custom formats
for f in &data.formats[..] {
global.format(*f);
}
}
fn shm_implementation() -> wl_shm::Implementation<ShmGlobalData> { match request {
wl_shm::Implementation { Request::CreatePool { id: pool, fd, size } => {
create_pool: |evlh, data, _, shm, pool, fd, size| { if size <= 0 {
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, data.log.clone()) {
Ok(p) => p,
Err(()) => {
shm.post_error( shm.post_error(
wl_shm::Error::InvalidFd as u32, 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, self.log.clone()) {
let arc_pool = Box::new(Arc::new(mmap_pool)); Ok(p) => p,
evlh.register( Err(()) => {
&pool, shm.post_error(
shm_pool_implementation(), wl_shm::Error::InvalidFd as u32,
data.clone(), format!("Failed mmap of fd {}.", fd),
Some(destroy_shm_pool), );
); return;
pool.set_user_data(Box::into_raw(arc_pool) as *mut ()); }
}, };
let arc_pool = Box::new(Arc::new(mmap_pool));
let pool = pool.implement_nonsend(
self.clone(),
Some(|pool: Resource<_>, _| {
drop(unsafe { Box::from_raw(pool.get_user_data() as *mut Arc<Pool>) })
}),
&self.token,
);
pool.set_user_data(Box::into_raw(arc_pool) as *mut ());
}
}
} }
} }
fn destroy_shm_pool(pool: &wl_shm_pool::WlShmPool) {
let arc_pool = unsafe { Box::from_raw(pool.get_user_data() as *mut Arc<Pool>) };
drop(arc_pool)
}
/// 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 {
@ -217,30 +222,48 @@ struct InternalBufferData {
data: BufferData, data: BufferData,
} }
fn shm_pool_implementation() -> wl_shm_pool::Implementation<ShmGlobalData> { impl Implementation<Resource<wl_shm_pool::WlShmPool>, wl_shm_pool::Request> for ShmGlobalData {
wl_shm_pool::Implementation { fn receive(&mut self, request: wl_shm_pool::Request, pool: Resource<wl_shm_pool::WlShmPool>) {
create_buffer: |evlh, data, _, pool, buffer, offset, width, height, stride, format| { use self::wl_shm_pool::Request;
if !data.formats.contains(&format) {
buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new()); let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) };
return;
match request {
Request::CreateBuffer {
id: buffer,
offset,
width,
height,
stride,
format,
} => {
if !self.formats.contains(&format) {
pool.post_error(
wl_shm::Error::InvalidFormat as u32,
format!("SHM format {:?} is not supported.", format),
);
return;
}
let data = Box::into_raw(Box::new(InternalBufferData {
pool: arc_pool.clone(),
data: BufferData {
offset: offset,
width: width,
height: height,
stride: stride,
format: format,
},
}));
let buffer = buffer.implement_nonsend(
self.clone(),
Some(|buffer: Resource<_>, _| {
drop(unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) })
}),
&self.token,
);
buffer.set_user_data(data as *mut ());
} }
let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc<Pool>) }; Request::Resize { size } => match arc_pool.resize(size) {
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(()) => {} Ok(()) => {}
Err(ResizeError::InvalidSize) => { Err(ResizeError::InvalidSize) => {
pool.post_error( pool.post_error(
@ -251,19 +274,15 @@ fn shm_pool_implementation() -> wl_shm_pool::Implementation<ShmGlobalData> {
Err(ResizeError::MremapFailed) => { Err(ResizeError::MremapFailed) => {
pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into()); pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into());
} }
} },
}, Request::Destroy => {}
destroy: |_, _, _, _| {}, }
} }
} }
fn destroy_buffer(buffer: &wl_buffer::WlBuffer) { impl Implementation<Resource<wl_buffer::WlBuffer>, wl_buffer::Request> for ShmGlobalData {
let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) }; fn receive(&mut self, request: wl_buffer::Request, _pool: Resource<wl_buffer::WlBuffer>) {
drop(buffer_data) // this will break if new requests are added to buffer =)
} let wl_buffer::Request::Destroy = request;
fn buffer_implementation() -> wl_buffer::Implementation<()> {
wl_buffer::Implementation {
destroy: |_, _, _, _| {},
} }
} }

View File

@ -161,8 +161,8 @@ unsafe fn map(fd: RawFd, size: usize) -> Result<*mut u8, ()> {
let ret = mman::mmap( let ret = mman::mmap(
ptr::null_mut(), ptr::null_mut(),
size, size,
mman::PROT_READ, mman::ProtFlags::PROT_READ,
mman::MAP_SHARED, mman::MapFlags::MAP_SHARED,
fd, fd,
0, 0,
); );
@ -179,8 +179,8 @@ unsafe fn nullify_map(ptr: *mut u8, size: usize) -> Result<(), ()> {
let ret = mman::mmap( let ret = mman::mmap(
ptr as *mut _, ptr as *mut _,
size, size,
mman::PROT_READ, mman::ProtFlags::PROT_READ,
mman::MAP_ANONYMOUS | mman::MAP_PRIVATE | mman::MAP_FIXED, mman::MapFlags::MAP_ANONYMOUS | mman::MapFlags::MAP_PRIVATE | mman::MapFlags::MAP_FIXED,
-1, -1,
0, 0,
); );
@ -191,7 +191,7 @@ unsafe fn place_sigbus_handler() {
// create our sigbus handler // create our sigbus handler
let action = SigAction::new( let action = SigAction::new(
SigHandler::SigAction(sigbus_handler), SigHandler::SigAction(sigbus_handler),
signal::SA_NODEFER, signal::SaFlags::SA_NODEFER,
signal::SigSet::empty(), signal::SigSet::empty(),
); );
match signal::sigaction(Signal::SIGBUS, &action) { match signal::sigaction(Signal::SIGBUS, &action) {