diff --git a/.rustfmt.toml b/.rustfmt.toml index 0409b3d..284d219 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -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 diff --git a/.travis.yml b/.travis.yml index 1243de1..7d93485 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ rust: sudo: required +branches: + only: + - master + dist: trusty # We cannot cache .vagga, because we actually do not have read permissions diff --git a/Cargo.toml b/Cargo.toml index 4f99f5b..fae0421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,15 @@ description = "Smithay is a library for writing wayland compositors." repository = "https://github.com/Smithay/smithay" [dependencies] -wayland-server = "0.14.0" -wayland-sys = "0.14.0" -nix = "0.9.0" +wayland-server = "0.20.1" +wayland-sys = "0.20.1" +nix = "0.10.0" xkbcommon = "0.2.1" tempfile = "2.1.5" slog = { version = "2.1.1" } slog-stdlog = "3.0.2" 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 } drm = { version = "^0.3.1", optional = true } 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 } dbus = { version = "0.6.1", 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" error-chain = "0.11.0" lazy_static = "1.0.0" @@ -38,7 +38,7 @@ slog-async = "2.2" rand = "0.3" [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_drm = ["drm", "gbm"] backend_libinput = ["input"] @@ -47,3 +47,4 @@ backend_session_udev = ["udev", "backend_session"] backend_session_logind = ["dbus", "systemd", "backend_session"] backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] + diff --git a/examples/drm.rs b/examples/drm.rs index 077af24..7993c72 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -34,7 +34,6 @@ use std::os::unix::io::AsRawFd; use std::os::unix::io::RawFd; use std::rc::Rc; use std::time::Duration; -use wayland_server::EventLoopHandle; #[derive(Debug)] pub struct Card(File); @@ -56,7 +55,7 @@ fn main() { ); // 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 @@ -124,10 +123,10 @@ fn main() { * 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) = - init_shell(&mut event_loop, log.clone(), egl_display.clone()); + let (compositor_token, _, _, window_map) = + init_shell(&mut display, event_loop.token(), log.clone(), egl_display); /* * Add a listening socket: @@ -139,7 +138,7 @@ fn main() { * Register the DrmDevice on the EventLoop */ let _source = drm_device_bind( - &mut event_loop, + &event_loop.token(), device, DrmHandlerImpl { compositor_token, @@ -159,7 +158,7 @@ fn main() { } pub struct DrmHandlerImpl { - compositor_token: CompositorToken>>>, + compositor_token: CompositorToken, window_map: Rc>, drawer: GliumDrawer>, logger: ::slog::Logger, @@ -167,8 +166,11 @@ pub struct DrmHandlerImpl { impl DrmHandler for DrmHandlerImpl { fn ready( - &mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice, _crtc: crtc::Handle, - _frame: u32, _duration: Duration, + &mut self, + _device: &mut DrmDevice, + _crtc: crtc::Handle, + _frame: u32, + _duration: Duration, ) { let mut frame = self.drawer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); @@ -215,8 +217,8 @@ impl DrmHandler for DrmHandlerImpl { if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + x += subdata.location.0; + y += subdata.location.1; } info!(self.logger, "Render window"); self.drawer.render_texture( @@ -248,7 +250,7 @@ impl DrmHandler for DrmHandlerImpl { frame.finish().unwrap(); } - fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice, error: DrmError) { + fn error(&mut self, _device: &mut DrmDevice, error: DrmError) { panic!("{:?}", error); } } diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs index 46b1087..ecb2ec2 100644 --- a/examples/helpers/glium.rs +++ b/examples/helpers/glium.rs @@ -135,8 +135,13 @@ impl GliumDrawer { } pub fn render_texture( - &self, target: &mut glium::Frame, texture: &Texture2d, y_inverted: bool, - surface_dimensions: (u32, u32), surface_location: (i32, i32), screen_size: (u32, u32), + &self, + target: &mut glium::Frame, + texture: &Texture2d, + y_inverted: bool, + surface_dimensions: (u32, u32), + surface_location: (i32, i32), + screen_size: (u32, u32), blending: glium::Blend, ) { let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32); diff --git a/examples/helpers/implementations.rs b/examples/helpers/implementations.rs index a8664ae..d494bb9 100644 --- a/examples/helpers/implementations.rs +++ b/examples/helpers/implementations.rs @@ -1,18 +1,21 @@ -use super::WindowMap; +use super::{SurfaceKind, WindowMap}; use glium::texture::Texture2d; use rand; use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format}; use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages}; -use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, - SurfaceUserImplementation}; -use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, - ShellSurfaceUserImplementation, ToplevelConfigure}; +use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent}; +use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState as XdgShellState, + ToplevelConfigure, XdgRequest, XdgSurfaceRole}; +use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, + ShellSurfaceKind, ShellSurfaceRole}; use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents; use std::cell::RefCell; use std::rc::Rc; -use 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)] pub struct SurfaceData { @@ -25,110 +28,62 @@ pub enum Buffer { Shm { data: Vec, size: (u32, u32) }, } -pub fn surface_implementation( -) -> SurfaceUserImplementation>>> { - SurfaceUserImplementation { - commit: |_, display, surface, token| { - // we retrieve the contents of the associated buffer and copy it - token.with_surface_data(surface, |attributes| { - match attributes.buffer.take() { - Some(Some((buffer, (_x, _y)))) => { - // we ignore hotspot coordinates in this simple example - match if let Some(display) = display.borrow().as_ref() { - display.egl_buffer_contents(buffer) - } else { - Err(BufferAccessError::NotManaged(buffer)) - } { - Ok(images) => { - match images.format { - Format::RGB => {} - Format::RGBA => {} - _ => { - // we don't handle the more complex formats here. - attributes.user_data.buffer = None; - attributes.user_data.texture = None; - return; - } - }; +fn surface_commit( + surface: &Resource, + token: CompositorToken, + display: &RefCell>, +) { + // we retrieve the contents of the associated buffer and copy it + token.with_surface_data(surface, |attributes| { + match attributes.buffer.take() { + Some(Some((buffer, (_x, _y)))) => { + // we ignore hotspot coordinates in this simple example + match if let Some(display) = display.borrow().as_ref() { + display.egl_buffer_contents(buffer) + } else { + Err(BufferAccessError::NotManaged(buffer)) + } { + Ok(images) => { + match images.format { + Format::RGB => {} + Format::RGBA => {} + _ => { + // we don't handle the more complex formats here. + attributes.user_data.buffer = 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.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 { - pub token: CompositorToken>>>, - pub window_map: Rc>>, (), F>>>, -} - -pub fn shell_implementation( -) -> ShellSurfaceUserImplementation>>, ShellIData, ()> -where - F: Fn(&SurfaceAttributes) -> 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, } - }, - new_popup: |_, _, _| PopupConfigure { - size: (10, 10), - position: (10, 10), - serial: 42, - }, - move_: |_, _, _, _, _| {}, - resize: |_, _, _, _, _, _| {}, - grab: |_, _, _, _, _| {}, - change_display_state: |_, _, _, _, _, _, _| ToplevelConfigure { - size: None, - states: vec![], - serial: 42, - }, - show_window_menu: |_, _, _, _, _, _, _| {}, - } + Some(None) => { + // erase the contents + attributes.user_data.buffer = None; + attributes.user_data.texture = None; + } + None => {} + } + }); } fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> { @@ -143,38 +98,104 @@ fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> { .map(|(x, y)| (x as i32, y as i32)) } -pub type MyWindowMap = WindowMap< - SurfaceData, - Roles, - Rc>>, - (), - fn(&SurfaceAttributes) -> Option<(i32, i32)>, ->; +pub type MyWindowMap = + WindowMap) -> Option<(i32, i32)>>; pub fn init_shell( - evl: &mut EventLoop, log: ::slog::Logger, data: Rc>> + display: &mut Display, + looptoken: LoopToken, + log: ::slog::Logger, + egl_display: Rc>>, ) -> ( - CompositorToken>>>, - StateToken>>, ()>>, + CompositorToken, + Arc>>, + Arc>>, Rc>, ) { - let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone()); - - let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new( - compositor_token, - get_size as _, - ))); - - let (shell_state_token, _, _) = shell_init( - evl, - compositor_token, - shell_implementation(), - ShellIData { - token: compositor_token, - window_map: window_map.clone(), + // Create the compositor + let c_egl_display = egl_display.clone(); + let (compositor_token, _, _) = compositor_init( + display, + looptoken.clone(), + move |request, (surface, ctoken)| match request { + SurfaceEvent::Commit => surface_commit(&surface, ctoken, &*c_egl_display), + SurfaceEvent::Frame { callback } => callback + .implement(|e, _| match e {}, None::) + .send(wl_callback::Event::Done { callback_data: 0 }), }, 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, + ) } diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs index 6659931..2747f60 100644 --- a/examples/helpers/mod.rs +++ b/examples/helpers/mod.rs @@ -4,4 +4,4 @@ mod window_map; pub use self::glium::GliumDrawer; pub use self::implementations::*; -pub use self::window_map::WindowMap; +pub use self::window_map::{Kind as SurfaceKind, WindowMap}; diff --git a/examples/helpers/window_map.rs b/examples/helpers/window_map.rs index 979b4c1..af79ca3 100644 --- a/examples/helpers/window_map.rs +++ b/examples/helpers/window_map.rs @@ -1,27 +1,57 @@ use smithay::utils::Rectangle; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction}; 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::protocol::wl_surface; -struct Window { - location: (i32, i32), - surface: Rectangle, - toplevel: ToplevelSurface, +pub enum Kind { + Xdg(ToplevelSurface), + Wl(ShellSurface), } -impl Window +impl Kind where U: 'static, - R: Role + Role + 'static, - CID: 'static, + R: Role + Role + Role> + '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> { + match *self { + Kind::Xdg(ref t) => t.get_surface(), + Kind::Wl(ref t) => t.get_surface(), + } + } +} + +struct Window { + location: (i32, i32), + surface: Rectangle, + toplevel: Kind, +} + +impl Window +where + U: 'static, + R: Role + Role + Role> + 'static, + SD: 'static, + D: 'static, { // Find the topmost surface under this point if any and the location of this point in the surface fn matching( - &self, point: (f64, f64), ctoken: CompositorToken, get_size: F - ) -> Option<(wl_surface::WlSurface, (f64, f64))> + &self, + point: (f64, f64), + ctoken: CompositorToken, + get_size: F, + ) -> Option<(Resource, (f64, f64))> where F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, { @@ -37,8 +67,8 @@ where |wl_surface, attributes, role, &(mut x, mut y)| { if let Some((w, h)) = get_size(attributes) { if let Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + x += subdata.location.0; + y += subdata.location.1; } let my_rect = Rectangle { x, @@ -47,9 +77,10 @@ where height: h, }; if my_rect.contains((point.0 as i32, point.1 as i32)) { - found = wl_surface - .clone() - .map(|s| (s, (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64))); + found = Some(( + wl_surface.clone(), + (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64), + )); TraversalAction::Break } else { TraversalAction::DoChildren((x, y)) @@ -63,7 +94,7 @@ where found } - fn self_update(&mut self, ctoken: CompositorToken, get_size: F) + fn self_update(&mut self, ctoken: CompositorToken, get_size: F) where F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, { @@ -76,8 +107,8 @@ where |_, attributes, role, &(mut x, mut y)| { if let Some((w, h)) = get_size(attributes) { if let Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + x += subdata.location.0; + y += subdata.location.1; } // update the bounding box if x < min_x { @@ -108,21 +139,21 @@ where } } -pub struct WindowMap { - ctoken: CompositorToken, - windows: Vec>, +pub struct WindowMap { + ctoken: CompositorToken, + windows: Vec>, get_size: F, } -impl WindowMap +impl WindowMap where F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, U: 'static, - R: Role + Role + 'static, - CID: 'static, + R: Role + Role + Role> + 'static, SD: 'static, + D: 'static, { - pub fn new(ctoken: CompositorToken, get_size: F) -> WindowMap { + pub fn new(ctoken: CompositorToken, get_size: F) -> WindowMap { WindowMap { ctoken: ctoken, windows: Vec::new(), @@ -130,7 +161,7 @@ where } } - pub fn insert(&mut self, toplevel: ToplevelSurface, location: (i32, i32)) { + pub fn insert(&mut self, toplevel: Kind, location: (i32, i32)) { let mut window = Window { location: location, surface: Rectangle { @@ -145,7 +176,10 @@ where 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, (f64, f64))> { for w in &self.windows { if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) { return Some(surface); @@ -155,8 +189,9 @@ where } pub fn get_surface_and_bring_to_top( - &mut self, point: (f64, f64) - ) -> Option<(wl_surface::WlSurface, (f64, f64))> { + &mut self, + point: (f64, f64), + ) -> Option<(Resource, (f64, f64))> { let mut found = None; for (i, w) in self.windows.iter().enumerate() { 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(&self, mut f: Func) where - Func: FnMut(&ToplevelSurface, (i32, i32)), + Func: FnMut(&Kind, (i32, i32)), { for w in self.windows.iter().rev() { f(&w.toplevel, w.location) diff --git a/examples/udev.rs b/examples/udev.rs index 6cbe5dc..36adbaa 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -53,9 +53,9 @@ use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; 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::sources::EventSource; use xkbcommon::xkb::keysyms as xkb; struct LibinputInputHandler { @@ -78,18 +78,16 @@ impl LibinputInputHandler { } impl InputHandler 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 */ } - 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 */ } - 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 */ } - fn on_keyboard_key( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent - ) { + fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) { let keycode = evt.key(); let state = evt.state(); debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); @@ -100,8 +98,9 @@ impl InputHandler for LibinputInputHandler { let running = &self.running; let mut session = &mut self.session; let log = &self.log; + let time = Event::time(&evt); 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)); if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace && state == KeyState::Pressed @@ -132,9 +131,7 @@ impl InputHandler for LibinputInputHandler { } }); } - fn on_pointer_move( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerMotionEvent - ) { + fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) { let (x, y) = (evt.dx(), evt.dy()); let serial = self.next_serial(); let mut location = self.pointer_location.borrow_mut(); @@ -149,10 +146,7 @@ impl InputHandler for LibinputInputHandler { evt.time(), ); } - fn on_pointer_move_absolute( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, - evt: event::pointer::PointerMotionAbsoluteEvent, - ) { + fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) { let (x, y) = ( evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1), @@ -166,9 +160,7 @@ impl InputHandler for LibinputInputHandler { evt.time(), ); } - fn on_pointer_button( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerButtonEvent - ) { + fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) { let serial = self.next_serial(); let button = evt.button(); let state = match evt.state() { @@ -185,9 +177,7 @@ impl InputHandler for LibinputInputHandler { }; self.pointer.button(button, state, serial, evt.time()); } - fn on_pointer_axis( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: event::pointer::PointerAxisEvent - ) { + fn on_pointer_axis(&mut self, _: &input::Seat, evt: event::pointer::PointerAxisEvent) { let source = match evt.source() { input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous, input::AxisSource::Finger => wayland_server::protocol::wl_pointer::AxisSource::Finger, @@ -244,30 +234,22 @@ impl InputHandler for LibinputInputHandler { event.done(); } } - fn on_touch_down( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchDownEvent - ) { + fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) { /* not done in this example */ } - fn on_touch_motion( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchMotionEvent - ) { + fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) { /* 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 */ } - fn on_touch_cancel( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchCancelEvent - ) { + fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) { /* not done in this example */ } - fn on_touch_frame( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: event::touch::TouchFrameEvent - ) { + fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) { /* 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 */ } } @@ -284,7 +266,7 @@ fn main() { ); // 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 @@ -292,15 +274,24 @@ fn main() { let name = display.add_socket_auto().unwrap().into_string().unwrap(); println!("Listening on socket: {}", name); env::set_var("WAYLAND_DISPLAY", name); - let display = Rc::new(display); + let display = Rc::new(RefCell::new(display)); /* * 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) = - init_shell(&mut event_loop, log.clone(), active_egl_context.clone()); + let (compositor_token, _, _, window_map) = init_shell( + &mut display.borrow_mut(), + event_loop.token(), + log.clone(), + active_egl_context.clone(), + ); /* * Initialize session @@ -321,7 +312,7 @@ fn main() { let bytes = include_bytes!("resources/cursor2.rgba"); let mut udev_backend = UdevBackend::new( - &mut event_loop, + event_loop.token(), &context, session.clone(), UdevHandlerImpl { @@ -340,17 +331,21 @@ fn main() { 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 keyboard = event_loop - .state() - .get_mut(&seat_token) + let pointer = w_seat.add_pointer(); + let keyboard = w_seat .add_keyboard("", "", "", None, 1000, 500) .expect("Failed to initialize the keyboard"); - let (output_token, _output_global) = Output::new( - &mut event_loop, + let (output, _output_global) = Output::new( + &mut display.borrow_mut(), + event_loop.token(), "Drm".into(), PhysicalProperties { width: 0, @@ -363,26 +358,20 @@ fn main() { ); let (w, h) = (1920, 1080); // Hardcode full-hd res - event_loop - .state() - .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 { + output.change_current_state( + Some(Mode { width: w as i32, height: h as i32, refresh: 60_000, - }); + }), + None, + None, + ); + output.set_preferred(Mode { + width: w as i32, + height: h as i32, + refresh: 60_000, + }); /* * Initialize libinput backend @@ -392,28 +381,25 @@ fn main() { let libinput_session_id = notifier.register(&mut libinput_context); libinput_context.udev_assign_seat(&seat).unwrap(); let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); - libinput_backend.set_handler( - &mut event_loop, - LibinputInputHandler { - log: log.clone(), - pointer, - keyboard, - window_map: window_map.clone(), - pointer_location, - screen_size: (w, h), - serial: 0, - session: session, - running: running.clone(), - }, - ); - let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop) + libinput_backend.set_handler(LibinputInputHandler { + log: log.clone(), + pointer, + keyboard, + window_map: window_map.clone(), + pointer_location, + screen_size: (w, h), + serial: 0, + session: session, + running: running.clone(), + }); + let libinput_event_source = libinput_bind(libinput_backend, event_loop.token()) .map_err(|(err, _)| err) .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) .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) .unwrap(); @@ -421,7 +407,7 @@ fn main() { if let Err(_) = event_loop.dispatch(Some(16)) { running.store(false, Ordering::SeqCst); } else { - display.flush_clients(); + display.borrow_mut().flush_clients(); window_map.borrow_mut().refresh(); } } @@ -435,14 +421,19 @@ fn main() { libinput_event_source.remove(); // destroy the udev backend freeing the drm devices - udev_event_source.remove().close(&mut event_loop) + // + // udev_event_source.remove() returns a Box>, 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 { - compositor_token: CompositorToken>>>, + compositor_token: CompositorToken, active_egl_context: Rc>>, backends: HashMap>>>>>, - display: Rc, + display: Rc>, primary_gpu: Option, window_map: Rc>, pointer_location: Rc>, @@ -452,7 +443,8 @@ struct UdevHandlerImpl { impl UdevHandlerImpl { pub fn scan_connectors( - &self, device: &mut DrmDevice + &self, + device: &mut DrmDevice, ) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): let res_handles = device.resource_handles().unwrap(); @@ -511,12 +503,10 @@ impl UdevHandlerImpl { } impl UdevHandler for UdevHandlerImpl { - fn device_added( - &mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice - ) -> Option { + fn device_added(&mut self, device: &mut DrmDevice) -> Option { // init hardware acceleration on the 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))); @@ -531,13 +521,13 @@ impl UdevHandler for UdevHandlerImpl { }) } - fn device_changed(&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice) { + fn device_changed(&mut self, device: &mut DrmDevice) { //quick and dirt, just re-init all backends let backends = self.backends.get(&device.device_id()).unwrap(); *backends.borrow_mut() = self.scan_connectors(device); } - fn device_removed(&mut self, _evlh: &mut EventLoopHandle, device: &mut DrmDevice) { + fn device_removed(&mut self, device: &mut DrmDevice) { // drop the backends on this side self.backends.remove(&device.device_id()); @@ -547,13 +537,13 @@ impl UdevHandler for UdevHandlerImpl { } } - fn error(&mut self, _evlh: &mut EventLoopHandle, error: IoError) { + fn error(&mut self, error: IoError) { error!(self.logger, "{:?}", error); } } pub struct DrmHandlerImpl { - compositor_token: CompositorToken>>>, + compositor_token: CompositorToken, backends: Rc>>>>, window_map: Rc>, pointer_location: Rc>, @@ -562,8 +552,11 @@ pub struct DrmHandlerImpl { impl DrmHandler for DrmHandlerImpl { fn ready( - &mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice, - crtc: crtc::Handle, _frame: u32, _duration: Duration, + &mut self, + _device: &mut DrmDevice, + crtc: crtc::Handle, + _frame: u32, + _duration: Duration, ) { if let Some(drawer) = self.backends.borrow().get(&crtc) { { @@ -616,8 +609,8 @@ impl DrmHandler for DrmHandlerImpl { if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + x += subdata.location.0; + y += subdata.location.1; } info!(self.logger, "Render window"); drawer.render_texture( @@ -655,9 +648,7 @@ impl DrmHandler for DrmHandlerImpl { } } - fn error( - &mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice, error: DrmError - ) { + fn error(&mut self, _device: &mut DrmDevice, error: DrmError) { error!(self.logger, "{:?}", error); } } diff --git a/examples/winit.rs b/examples/winit.rs index 24ff502..a680b8d 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -26,7 +26,7 @@ use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat}; use smithay::wayland::shm::init_shm_global; use std::cell::RefCell; use std::rc::Rc; -use wayland_server::EventLoopHandle; +use wayland_server::Display; use wayland_server::protocol::{wl_output, wl_pointer}; struct WinitInputHandler { @@ -46,30 +46,27 @@ impl WinitInputHandler { } impl InputHandler 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 */ } - fn on_seat_destroyed(&mut self, _evlh: &mut EventLoopHandle, _: &input::Seat) { + fn on_seat_destroyed(&mut self, _: &input::Seat) { /* 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 */ } - fn on_keyboard_key( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitKeyboardInputEvent - ) { + fn on_keyboard_key(&mut self, _: &input::Seat, evt: winit::WinitKeyboardInputEvent) { let keycode = evt.key_code(); let state = evt.state(); debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state)); 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 */ } - fn on_pointer_move_absolute( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseMovedEvent - ) { + fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: winit::WinitMouseMovedEvent) { // on winit, mouse events are already in pixel coordinates let (x, y) = evt.position(); self.pointer_location = (x, y); @@ -81,9 +78,7 @@ impl InputHandler for WinitInputHandler { evt.time(), ); } - fn on_pointer_button( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseInputEvent - ) { + fn on_pointer_button(&mut self, _: &input::Seat, evt: winit::WinitMouseInputEvent) { let serial = self.next_serial(); let button = match evt.button() { input::MouseButton::Left => 0x110, @@ -105,9 +100,7 @@ impl InputHandler for WinitInputHandler { }; self.pointer.button(button, state, serial, evt.time()); } - fn on_pointer_axis( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, evt: winit::WinitMouseWheelEvent - ) { + fn on_pointer_axis(&mut self, _: &input::Seat, evt: winit::WinitMouseWheelEvent) { let source = match evt.source() { input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous, input::AxisSource::Wheel => wayland_server::protocol::wl_pointer::AxisSource::Wheel, @@ -152,28 +145,22 @@ impl InputHandler for WinitInputHandler { event.done(); } } - fn on_touch_down( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchStartedEvent - ) { + fn on_touch_down(&mut self, _: &input::Seat, _: winit::WinitTouchStartedEvent) { /* not done in this example */ } - fn on_touch_motion( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchMovedEvent - ) { + fn on_touch_motion(&mut self, _: &input::Seat, _: winit::WinitTouchMovedEvent) { /* 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 */ } - fn on_touch_cancel( - &mut self, _evlh: &mut EventLoopHandle, _: &input::Seat, _: winit::WinitTouchCancelledEvent - ) { + fn on_touch_cancel(&mut self, _: &input::Seat, _: winit::WinitTouchCancelledEvent) { /* 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 */ } - fn on_input_config_changed(&mut self, _evlh: &mut EventLoopHandle, _: &mut ()) { + fn on_input_config_changed(&mut self, _: &mut ()) { /* never happens with winit */ } } @@ -188,7 +175,7 @@ fn main() { // Initialize a simple backend for testing 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( if let Ok(egl_display) = renderer.bind_wl_display(&display) { @@ -206,22 +193,25 @@ fn main() { * 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) = - init_shell(&mut event_loop, log.clone(), egl_display); + let (compositor_token, _, _, window_map) = + 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 keyboard = event_loop - .state() - .get_mut(&seat_token) - .add_keyboard("", "fr", "oss", None, 1000, 500) + let pointer = seat.add_pointer(); + let keyboard = seat.add_keyboard("", "fr", "oss", None, 1000, 500) .expect("Failed to initialize the keyboard"); - let (output_token, _output_global) = Output::new( - &mut event_loop, + let (output, _) = Output::new( + &mut display, + event_loop.token(), "Winit".into(), PhysicalProperties { width: 0, @@ -233,38 +223,29 @@ fn main() { log.clone(), ); - event_loop - .state() - .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 { + output.change_current_state( + Some(Mode { width: w as i32, height: h as i32, refresh: 60_000, - }); - - input.set_handler( - &mut event_loop, - WinitInputHandler { - log: log.clone(), - pointer, - keyboard, - window_map: window_map.clone(), - pointer_location: (0.0, 0.0), - serial: 0, - }, + }), + None, + None, ); + 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: @@ -273,7 +254,7 @@ fn main() { println!("Listening on socket: {}", name); loop { - input.dispatch_new_events(&mut event_loop).unwrap(); + input.dispatch_new_events().unwrap(); let mut frame = drawer.draw(); 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 Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + x += subdata.location.0; + y += subdata.location.1; } drawer.render_texture( &mut frame, diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index 0f8b3ca..3410e55 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -37,8 +37,11 @@ pub(crate) struct DrmBackendInternal { impl DrmBackend { pub(crate) fn new( - context: Rc, GbmDevice>>, crtc: crtc::Handle, mode: Mode, - connectors: Vec, log: ::slog::Logger, + context: Rc, GbmDevice>>, + crtc: crtc::Handle, + mode: Mode, + connectors: Vec, + log: ::slog::Logger, ) -> Result { // logger already initialized by the DrmDevice info!(log, "Initializing DrmBackend"); @@ -351,7 +354,8 @@ impl DrmBackendInternal { } pub(crate) fn page_flip( - &self, fb: Option<&framebuffer::Info> + &self, + fb: Option<&framebuffer::Info>, ) -> ::std::result::Result<(), SwapBuffersError> { trace!(self.logger, "Queueing Page flip"); @@ -421,7 +425,9 @@ impl GraphicsBackend for DrmBackend { } fn set_cursor_representation( - &self, buffer: &ImageBuffer, Vec>, hotspot: (u32, u32) + &self, + buffer: &ImageBuffer, Vec>, + hotspot: (u32, u32), ) -> Result<()> { let (w, h) = buffer.dimensions(); debug!(self.backend.logger, "Importing cursor"); diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 91fe4b3..6b32c54 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -66,8 +66,6 @@ //! impl ControlDevice for Card {} //! //! # fn main() { -//! let (_display, mut event_loop) = wayland_server::create_display(); -//! //! // Open the drm device //! let mut options = OpenOptions::new(); //! options.read(true); @@ -139,7 +137,6 @@ //! # use std::time::Duration; //! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind}; //! use smithay::backend::graphics::egl::EGLGraphicsBackend; -//! use wayland_server::EventLoopHandle; //! # //! # #[derive(Debug)] //! # pub struct Card(File); @@ -153,7 +150,7 @@ //! # //! # 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(); //! # options.read(true); @@ -181,7 +178,6 @@ //! impl DrmHandler for MyDrmHandler { //! fn ready( //! &mut self, -//! _evlh: &mut EventLoopHandle, //! _device: &mut DrmDevice, //! _crtc: CrtcHandle, //! _frame: u32, @@ -192,7 +188,6 @@ //! } //! fn error( //! &mut self, -//! _evlh: &mut EventLoopHandle, //! device: &mut DrmDevice, //! error: DrmError) //! { @@ -203,7 +198,11 @@ //! // render something (like clear_color) //! 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(); //! # } @@ -233,8 +232,9 @@ use std::rc::{Rc, Weak}; use std::sync::{Arc, Once, ONCE_INIT}; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; -use wayland_server::{Display, EventLoopHandle}; -use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; +use wayland_server::{Display, LoopToken}; +use wayland_server::commons::Implementation; +use wayland_server::sources::{FdEvent, FdInterest, Source}; mod backend; pub mod error; @@ -378,7 +378,10 @@ impl DrmDevice { /// Errors if initialization fails or the mode is not available on all given /// connectors. pub fn create_backend( - &mut self, crtc: crtc::Handle, mode: Mode, connectors: I + &mut self, + crtc: crtc::Handle, + mode: Mode, + connectors: I, ) -> Result> where I: Into>, @@ -532,44 +535,66 @@ impl Drop for DrmDevice { pub trait DrmHandler { /// The `DrmBackend` of crtc has finished swapping buffers and new frame can now /// (and should be immediately) be rendered. - fn ready( - &mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice, crtc: crtc::Handle, frame: u32, - duration: Duration, - ); + fn ready(&mut self, device: &mut DrmDevice, crtc: crtc::Handle, frame: u32, duration: Duration); /// The `DrmDevice` has thrown an error. /// /// The related backends are most likely *not* usable anymore and /// the whole stack has to be recreated.. - fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice, error: DrmError); + fn error(&mut self, device: &mut DrmDevice, error: DrmError); } /// Bind a `DrmDevice` to an `EventLoop`, /// /// This will cause it to recieve events and feed them into an `DrmHandler` pub fn drm_device_bind( - evlh: &mut EventLoopHandle, device: DrmDevice, handler: H -) -> ::std::result::Result, H)>, (IoError, (DrmDevice, H))> + token: &LoopToken, + device: DrmDevice, + handler: H, +) -> ::std::result::Result<(Source, Rc>>), (IoError, (DrmDevice, H))> where A: ControlDevice + 'static, H: DrmHandler + 'static, { 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_event_source_implementation(), - (device, handler), 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() -> FdEventSourceImpl<(DrmDevice, H)> +struct DrmFdImpl { + device: Rc>>, + handler: H, +} + +impl Implementation<(), FdEvent> for DrmFdImpl where A: ControlDevice + 'static, H: DrmHandler + 'static, { - FdEventSourceImpl { - ready: |evlh, &mut (ref mut device, ref mut handler), _, _| { - match crtc::receive_events(device) { + fn receive(&mut self, event: FdEvent, (): ()) { + let mut device = self.device.borrow_mut(); + match event { + FdEvent::Ready { .. } => match crtc::receive_events(&mut *device) { Ok(events) => for event in events { if let crtc::Event::PageFlip(event) = event { if device.active.load(Ordering::SeqCst) { @@ -584,20 +609,21 @@ where backend.unlock_buffer(); trace!(device.logger, "Handling event for backend {:?}", event.crtc); // 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 { device.backends.borrow_mut().remove(&event.crtc); } } } }, - Err(err) => handler.error(evlh, device, err), - }; - }, - error: |evlh, &mut (ref mut device, ref mut handler), _, error| { - warn!(device.logger, "DrmDevice errored: {}", error); - handler.error(evlh, device, error.into()); - }, + Err(err) => self.handler.error(&mut device, err), + }, + FdEvent::Error { error, .. } => { + warn!(device.logger, "DrmDevice errored: {}", error); + self.handler.error(&mut device, error.into()); + } + } } } @@ -629,7 +655,7 @@ impl AsSessionObserver> for Drm #[cfg(feature = "backend_session")] impl SessionObserver for DrmDeviceObserver { - 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 major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) { return; @@ -665,7 +691,7 @@ impl SessionObserver for DrmDeviceObserver { } } - fn activate(&mut self, _evlh: &mut EventLoopHandle, devnum: Option<(u32, u32, Option)>) { + fn activate(&mut self, devnum: Option<(u32, u32, Option)>) { if let Some((major, minor, fd)) = devnum { if major as u64 != stat::major(self.device_id) || minor as u64 != stat::minor(self.device_id) { return; diff --git a/src/backend/graphics/egl/context.rs b/src/backend/graphics/egl/context.rs index b564a94..d1abf19 100644 --- a/src/backend/graphics/egl/context.rs +++ b/src/backend/graphics/egl/context.rs @@ -50,7 +50,10 @@ impl> DerefMut for EGLContext> EGLContext { /// Create a new `EGLContext` from a given `NativeDisplay` pub fn new( - native: N, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L + native: N, + attributes: GlAttributes, + reqs: PixelFormatRequirements, + logger: L, ) -> Result> where L: Into>, @@ -82,7 +85,9 @@ impl> EGLContext { } unsafe fn new_internal( - ptr: ffi::NativeDisplayType, mut attributes: GlAttributes, reqs: PixelFormatRequirements, + ptr: ffi::NativeDisplayType, + mut attributes: GlAttributes, + reqs: PixelFormatRequirements, log: ::slog::Logger, ) -> Result< ( diff --git a/src/backend/graphics/egl/ffi.rs b/src/backend/graphics/egl/ffi.rs index 304506e..2dc0181 100644 --- a/src/backend/graphics/egl/ffi.rs +++ b/src/backend/graphics/egl/ffi.rs @@ -42,7 +42,8 @@ pub mod egl { #[allow(non_snake_case, unused_variables, dead_code)] #[inline] 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 { __gl_imports::mem::transmute::< _, @@ -53,7 +54,8 @@ pub mod egl { #[allow(non_snake_case, unused_variables, dead_code)] #[inline] 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 { __gl_imports::mem::transmute::< _, @@ -64,7 +66,9 @@ pub mod egl { #[allow(non_snake_case, unused_variables, dead_code)] #[inline] 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, ) -> types::EGLBoolean { __gl_imports::mem::transmute::< diff --git a/src/backend/graphics/egl/native.rs b/src/backend/graphics/egl/native.rs index b58c72c..cb8a54b 100644 --- a/src/backend/graphics/egl/native.rs +++ b/src/backend/graphics/egl/native.rs @@ -31,7 +31,9 @@ pub trait Backend { /// The returned `EGLDisplay` needs to be a valid ptr for egl, /// but there is no way to test that. unsafe fn get_display 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; } @@ -43,7 +45,9 @@ impl Backend for Wayland { type Surface = wegl::WlEglSurface; unsafe fn get_display( - display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger + display: ffi::NativeDisplayType, + has_dp_extension: F, + log: ::slog::Logger, ) -> ffi::egl::types::EGLDisplay where F: Fn(&str) -> bool, @@ -87,7 +91,9 @@ impl Backend for X11 { type Surface = XlibWindow; unsafe fn get_display( - display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger + display: ffi::NativeDisplayType, + has_dp_extension: F, + log: ::slog::Logger, ) -> ffi::egl::types::EGLDisplay where F: Fn(&str) -> bool, @@ -114,7 +120,9 @@ impl Backend for Gbm { type Surface = GbmSurface; unsafe fn get_display( - display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger + display: ffi::NativeDisplayType, + has_dp_extension: F, + log: ::slog::Logger, ) -> ffi::egl::types::EGLDisplay where F: Fn(&str) -> bool, diff --git a/src/backend/graphics/egl/surface.rs b/src/backend/graphics/egl/surface.rs index 419c92a..f754e20 100644 --- a/src/backend/graphics/egl/surface.rs +++ b/src/backend/graphics/egl/surface.rs @@ -30,7 +30,8 @@ impl DerefMut for EGLSurface { impl EGLSurface { pub(crate) fn new, D: native::NativeDisplay>( - context: &EGLContext, native: N + context: &EGLContext, + native: N, ) -> Result> { let surface = unsafe { ffi::egl::CreateWindowSurface( diff --git a/src/backend/graphics/egl/wayland.rs b/src/backend/graphics/egl/wayland.rs index 5ae241f..7382837 100644 --- a/src/backend/graphics/egl/wayland.rs +++ b/src/backend/graphics/egl/wayland.rs @@ -17,7 +17,7 @@ use nix::libc::c_uint; use std::fmt; use std::rc::{Rc, Weak}; 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; /// Error that can occur when accessing an EGL buffer @@ -25,7 +25,7 @@ pub enum BufferAccessError { /// The corresponding Context is not alive anymore ContextLost, /// This buffer is not managed by the EGL buffer - NotManaged(WlBuffer), + NotManaged(Resource), /// Failed to create EGLImages from the buffer EGLImageCreationFailed, /// The required EGL extension is not supported by the underlying EGL implementation @@ -175,7 +175,7 @@ pub struct EGLImages { /// Format of these images pub format: Format, images: Vec, - buffer: WlBuffer, + buffer: Resource, } impl EGLImages { @@ -192,7 +192,9 @@ impl EGLImages { /// /// The given `tex_id` needs to be a valid GL texture otherwise undefined behavior might occur. pub unsafe fn bind_to_texture( - &self, plane: usize, tex_id: c_uint + &self, + plane: usize, + tex_id: c_uint, ) -> ::std::result::Result<(), TextureCreationError> { if self.display.upgrade().is_some() { 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, *mut wl_display); impl EGLDisplay { fn new>( - context: &EGLContext, display: *mut wl_display + context: &EGLContext, + display: *mut wl_display, ) -> EGLDisplay { EGLDisplay(Rc::downgrade(&context.display), display) } @@ -266,14 +269,15 @@ impl EGLDisplay { /// a `BufferAccessError::NotManaged(WlBuffer)` is returned with the original buffer /// to render it another way. pub fn egl_buffer_contents( - &self, buffer: WlBuffer + &self, + buffer: Resource, ) -> ::std::result::Result { if let Some(display) = self.0.upgrade() { let mut format: i32 = 0; if unsafe { ffi::egl::QueryWaylandBufferWL( *display, - buffer.ptr() as *mut _, + buffer.c_ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _, ) == 0 @@ -294,7 +298,7 @@ impl EGLDisplay { if unsafe { ffi::egl::QueryWaylandBufferWL( *display, - buffer.ptr() as *mut _, + buffer.c_ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _, ) == 0 @@ -306,7 +310,7 @@ impl EGLDisplay { if unsafe { ffi::egl::QueryWaylandBufferWL( *display, - buffer.ptr() as *mut _, + buffer.c_ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _, ) == 0 @@ -318,7 +322,7 @@ impl EGLDisplay { if unsafe { ffi::egl::QueryWaylandBufferWL( *display, - buffer.ptr() as *mut _, + buffer.c_ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _, ) != 0 @@ -339,7 +343,7 @@ impl EGLDisplay { *display, ffi::egl::NO_CONTEXT, ffi::egl::WAYLAND_BUFFER_WL, - buffer.ptr() as *mut _, + buffer.c_ptr() as *mut _, out.as_ptr(), ) }; @@ -394,10 +398,10 @@ impl> EGLWaylandExtensions for E if !self.egl_to_texture_support { 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 { bail!(ErrorKind::OtherEGLDisplayAlreadyBound); } - Ok(EGLDisplay::new(self, unsafe { display.ptr() })) + Ok(EGLDisplay::new(self, display.c_ptr())) } } diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 05d276e..6da8e5b 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -30,7 +30,9 @@ pub trait GraphicsBackend { /// 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. fn set_cursor_representation( - &self, cursor: &Self::CursorFormat, hotspot: (u32, u32) + &self, + cursor: &Self::CursorFormat, + hotspot: (u32, u32), ) -> Result<(), Self::Error>; } diff --git a/src/backend/input.rs b/src/backend/input.rs index bed7877..0bc4769 100644 --- a/src/backend/input.rs +++ b/src/backend/input.rs @@ -2,7 +2,6 @@ use std::error::Error; use std::string::ToString; -use wayland_server::EventLoopHandle; /// A seat describes a group of input devices and at least one /// graphics device belonging together. @@ -521,31 +520,31 @@ pub trait InputBackend: Sized { type TouchFrameEvent: TouchFrameEvent; /// Sets a new handler for this `InputBackend` - fn set_handler + 'static>(&mut self, evlh: &mut EventLoopHandle, handler: H); + fn set_handler + 'static>(&mut self, handler: H); /// Get a reference to the currently set handler, if any fn get_handler(&mut self) -> Option<&mut InputHandler>; /// Clears the currently handler, if one is set - fn clear_handler(&mut self, evlh: &mut EventLoopHandle); + fn clear_handler(&mut self); /// Get current `InputConfig` fn input_config(&mut self) -> &mut Self::InputConfig; /// 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`. pub trait InputHandler { /// 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. - 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. /// /// ## Note: /// /// 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. /// @@ -554,7 +553,7 @@ pub trait InputHandler { /// - `seat` - The `Seat` the event belongs to /// - `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. /// @@ -562,30 +561,28 @@ pub trait InputHandler { /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to /// - `event` - The pointer absolute movement event - fn on_pointer_move_absolute( - &mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionAbsoluteEvent - ); + fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent); /// Called when a new pointer button event was received. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// # Arguments /// /// - `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. - 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. /// @@ -593,99 +590,97 @@ pub trait InputHandler { /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to /// - `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. /// /// 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. - 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 InputHandler for Box> { - fn on_seat_created(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) { - (**self).on_seat_created(evlh, seat) + fn on_seat_created(&mut self, seat: &Seat) { + (**self).on_seat_created(seat) } - fn on_seat_destroyed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) { - (**self).on_seat_destroyed(evlh, seat) + fn on_seat_destroyed(&mut self, seat: &Seat) { + (**self).on_seat_destroyed(seat) } - fn on_seat_changed(&mut self, evlh: &mut EventLoopHandle, seat: &Seat) { - (**self).on_seat_changed(evlh, seat) + fn on_seat_changed(&mut self, seat: &Seat) { + (**self).on_seat_changed(seat) } - fn on_keyboard_key(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::KeyboardKeyEvent) { - (**self).on_keyboard_key(evlh, seat, event) + fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent) { + (**self).on_keyboard_key(seat, event) } - fn on_pointer_move(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionEvent) { - (**self).on_pointer_move(evlh, seat, event) + fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent) { + (**self).on_pointer_move(seat, event) } - fn on_pointer_move_absolute( - &mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerMotionAbsoluteEvent - ) { - (**self).on_pointer_move_absolute(evlh, seat, event) + fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent) { + (**self).on_pointer_move_absolute(seat, event) } - fn on_pointer_button(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerButtonEvent) { - (**self).on_pointer_button(evlh, seat, event) + fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent) { + (**self).on_pointer_button(seat, event) } - fn on_pointer_axis(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::PointerAxisEvent) { - (**self).on_pointer_axis(evlh, seat, event) + fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent) { + (**self).on_pointer_axis(seat, event) } - fn on_touch_down(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchDownEvent) { - (**self).on_touch_down(evlh, seat, event) + fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent) { + (**self).on_touch_down(seat, event) } - fn on_touch_motion(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchMotionEvent) { - (**self).on_touch_motion(evlh, seat, event) + fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent) { + (**self).on_touch_motion(seat, event) } - fn on_touch_up(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchUpEvent) { - (**self).on_touch_up(evlh, seat, event) + fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent) { + (**self).on_touch_up(seat, event) } - fn on_touch_cancel(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchCancelEvent) { - (**self).on_touch_cancel(evlh, seat, event) + fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent) { + (**self).on_touch_cancel(seat, event) } - fn on_touch_frame(&mut self, evlh: &mut EventLoopHandle, seat: &Seat, event: B::TouchFrameEvent) { - (**self).on_touch_frame(evlh, seat, event) + fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent) { + (**self).on_touch_frame(seat, event) } - fn on_input_config_changed(&mut self, evlh: &mut EventLoopHandle, config: &mut B::InputConfig) { - (**self).on_input_config_changed(evlh, config) + fn on_input_config_changed(&mut self, config: &mut B::InputConfig) { + (**self).on_input_config_changed(config) } } diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index caef89d..faf6577 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -11,8 +11,9 @@ use std::hash::{Hash, Hasher}; use std::io::Error as IoError; use std::os::unix::io::RawFd; use std::path::Path; -use wayland_server::EventLoopHandle; -use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; +use wayland_server::LoopToken; +use wayland_server::commons::Implementation; +use wayland_server::sources::{FdEvent, FdInterest, Source}; // No idea if this is the same across unix platforms // 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 TouchFrameEvent = event::touch::TouchFrameEvent; - fn set_handler + 'static>( - &mut self, evlh: &mut EventLoopHandle, mut handler: H - ) { + fn set_handler + 'static>(&mut self, mut handler: H) { if self.handler.is_some() { - self.clear_handler(evlh); + self.clear_handler(); } info!(self.logger, "New input handler set"); for seat in self.seats.values() { 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)); } @@ -276,11 +275,11 @@ impl backend::InputBackend for LibinputInputBackend { .map(|handler| handler as &mut backend::InputHandler) } - fn clear_handler(&mut self, evlh: &mut EventLoopHandle) { + fn clear_handler(&mut self) { if let Some(mut handler) = self.handler.take() { for seat in self.seats.values() { 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"); } @@ -290,7 +289,7 @@ impl backend::InputBackend for LibinputInputBackend { &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; self.context.dispatch()?; @@ -323,7 +322,7 @@ impl backend::InputBackend for LibinputInputBackend { } if let Some(ref mut handler) = self.handler { 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) => { @@ -340,7 +339,7 @@ impl backend::InputBackend for LibinputInputBackend { )); if let Some(ref mut handler) = self.handler { 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(ref mut handler) = self.handler { trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat); - handler.on_seat_destroyed(evlh, &seat); + handler.on_seat_destroyed(&seat); } } else { 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 { if let Some(seat) = self.seats.get(&device_seat) { trace!(self.logger, "Calling on_seat_changed with {:?}", seat); - handler.on_seat_changed(evlh, &seat); + handler.on_seat_changed(&seat); } else { warn!(self.logger, "Seat changed that was never created"); continue; @@ -398,7 +397,7 @@ impl backend::InputBackend for LibinputInputBackend { } } 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) => { @@ -409,7 +408,7 @@ impl backend::InputBackend for LibinputInputBackend { match touch_event { TouchEvent::Down(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) => { trace!( @@ -417,11 +416,11 @@ impl backend::InputBackend for LibinputInputBackend { "Calling on_touch_motion with {:?}", motion_event ); - handler.on_touch_motion(evlh, seat, motion_event) + handler.on_touch_motion(seat, motion_event) } TouchEvent::Up(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) => { trace!( @@ -429,11 +428,11 @@ impl backend::InputBackend for LibinputInputBackend { "Calling on_touch_cancel with {:?}", cancel_event ); - handler.on_touch_cancel(evlh, seat, cancel_event) + handler.on_touch_cancel(seat, cancel_event) } TouchEvent::Frame(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 { @@ -449,7 +448,7 @@ impl backend::InputBackend for LibinputInputBackend { let device_seat = key_event.device().seat(); if let &Some(ref seat) = &self.seats.get(&device_seat) { 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 { warn!(self.logger, "Recieved key event of non existing Seat"); continue; @@ -469,7 +468,7 @@ impl backend::InputBackend for LibinputInputBackend { "Calling on_pointer_move with {:?}", motion_event ); - handler.on_pointer_move(evlh, seat, motion_event); + handler.on_pointer_move(seat, motion_event); } PointerEvent::MotionAbsolute(motion_abs_event) => { trace!( @@ -477,11 +476,11 @@ impl backend::InputBackend for LibinputInputBackend { "Calling on_pointer_move_absolute with {:?}", 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) => { 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) => { trace!( @@ -489,7 +488,7 @@ impl backend::InputBackend for LibinputInputBackend { "Calling on_pointer_button with {:?}", button_event ); - handler.on_pointer_button(evlh, seat, button_event); + handler.on_pointer_button(seat, button_event); } } } else { @@ -554,7 +553,7 @@ impl From for backend::MouseButtonState { #[cfg(feature = "backend_session")] 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 major != INPUT_MAJOR { return; @@ -564,7 +563,7 @@ impl SessionObserver for libinput::Libinput { self.suspend() } - fn activate(&mut self, _state: &mut EventLoopHandle, _device: Option<(u32, u32, Option)>) { + fn activate(&mut self, _device: Option<(u32, u32, Option)>) { // 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 self.resume().expect("Unable to resume libinput context"); @@ -602,27 +601,25 @@ impl libinput::LibinputInterface for LibinputSessionInterface { /// Automatically feeds the backend with incoming events without any manual calls to /// `dispatch_new_events`. Should be used to achieve the smallest possible latency. pub fn libinput_bind( - backend: LibinputInputBackend, evlh: &mut EventLoopHandle -) -> ::std::result::Result, (IoError, LibinputInputBackend)> { + backend: LibinputInputBackend, + token: LoopToken, +) -> ::std::result::Result, (IoError, LibinputInputBackend)> { let fd = unsafe { backend.context.fd() }; - evlh.add_fd_event_source( - fd, - fd_event_source_implementation(), - backend, - FdInterest::READ, - ) + token.add_fd_event_source(fd, FdInterest::READ, backend) } -fn fd_event_source_implementation() -> FdEventSourceImpl { - FdEventSourceImpl { - ready: |evlh, ref mut backend, _, _| { - use backend::input::InputBackend; - if let Err(error) = backend.dispatch_new_events(evlh) { - warn!(backend.logger, "Libinput errored: {}", error); +impl Implementation<(), FdEvent> for LibinputInputBackend { + fn receive(&mut self, event: FdEvent, (): ()) { + match event { + FdEvent::Ready { .. } => { + use backend::input::InputBackend; + if let Err(error) = self.dispatch_new_events() { + warn!(self.logger, "Libinput errored: {}", error); + } } - }, - error: |_evlh, ref backend, _, error| { - warn!(backend.logger, "Libinput fd errored: {}", error); - }, + FdEvent::Error { error, .. } => { + warn!(self.logger, "Libinput fd errored: {}", error); + } + } } } diff --git a/src/backend/session/auto.rs b/src/backend/session/auto.rs index 46bacde..59e4807 100644 --- a/src/backend/session/auto.rs +++ b/src/backend/session/auto.rs @@ -39,8 +39,9 @@ use std::io::Error as IoError; use std::os::unix::io::RawFd; use std::path::Path; use std::rc::Rc; -use wayland_server::EventLoopHandle; -use wayland_server::sources::{EventSource, SignalEventSource}; +use wayland_server::LoopToken; +use wayland_server::commons::downcast_impl; +use wayland_server::sources::{SignalEvent, Source}; /// `Session` using the best available inteface #[derive(Clone)] @@ -71,7 +72,7 @@ pub enum BoundAutoSession { #[cfg(feature = "backend_session_logind")] Logind(BoundLogindSession), /// Bound direct / tty session - Direct(SignalEventSource), + Direct(Source), } /// 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 /// session state and call it's `SessionObservers`. pub fn auto_session_bind( - notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle + notifier: AutoSessionNotifier, + token: &LoopToken, ) -> ::std::result::Result { Ok(match notifier { #[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)))?), - 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)))?), }) } @@ -210,7 +212,8 @@ impl SessionNotifier for AutoSessionNotifier { type Id = AutoId; fn register>( - &mut self, signal: &mut A + &mut self, + signal: &mut A, ) -> Self::Id { match self { #[cfg(feature = "backend_session_logind")] @@ -257,7 +260,9 @@ impl BoundAutoSession { match self { #[cfg(feature = "backend_session_logind")] 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!())) + } } } } diff --git a/src/backend/session/dbus/logind.rs b/src/backend/session/dbus/logind.rs index 54a3e4c..959c373 100644 --- a/src/backend/session/dbus/logind.rs +++ b/src/backend/session/dbus/logind.rs @@ -42,8 +42,9 @@ use std::path::Path; use std::rc::{Rc, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; use systemd::login; -use wayland_server::EventLoopHandle; -use wayland_server::sources::{EventSource, FdEventSource, FdEventSourceImpl, FdInterest}; +use wayland_server::LoopToken; +use wayland_server::commons::Implementation; +use wayland_server::sources::{FdEvent, FdInterest, Source}; struct LogindSessionImpl { conn: RefCell, @@ -62,6 +63,7 @@ pub struct LogindSession { } /// `SessionNotifier` via the logind dbus interface +#[derive(Clone)] pub struct LogindSessionNotifier { internal: Rc, } @@ -186,7 +188,11 @@ impl LogindSessionNotifier { impl LogindSessionImpl { 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>, ) -> Result 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 { let message = if let ConnectionItem::Signal(ref s) = item { s @@ -246,7 +252,7 @@ impl LogindSessionImpl { //So lets just put it to sleep.. forever for signal in &mut *self.signals.borrow_mut() { if let &mut Some(ref mut signal) = signal { - signal.pause(evlh, None); + signal.pause(None); } } self.active.store(false, Ordering::SeqCst); @@ -263,7 +269,7 @@ impl LogindSessionImpl { ); for signal in &mut *self.signals.borrow_mut() { 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), @@ -289,7 +295,7 @@ impl LogindSessionImpl { debug!(self.logger, "Reactivating device ({},{})", major, minor); for signal in &mut *self.signals.borrow_mut() { 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; fn register>( - &mut self, signal: &mut A + &mut self, + signal: &mut A, ) -> Self::Id { self.internal .signals @@ -422,7 +429,7 @@ impl SessionNotifier for LogindSessionNotifier { pub struct BoundLogindSession { notifier: LogindSessionNotifier, _watches: Vec, - sources: Vec>>, + sources: Vec>, } /// 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 /// session state and call it's `SessionObservers`. pub fn logind_session_bind( - notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle + notifier: LogindSessionNotifier, + token: &LoopToken, ) -> ::std::result::Result { let watches = notifier.internal.conn.borrow().watch_fds(); @@ -443,14 +451,9 @@ pub fn logind_session_bind( let mut interest = FdInterest::empty(); interest.set(FdInterest::READ, watch.readable()); interest.set(FdInterest::WRITE, watch.writable()); - evlh.add_fd_event_source( - watch.fd(), - fd_event_source_implementation(), - notifier.internal.clone(), - interest, - ) + token.add_fd_event_source(watch.fd(), interest, notifier.clone()) }) - .collect::<::std::result::Result>>, (IoError, _)>>() + .collect::<::std::result::Result>, (IoError, _)>>() .map_err(|(err, _)| { ( err, @@ -492,35 +495,40 @@ impl Drop for LogindSessionNotifier { } } -fn fd_event_source_implementation() -> FdEventSourceImpl> { - FdEventSourceImpl { - ready: |evlh, session, fd, interest| { - let conn = session.conn.borrow(); - let items = conn.watch_handle( - fd, - match interest { - 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, - _ => return, - }, - ); - if let Err(err) = session.handle_signals(evlh, items) { - error!(session.logger, "Error handling dbus signals: {}", err); +impl Implementation<(), FdEvent> for LogindSessionNotifier { + fn receive(&mut self, event: FdEvent, (): ()) { + match event { + FdEvent::Ready { fd, mask } => { + let conn = self.internal.conn.borrow(); + let items = conn.watch_handle( + fd, + match mask { + 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, + _ => return, + }, + ); + if let Err(err) = self.internal.handle_signals(items) { + error!(self.internal.logger, "Error handling dbus signals: {}", err); + } } - }, - error: |evlh, session, fd, error| { - warn!(session.logger, "Error on dbus connection: {:?}", error); - // handle the remaining messages, they might contain the SessionRemoved event - // in case the server did close the connection. - let conn = session.conn.borrow(); - let items = conn.watch_handle(fd, WatchEvent::Error as u32); - if let Err(err) = session.handle_signals(evlh, items) { - error!(session.logger, "Error handling dbus signals: {}", err); + FdEvent::Error { fd, error } => { + warn!( + self.internal.logger, + "Error on dbus connection: {:?}", error + ); + // handle the remaining messages, they might contain the SessionRemoved event + // in case the server did close the connection. + let conn = self.internal.conn.borrow(); + 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); + } } - }, + } } } diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index eaeded8..f9a5c71 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -59,8 +59,9 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "backend_session_udev")] use udev::Context; -use wayland_server::EventLoopHandle; -use wayland_server::sources::SignalEventSource; +use wayland_server::LoopToken; +use wayland_server::commons::Implementation; +use wayland_server::sources::{SignalEvent, Source}; #[allow(dead_code)] mod tty { @@ -171,8 +172,11 @@ impl DirectSession { .new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt")); let fd = tty.map(|path| { - open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty()) - .chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy()))) + open( + 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(""))))?; let active = Arc::new(AtomicBool::new(true)); @@ -345,7 +349,8 @@ impl SessionNotifier for DirectSessionNotifier { type Id = Id; fn register>( - &mut self, signal: &mut A + &mut self, + signal: &mut A, ) -> Self::Id { self.signals.push(Some(Box::new(signal.observer()))); 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`. /// /// 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 /// session state and call it's `SessionObservers`. pub fn direct_session_bind( - notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle -) -> ::std::result::Result, (IoError, DirectSessionNotifier)> { + notifier: DirectSessionNotifier, + token: &LoopToken, +) -> ::std::result::Result, (IoError, DirectSessionNotifier)> { let signal = notifier.signal; - evlh.add_signal_event_source( - |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, - ) + token.add_signal_event_source(signal, notifier) } error_chain! { diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index a11c6d3..de8b430 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -16,7 +16,6 @@ use std::os::unix::io::RawFd; use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use wayland_server::EventLoopHandle; /// 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 /// (major, minor) is provided. All observers not using the specified device should /// 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 /// /// 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 /// should not be used anymore and be closed. All observers not using the specified device should /// ignore the signal in that case. - fn activate(&mut self, evlh: &mut EventLoopHandle, device: Option<(u32, u32, Option)>); + fn activate(&mut self, device: Option<(u32, u32, Option)>); } impl Session for () { diff --git a/src/backend/udev.rs b/src/backend/udev.rs index c849d27..674ef1d 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -24,8 +24,9 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::path::{Path, PathBuf}; use std::rc::{Rc, Weak}; use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult}; -use wayland_server::EventLoopHandle; -use wayland_server::sources::{EventSource, FdEventSource, FdEventSourceImpl, FdInterest}; +use wayland_server::LoopToken; +use wayland_server::commons::Implementation; +use wayland_server::sources::{FdEvent, FdInterest, Source}; /// Udev's `DrmDevice` type based on the underlying session pub struct SessionFdDrmDevice(RawFd); @@ -48,11 +49,13 @@ pub struct UdevBackend< S: Session + 'static, T: UdevHandler + 'static, > { - devices: Rc, H)>>>>, + _handler: ::std::marker::PhantomData, + devices: Rc, Rc>>)>>>, monitor: MonitorSocket, session: S, handler: T, logger: ::slog::Logger, + token: LoopToken, } impl + 'static, S: Session + 'static, T: UdevHandler + 'static> @@ -67,7 +70,11 @@ impl + 'static, S: Session + 'static, T: UdevH /// `handler` - User-provided handler to respond to any detected changes /// `logger` - slog Logger to be used by the backend and its `DrmDevices`. 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> where L: Into>, @@ -81,7 +88,7 @@ impl + 'static, S: Session + 'static, T: UdevH .flat_map(|path| { 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), Err(err) => { warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); @@ -94,13 +101,13 @@ impl + 'static, S: Session + 'static, T: UdevH Ok(mut device) => { let devnum = device.device_id(); let fd = device.as_raw_fd(); - match handler.device_added(evlh, &mut device) { + match handler.device_added(&mut device) { Some(drm_handler) => { - match drm_device_bind(&mut evlh, device, drm_handler) { - Ok(event_source) => Some((devnum, event_source)), + match drm_device_bind(&token, device, drm_handler) { + Ok((event_source, device)) => Some((devnum, (event_source, device))), Err((err, (mut device, _))) => { warn!(logger, "Failed to bind device. Error: {:?}.", err); - handler.device_removed(evlh, &mut device); + handler.device_removed(&mut device); drop(device); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); @@ -124,7 +131,7 @@ impl + 'static, S: Session + 'static, T: UdevH } } }) - .collect::, H)>>>(); + .collect::>(); let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?; builder @@ -135,20 +142,25 @@ impl + 'static, S: Session + 'static, T: UdevH .chain_err(|| ErrorKind::FailedToInitMonitor)?; Ok(UdevBackend { + _handler: ::std::marker::PhantomData, devices: Rc::new(RefCell::new(devices)), monitor, session, handler, logger, + token, }) } /// 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(); - for (_, event_source) in devices.drain() { - let (mut device, _) = event_source.remove(); - self.handler.device_removed(evlh, &mut device); + for (_, (event_source, device)) in devices.drain() { + event_source.remove(); + 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(); drop(device); if let Err(err) = self.session.close(fd) { @@ -163,8 +175,8 @@ impl + 'static, S: Session + 'static, T: UdevH } /// `SessionObserver` linked to the `UdevBackend` it was created from. -pub struct UdevBackendObserver + 'static> { - devices: Weak, H)>>>>, +pub struct UdevBackendObserver { + devices: Weak, Rc>>)>>>, logger: ::slog::Logger, } @@ -172,9 +184,9 @@ impl< H: DrmHandler + 'static, S: Session + 'static, T: UdevHandler + 'static, -> AsSessionObserver> for UdevBackend +> AsSessionObserver for UdevBackend { - fn observer(&mut self) -> UdevBackendObserver { + fn observer(&mut self) -> UdevBackendObserver { UdevBackendObserver { devices: Rc::downgrade(&self.devices), logger: self.logger.clone(), @@ -182,25 +194,21 @@ impl< } } -impl + 'static> SessionObserver for UdevBackendObserver { - fn pause<'a>(&mut self, evlh: &mut EventLoopHandle, devnum: Option<(u32, u32)>) { +impl SessionObserver for UdevBackendObserver { + fn pause<'a>(&mut self, devnum: Option<(u32, u32)>) { if let Some(devices) = self.devices.upgrade() { - for fd_event_source in devices.borrow_mut().values_mut() { - fd_event_source.with_idata(evlh, |&mut (ref mut device, _), evlh| { - info!(self.logger, "changed successful"); - device.observer().pause(evlh, devnum); - }) + for &mut (_, ref device) in devices.borrow_mut().values_mut() { + info!(self.logger, "changed successful"); + device.borrow_mut().observer().pause(devnum); } } } - fn activate<'a>(&mut self, evlh: &mut EventLoopHandle, devnum: Option<(u32, u32, Option)>) { + fn activate<'a>(&mut self, devnum: Option<(u32, u32, Option)>) { if let Some(devices) = self.devices.upgrade() { - for fd_event_source in devices.borrow_mut().values_mut() { - fd_event_source.with_idata(evlh, |&mut (ref mut device, _), evlh| { - info!(self.logger, "changed successful"); - device.observer().activate(evlh, devnum); - }) + for &mut (_, ref device) in devices.borrow_mut().values_mut() { + info!(self.logger, "changed successful"); + device.borrow_mut().observer().activate(devnum); } } } @@ -210,143 +218,153 @@ impl + 'static> SessionObserver for UdevBacken /// /// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`. /// No runtime functionality can be provided without using this function. -pub fn udev_backend_bind( - evlh: &mut EventLoopHandle, udev: UdevBackend -) -> ::std::result::Result>, (IoError, UdevBackend)> +pub fn udev_backend_bind( + token: &LoopToken, + udev: UdevBackend, +) -> ::std::result::Result, (IoError, UdevBackend)> where H: DrmHandler + 'static, T: UdevHandler + 'static, S: Session + 'static, { 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() -> FdEventSourceImpl> +impl Implementation<(), FdEvent> for UdevBackend where H: DrmHandler + 'static, T: UdevHandler + 'static, S: Session + 'static, { - FdEventSourceImpl { - ready: |mut evlh, udev, _, _| { - let events = udev.monitor.clone().collect::>(); - for event in events { - match event.event_type() { - // New device - EventType::Add => { - info!(udev.logger, "Device Added"); - if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { - let mut device = { - match DrmDevice::new( - { - let logger = udev.logger.clone(); - match udev.session.open( - path, - fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY - | fcntl::O_NONBLOCK, - ) { - Ok(fd) => SessionFdDrmDevice(fd), - Err(err) => { + fn receive(&mut self, event: FdEvent, (): ()) { + match event { + FdEvent::Ready { .. } => { + let events = self.monitor.clone().collect::>(); + for event in events { + match event.event_type() { + // New device + EventType::Add => { + info!(self.logger, "Device Added"); + if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { + let mut device = { + match DrmDevice::new( + { + let logger = self.logger.clone(); + match self.session.open( + path, + fcntl::OFlag::O_RDWR | fcntl::OFlag::O_CLOEXEC + | fcntl::OFlag::O_NOCTTY + | fcntl::OFlag::O_NONBLOCK, + ) { + 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!( - logger, - "Unable to open drm device {:?}, Error: {:?}. Skipping", - path, - err + self.logger, + "Failed to bind device. Error: {:?}.", 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; } - } - }; - 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); + None => { + self.handler.device_removed(&mut device); drop(device); - if let Err(err) = udev.session.close(fd) { + if let Err(err) = self.session.close(fd) { warn!( - udev.logger, - "Failed to close dropped device. Error: {:?}. Ignoring", err + self.logger, + "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 - ); }; } } - } - // New connector - EventType::Change => { - info!(udev.logger, "Device Changed"); - if let Some(devnum) = event.devnum() { - info!(udev.logger, "Devnum: {:b}", devnum); - if let Some(fd_event_source) = udev.devices.borrow_mut().get_mut(&devnum) { - let handler = &mut udev.handler; - let logger = &udev.logger; - fd_event_source.with_idata(evlh, move |&mut (ref mut device, _), evlh| { - info!(logger, "changed successful"); - handler.device_changed(evlh, device); - }) - } else { - info!(udev.logger, "changed, but device not tracked by backend"); - }; - } else { - info!(udev.logger, "changed, but no devnum"); + // Device removed + EventType::Remove => { + info!(self.logger, "Device Remove"); + if let Some(devnum) = event.devnum() { + if let Some((fd_event_source, device)) = + self.devices.borrow_mut().remove(&devnum) + { + fd_event_source.remove(); + 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(); + drop(device); + if let Err(err) = self.session.close(fd) { + warn!( + self.logger, + "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"); + } + } + _ => {} } - _ => {} } } - }, - error: |evlh, udev, _, err| udev.handler.error(evlh, err), + FdEvent::Error { error, .. } => self.handler.error(error), + } } } @@ -358,9 +376,7 @@ pub trait UdevHandler + 'static> { /// /// ## Panics /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_added( - &mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice - ) -> Option; + fn device_added(&mut self, device: &mut DrmDevice) -> Option; /// Called when an open device is changed. /// /// This usually indicates that some connectors did become available or were unplugged. The handler @@ -368,7 +384,7 @@ pub trait UdevHandler + 'static> { /// /// ## Panics /// 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); + fn device_changed(&mut self, device: &mut DrmDevice); /// Called when a device was removed. /// /// The device will not accept any operations anymore and its file descriptor will be closed once @@ -376,12 +392,12 @@ pub trait UdevHandler + 'static> { /// /// ## Panics /// 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); + fn device_removed(&mut self, device: &mut DrmDevice); /// Called when the udev context has encountered and error. /// /// ## Panics /// 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 diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 40c538f..ae0a658 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -18,7 +18,7 @@ use std::fmt; use std::rc::Rc; use std::time::Instant; 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, MouseScrollDelta, Touch, TouchPhase, Window as WinitWindow, WindowBuilder, WindowEvent}; @@ -102,7 +102,8 @@ where /// graphics backend trait, from a given `WindowBuilder` struct and a corresponding /// `WinitInputBackend`, which implements the `InputBackend` trait pub fn init_from_builder( - builder: WindowBuilder, logger: L + builder: WindowBuilder, + logger: L, ) -> Result<(WinitGraphicsBackend, WinitInputBackend)> where L: Into>, @@ -124,7 +125,9 @@ where /// `GlAttributes` for further customization of the rendering pipeline and a /// corresponding `WinitInputBackend`, which implements the `InputBackend` trait. pub fn init_from_builder_with_gl_attr( - builder: WindowBuilder, attributes: GlAttributes, logger: L + builder: WindowBuilder, + attributes: GlAttributes, + logger: L, ) -> Result<(WinitGraphicsBackend, WinitInputBackend)> where L: Into>, @@ -185,15 +188,15 @@ where /// Handler trait to recieve window-related events to provide a better *nested* experience. pub trait WinitEventsHandler { /// 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 - 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 - fn focus_changed(&mut self, evlh: &mut EventLoopHandle, focused: bool); + fn focus_changed(&mut self, focused: bool); /// The window needs to be redrawn - fn refresh(&mut self, evlh: &mut EventLoopHandle); + fn refresh(&mut self); /// 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 { @@ -213,7 +216,9 @@ impl GraphicsBackend for WinitGraphicsBackend { } fn set_cursor_representation( - &self, cursor: &Self::CursorFormat, _hotspot: (u32, u32) + &self, + cursor: &Self::CursorFormat, + _hotspot: (u32, u32), ) -> ::std::result::Result<(), ()> { // Cannot log this one, as `CursorFormat` is not `Debug` and should not be debug!(self.logger, "Changing cursor representation"); @@ -621,13 +626,13 @@ impl InputBackend for WinitInputBackend { type TouchCancelEvent = WinitTouchCancelledEvent; type TouchFrameEvent = UnusedEvent; - fn set_handler + 'static>(&mut self, evlh: &mut EventLoopHandle, mut handler: H) { + fn set_handler + 'static>(&mut self, mut handler: H) { if self.handler.is_some() { - self.clear_handler(evlh); + self.clear_handler(); } info!(self.logger, "New input handler set."); 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)); } @@ -637,14 +642,14 @@ impl InputBackend for WinitInputBackend { .map(|handler| handler as &mut InputHandler) } - fn clear_handler(&mut self, evlh: &mut EventLoopHandle) { + fn clear_handler(&mut self) { if let Some(mut handler) = self.handler.take() { trace!( self.logger, "Calling on_seat_destroyed with {:?}", self.seat ); - handler.on_seat_destroyed(evlh, &self.seat); + handler.on_seat_destroyed(&self.seat); } 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 /// not be used anymore as well. - fn dispatch_new_events( - &mut self, evlh: &mut EventLoopHandle - ) -> ::std::result::Result<(), WinitInputError> { + fn dispatch_new_events(&mut self) -> ::std::result::Result<(), WinitInputError> { let mut closed = false; { @@ -701,18 +704,16 @@ impl InputBackend for WinitInputBackend { _ => {} }; 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)) => { - events_handler.moved(evlh, x, y) - } + (WindowEvent::Moved(x, y), _, Some(events_handler)) => events_handler.moved(x, y), (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)) => { - events_handler.hidpi_changed(evlh, factor) + events_handler.hidpi_changed(factor) } ( WindowEvent::KeyboardInput { @@ -737,7 +738,6 @@ impl InputBackend for WinitInputBackend { (scancode, state) ); handler.on_keyboard_key( - evlh, seat, WinitKeyboardInputEvent { time, @@ -756,7 +756,6 @@ impl InputBackend for WinitInputBackend { ) => { trace!(logger, "Calling on_pointer_move_absolute with {:?}", (x, y)); handler.on_pointer_move_absolute( - evlh, seat, WinitMouseMovedEvent { window: window.clone(), @@ -769,7 +768,7 @@ impl InputBackend for WinitInputBackend { (WindowEvent::MouseWheel { delta, .. }, Some(handler), _) => { let event = WinitMouseWheelEvent { time, 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), _) => { trace!( @@ -778,7 +777,6 @@ impl InputBackend for WinitInputBackend { (button, state) ); handler.on_pointer_button( - evlh, seat, WinitMouseInputEvent { time, @@ -799,7 +797,6 @@ impl InputBackend for WinitInputBackend { ) => { trace!(logger, "Calling on_touch_down at {:?}", (x, y)); handler.on_touch_down( - evlh, seat, WinitTouchStartedEvent { window: window.clone(), @@ -821,7 +818,6 @@ impl InputBackend for WinitInputBackend { ) => { trace!(logger, "Calling on_touch_motion at {:?}", (x, y)); handler.on_touch_motion( - evlh, seat, WinitTouchMovedEvent { window: window.clone(), @@ -843,7 +839,6 @@ impl InputBackend for WinitInputBackend { ) => { trace!(logger, "Calling on_touch_motion at {:?}", (x, y)); handler.on_touch_motion( - evlh, seat, WinitTouchMovedEvent { window: window.clone(), @@ -853,7 +848,7 @@ impl InputBackend for WinitInputBackend { }, ); 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 { @@ -865,7 +860,7 @@ impl InputBackend for WinitInputBackend { _, ) => { 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, _, _) => { warn!(logger, "Window closed"); diff --git a/src/wayland/compositor/handlers.rs b/src/wayland/compositor/handlers.rs index da2e760..1334278 100644 --- a/src/wayland/compositor/handlers.rs +++ b/src/wayland/compositor/handlers.rs @@ -1,168 +1,188 @@ -use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole, - SurfaceUserImplementation}; +use super::{CompositorToken, Damage, Rectangle, RectangleKind, Role, RoleType, SubsurfaceRole, SurfaceEvent}; use super::region::RegionData; use super::tree::{Location, SurfaceData}; use std::cell::RefCell; 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}; /* * wl_compositor */ -pub(crate) fn compositor_bind( - evlh: &mut EventLoopHandle, idata: &mut SurfaceIData, _: &Client, - compositor: wl_compositor::WlCompositor, -) where - U: Default + 'static, - R: Default + 'static, - ID: 'static, -{ - trace!(idata.log, "Binding a new wl_compositor."); - evlh.register( - &compositor, - compositor_implementation::(), - idata.clone(), - None, - ); -} - -fn compositor_implementation() -> wl_compositor::Implementation> +pub(crate) fn implement_compositor( + compositor: NewResource, + token: LoopToken, + log: ::slog::Logger, + implem: Rc>, +) -> Resource where U: Default + 'static, R: Default + 'static, - ID: 'static, + Impl: Implementation<(Resource, CompositorToken), SurfaceEvent> + 'static, { - wl_compositor::Implementation { - create_surface: |evlh, idata, _, _, surface| { - unsafe { SurfaceData::::init(&surface) }; - evlh.register( - &surface, - surface_implementation::(), - idata.clone(), - Some(destroy_surface::), - ); + let my_token = token.clone(); + compositor.implement_nonsend( + move |request, _compositor| match request { + wl_compositor::Request::CreateSurface { id } => { + trace!(log, "Creating a new wl_surface."); + implement_surface(id, &token, log.clone(), implem.clone()); + } + wl_compositor::Request::CreateRegion { id } => { + trace!(log, "Creating a new wl_region."); + implement_region(id, &token); + } }, - create_region: |evlh, _, _, _, region| { - unsafe { RegionData::init(®ion) }; - evlh.register(®ion, region_implementation(), (), Some(destroy_region)); - }, - } + None::, + &my_token, + ) } /* * wl_surface */ -/// Internal implementation data of surfaces -/// -/// This type is only visible as type parameter of -/// the `Global` handle you are provided. -pub struct SurfaceIData { +// Internal implementation data of surfaces +pub(crate) struct SurfaceImplem { log: ::slog::Logger, - implem: SurfaceUserImplementation, - idata: Rc>, + implem: + Rc, CompositorToken), SurfaceEvent>>>, } -impl SurfaceIData { - pub(crate) fn make( - log: ::slog::Logger, implem: SurfaceUserImplementation, idata: ID - ) -> SurfaceIData { - SurfaceIData { +impl SurfaceImplem { + fn make(log: ::slog::Logger, implem: Rc>) -> SurfaceImplem + where + Impl: Implementation<(Resource, CompositorToken), SurfaceEvent> + + 'static, + { + SurfaceImplem { log: log, implem: implem, - idata: Rc::new(RefCell::new(idata)), } } } -impl Clone for SurfaceIData { - fn clone(&self) -> SurfaceIData { - SurfaceIData { - log: self.log.clone(), - implem: self.implem, - idata: self.idata.clone(), +impl Implementation, wl_surface::Request> for SurfaceImplem +where + U: 'static, + R: 'static, +{ + fn receive(&mut self, req: wl_surface::Request, surface: Resource) { + match req { + wl_surface::Request::Attach { buffer, x, y } => unsafe { + SurfaceData::::with_data(&surface, |d| { + d.buffer = Some(buffer.map(|b| (b.clone(), (x, y)))) + }); + }, + wl_surface::Request::Damage { + x, + y, + width, + height, + } => unsafe { + SurfaceData::::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::::with_data(&surface, |d| d.opaque_region = attributes); + }, + wl_surface::Request::SetInputRegion { region } => unsafe { + let attributes = region.map(|r| RegionData::get_attributes(&r)); + SurfaceData::::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::::with_data(&surface, |d| d.buffer_transform = transform); + }, + wl_surface::Request::SetBufferScale { scale } => unsafe { + SurfaceData::::with_data(&surface, |d| d.buffer_scale = scale); + }, + wl_surface::Request::DamageBuffer { + x, + y, + width, + height, + } => unsafe { + SurfaceData::::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( -) -> wl_surface::Implementation> { - wl_surface::Implementation { - attach: |_, _, _, surface, buffer, x, y| unsafe { - SurfaceData::::with_data(surface, |d| { - d.buffer = Some(buffer.map(|b| (b.clone_unchecked(), (x, y)))) - }); - }, - damage: |_, _, _, surface, x, y, width, height| unsafe { - SurfaceData::::with_data(surface, |d| { - d.damage = Damage::Surface(Rectangle { - x, - y, - width, - height, - }) - }); - }, - frame: |evlh, idata, _, surface, callback| { - let mut user_idata = idata.idata.borrow_mut(); - 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::::with_data(surface, |d| d.opaque_region = attributes); - }, - set_input_region: |_, _, _, surface, region| unsafe { - let attributes = region.map(|r| RegionData::get_attributes(r)); - SurfaceData::::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::::with_data(surface, |d| d.buffer_transform = transform); - }, - set_buffer_scale: |_, _, _, surface, scale| unsafe { - SurfaceData::::with_data(surface, |d| d.buffer_scale = scale); - }, - damage_buffer: |_, _, _, surface, x, y, width, height| unsafe { - SurfaceData::::with_data(surface, |d| { - d.damage = Damage::Buffer(Rectangle { - x, - y, - width, - height, - }) - }); - }, - destroy: |_, _, _, _| {}, +fn implement_surface( + surface: NewResource, + token: &LoopToken, + log: ::slog::Logger, + implem: Rc>, +) -> Resource +where + U: Default + 'static, + R: Default + 'static, + Impl: Implementation<(Resource, CompositorToken), SurfaceEvent> + 'static, +{ + let surface = surface.implement_nonsend( + SurfaceImplem::make(log, implem), + Some(|surface, _| unsafe { + SurfaceData::::cleanup(&surface); + }), + token, + ); + unsafe { + SurfaceData::::init(&surface); } -} - -fn destroy_surface(surface: &wl_surface::WlSurface) { - unsafe { SurfaceData::::cleanup(surface) } + surface } /* * wl_region */ -pub(crate) fn region_implementation() -> wl_region::Implementation<()> { - wl_region::Implementation { - add: |_, _, _, region, x, y, width, height| { - unsafe { - RegionData::add_rectangle( - region, +pub(crate) struct RegionImplem; + +impl Implementation, wl_region::Request> for RegionImplem { + fn receive(&mut self, request: wl_region::Request, region: Resource) { + unsafe { + match request { + wl_region::Request::Add { + x, + y, + width, + height, + } => RegionData::add_rectangle( + ®ion, RectangleKind::Add, Rectangle { x, @@ -170,13 +190,14 @@ pub(crate) fn region_implementation() -> wl_region::Implementation<()> { width, height, }, - ) - }; - }, - subtract: |_, _, _, region, x, y, width, height| { - unsafe { - RegionData::add_rectangle( - region, + ), + wl_region::Request::Subtract { + x, + y, + width, + height, + } => RegionData::add_rectangle( + ®ion, RectangleKind::Subtract, Rectangle { x, @@ -184,134 +205,148 @@ pub(crate) fn region_implementation() -> wl_region::Implementation<()> { width, height, }, - ) - }; - }, - destroy: |_, _, _, _| {}, + ), + wl_region::Request::Destroy => { + // all is handled by our destructor + } + } + } } } -fn destroy_region(region: &wl_region::WlRegion) { - unsafe { RegionData::cleanup(region) }; +fn implement_region( + region: NewResource, + token: &LoopToken, +) -> Resource { + let region = region.implement_nonsend( + RegionImplem, + Some(|region, _| unsafe { RegionData::cleanup(®ion) }), + token, + ); + unsafe { + RegionData::init(®ion); + } + region } /* * wl_subcompositor */ -pub(crate) fn subcompositor_bind( - evlh: &mut EventLoopHandle, _: &mut (), _: &Client, subcompositor: wl_subcompositor::WlSubcompositor -) where - R: RoleType + Role + 'static, - U: 'static, -{ - evlh.register( - &subcompositor, - subcompositor_implementation::(), - (), - None, - ); -} - -fn subcompositor_implementation() -> wl_subcompositor::Implementation<()> +pub(crate) fn implement_subcompositor( + subcompositor: NewResource, + token: LoopToken, +) -> Resource where R: RoleType + Role + 'static, U: 'static, { - wl_subcompositor::Implementation { - get_subsurface: |evlh, _, _, subcompositor, subsurface, surface, parent| { - if let Err(()) = unsafe { SurfaceData::::set_parent(surface, parent) } { - subcompositor.post_error( - wl_subcompositor::Error::BadSurface as u32, - "Surface already has a role.".into(), - ); - return; + let my_token = token.clone(); + subcompositor.implement_nonsend( + move |request, subcompositor: Resource<_>| match request { + wl_subcompositor::Request::GetSubsurface { + id, + surface, + parent, + } => { + if let Err(()) = unsafe { SurfaceData::::set_parent(&surface, &parent) } { + subcompositor.post_error( + wl_subcompositor::Error::BadSurface as u32, + "Surface already has a role.".into(), + ); + return; + } + let subsurface = implement_subsurface::(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 _); - evlh.register( - &subsurface, - subsurface_implementation::(), - (), - Some(destroy_subsurface::), - ); + wl_subcompositor::Request::Destroy => {} }, - destroy: |_, _, _, _| {}, - } + None::, + &my_token, + ) } /* * wl_subsurface */ -unsafe fn with_subsurface_attributes(subsurface: &wl_subsurface::WlSubsurface, f: F) +unsafe fn with_subsurface_attributes(subsurface: &Resource, f: F) where F: FnOnce(&mut SubsurfaceRole), U: 'static, R: RoleType + Role + 'static, { let ptr = subsurface.get_user_data(); - let surface = &*(ptr as *mut wl_surface::WlSurface); + let surface = &*(ptr as *mut Resource); SurfaceData::::with_role_data::(surface, |d| f(d)) .expect("The surface does not have a subsurface role while it has a wl_subsurface?!"); } -fn subsurface_implementation() -> wl_subsurface::Implementation<()> +fn implement_subsurface( + subsurface: NewResource, + token: &LoopToken, +) -> Resource where - R: RoleType + Role + 'static, U: 'static, + R: RoleType + Role + 'static, { - wl_subsurface::Implementation { - set_position: |_, _, _, subsurface, x, y| unsafe { - with_subsurface_attributes::(subsurface, |attrs| { - attrs.x = x; - attrs.y = y; - }); - }, - place_above: |_, _, _, subsurface, sibling| unsafe { - let ptr = subsurface.get_user_data(); - let surface = &*(ptr as *mut wl_surface::WlSurface); - if let Err(()) = SurfaceData::::reorder(surface, Location::After, sibling) { - subsurface.post_error( - wl_subsurface::Error::BadSurface as u32, - "Provided surface is not a sibling or parent.".into(), - ); + subsurface.implement_nonsend( + |request, subsurface| unsafe { + match request { + wl_subsurface::Request::SetPosition { x, y } => { + with_subsurface_attributes::(&subsurface, |attrs| { + attrs.location = (x, y); + }) + } + wl_subsurface::Request::PlaceAbove { sibling } => { + let surface = &*(subsurface.get_user_data() as *mut Resource); + if let Err(()) = SurfaceData::::reorder(surface, Location::After, &sibling) { + subsurface.post_error( + wl_subsurface::Error::BadSurface as u32, + "Provided surface is not a sibling or parent.".into(), + ) + } + } + wl_subsurface::Request::PlaceBelow { sibling } => { + let surface = &*(subsurface.get_user_data() as *mut Resource); + if let Err(()) = SurfaceData::::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::(&subsurface, |attrs| { + attrs.sync = true; + }) + } + wl_subsurface::Request::SetDesync => { + with_subsurface_attributes::(&subsurface, |attrs| { + attrs.sync = false; + }) + } + wl_subsurface::Request::Destroy => { + // Our destructor already handles it + } } }, - place_below: |_, _, _, subsurface, sibling| unsafe { - let ptr = subsurface.get_user_data(); - let surface = &*(ptr as *mut wl_surface::WlSurface); - if let Err(()) = SurfaceData::::reorder(surface, Location::Before, sibling) { - 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::(subsurface, |attrs| { - attrs.sync = true; - }); - }, - set_desync: |_, _, _, subsurface| unsafe { - with_subsurface_attributes::(subsurface, |attrs| { - attrs.sync = false; - }); - }, - destroy: |_, _, _, _| {}, - } + Some(|subsurface, _| unsafe { + destroy_subsurface::(&subsurface); + }), + token, + ) } -fn destroy_subsurface(subsurface: &wl_subsurface::WlSubsurface) +unsafe fn destroy_subsurface(subsurface: &Resource) where U: 'static, R: RoleType + Role + 'static, { let ptr = subsurface.get_user_data(); subsurface.set_user_data(::std::ptr::null_mut()); - unsafe { - let surface = Box::from_raw(ptr as *mut wl_surface::WlSurface); - if surface.status() == Liveness::Alive { - SurfaceData::::unset_parent(&surface); - } + let surface = Box::from_raw(ptr as *mut Resource); + if surface.is_alive() { + SurfaceData::::unset_parent(&surface); } } diff --git a/src/wayland/compositor/mod.rs b/src/wayland/compositor/mod.rs index 6aedd21..e0c67bf 100644 --- a/src/wayland/compositor/mod.rs +++ b/src/wayland/compositor/mod.rs @@ -29,9 +29,7 @@ //! ``` //! # extern crate wayland_server; //! # #[macro_use] extern crate smithay; -//! use wayland_server::protocol::wl_compositor::WlCompositor; -//! use wayland_server::protocol::wl_subcompositor::WlSubcompositor; -//! use smithay::wayland::compositor::{compositor_init, SurfaceUserImplementation}; +//! use smithay::wayland::compositor::compositor_init; //! //! // 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 @@ -45,21 +43,18 @@ //! define_roles!(MyRoles); //! //! # fn main() { -//! # let (_display, mut event_loop) = wayland_server::create_display(); -//! // define your implementation for surface -//! let my_implementation = SurfaceUserImplementation { -//! commit: |evlh, idata, surface, token| { /* ... */ }, -//! frame: |evlh, idata, surface, callback, token| { /* ... */ } -//! }; -//! // define your implementation data -//! let my_implementation_data = (); -//! +//! # let (mut display, event_loop) = wayland_server::Display::new(); //! // Call the init function: //! let (token, _, _) = compositor_init::( -//! &mut event_loop, -//! my_implementation, // instance of compositor::SurfaceUserImplementation -//! my_implementation_data, // whatever implementation data you need -//! None // put a logger here +//! &mut display, +//! event_loop.token(), +//! |request, (surface, compositor_token)| { +//! /* +//! 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 @@ -82,20 +77,23 @@ //! 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. +use std::cell::RefCell; +use std::rc::Rc; + mod handlers; mod tree; mod region; pub mod roles; -pub use self::handlers::SurfaceIData; use self::region::RegionData; use self::roles::{Role, RoleType, WrongRole}; use self::tree::SurfaceData; pub use self::tree::TraversalAction; use utils::Rectangle; -use wayland_server::{resource_is_registered, EventLoopHandle, Global}; -use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region, - wl_subcompositor, wl_surface}; +use wayland_server::{Display, Global, LoopToken, NewResource, Resource}; +use wayland_server::commons::Implementation; +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 /// should be considered damaged and needs to be redrawn @@ -135,7 +133,7 @@ pub struct SurfaceAttributes { /// 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 /// the surface. - pub buffer: Option>, + pub buffer: Option, (i32, i32))>>, /// Scale of the contents of the buffer, for higher-resolution contents. /// /// If it matches the one of the output displaying this surface, no change @@ -182,12 +180,9 @@ impl Default for SurfaceAttributes { /// Attributes defining the behaviour of a sub-surface relative to its parent #[derive(Copy, Clone, Debug)] 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 - pub x: i32, - /// Vertical location of the top-left corner of this sub-surface relative to - /// the top-left corner of its parent - pub y: i32, + pub location: (i32, i32), /// Sync status of this sub-surface /// /// If `true`, this surface should be repainted synchronously with its parent @@ -199,8 +194,7 @@ pub struct SubsurfaceRole { impl Default for SubsurfaceRole { fn default() -> SubsurfaceRole { SubsurfaceRole { - x: 0, - y: 0, + location: (0, 0), sync: true, } } @@ -240,58 +234,52 @@ impl Default for RegionAttributes { /// This token can be cloned at will, and is the entry-point to /// access data associated with the `wl_surface` and `wl_region` managed /// by the `CompositorGlobal` that provided it. -pub struct CompositorToken { +pub struct CompositorToken { _data: ::std::marker::PhantomData<*mut U>, _role: ::std::marker::PhantomData<*mut R>, - _idata: ::std::marker::PhantomData<*mut ID>, } // we implement them manually because #[derive(..)] would require // U: Clone and R: Clone -impl Copy for CompositorToken {} -impl Clone for CompositorToken { - fn clone(&self) -> CompositorToken { +impl Copy for CompositorToken {} +impl Clone for CompositorToken { + fn clone(&self) -> CompositorToken { *self } } -impl CompositorToken { - pub(crate) fn make() -> CompositorToken { +impl CompositorToken { + pub(crate) fn make() -> CompositorToken { CompositorToken { _data: ::std::marker::PhantomData, _role: ::std::marker::PhantomData, - _idata: ::std::marker::PhantomData, } } } -impl CompositorToken { +impl CompositorToken { /// Access the data of a 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 /// will panic (having more than one compositor is not supported). - pub fn with_surface_data(&self, surface: &wl_surface::WlSurface, f: F) -> T + pub fn with_surface_data(&self, surface: &Resource, f: F) -> T where F: FnOnce(&mut SurfaceAttributes) -> T, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::with_data(surface, f) } } } -impl CompositorToken +impl CompositorToken where U: 'static, R: RoleType + Role + 'static, - ID: 'static, { /// Access the data of a surface tree from bottom to top /// @@ -304,22 +292,22 @@ where /// - The surface object itself /// - a mutable reference to its surface attribute 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. /// /// If the surface not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). pub fn with_surface_tree_upward( - &self, surface: &wl_surface::WlSurface, initial: T, f: F + &self, + surface: &Resource, + initial: T, + f: F, ) -> Result<(), ()> where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, + F: FnMut(&Resource, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { @@ -336,16 +324,16 @@ where /// /// Behavior is the same as `with_surface_tree_upward`. pub fn with_surface_tree_downward( - &self, surface: &wl_surface::WlSurface, initial: T, f: F + &self, + surface: &Resource, + initial: T, + f: F, ) -> Result<(), ()> where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, + F: FnMut(&Resource, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { @@ -360,12 +348,9 @@ where /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn get_parent(&self, surface: &wl_surface::WlSurface) -> Option { + pub fn get_parent(&self, surface: &Resource) -> Option> { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::get_parent(surface) } @@ -375,29 +360,23 @@ where /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn get_children(&self, surface: &wl_surface::WlSurface) -> Vec { + pub fn get_children(&self, surface: &Resource) -> Vec> { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::get_children(surface) } } } -impl CompositorToken { +impl CompositorToken { /// Check wether this surface as a role or not /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// 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) -> bool { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::has_a_role(surface) } @@ -407,15 +386,12 @@ impl CompositorToken { /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn has_role(&self, surface: &wl_surface::WlSurface) -> bool + pub fn has_role(&self, surface: &Resource) -> bool where R: Role, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::has_role::(surface) } @@ -427,16 +403,13 @@ impl CompositorToken { /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn give_role(&self, surface: &wl_surface::WlSurface) -> Result<(), ()> + pub fn give_role(&self, surface: &Resource) -> Result<(), ()> where R: Role, RoleData: Default, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::give_role::(surface) } @@ -449,16 +422,15 @@ impl CompositorToken { /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). pub fn give_role_with( - &self, surface: &wl_surface::WlSurface, data: RoleData + &self, + surface: &Resource, + data: RoleData, ) -> Result<(), RoleData> where R: Role, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::give_role_with::(surface, data) } @@ -470,18 +442,13 @@ impl CompositorToken { /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn with_role_data( - &self, surface: &wl_surface::WlSurface, f: F - ) -> Result + pub fn with_role_data(&self, surface: &Resource, f: F) -> Result where R: Role, F: FnOnce(&mut RoleData) -> T, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::with_role_data::(surface, f) } @@ -493,15 +460,12 @@ impl CompositorToken { /// /// If the surface is not managed by the CompositorGlobal that provided this token, this /// will panic (having more than one compositor is not supported). - pub fn remove_role(&self, surface: &wl_surface::WlSurface) -> Result + pub fn remove_role(&self, surface: &Resource) -> Result where R: Role, { assert!( - resource_is_registered( - surface, - &self::handlers::surface_implementation::() - ), + surface.is_implemented_with::>(), "Accessing the data of foreign surfaces is not supported." ); unsafe { SurfaceData::::remove_role::(surface) } @@ -511,10 +475,10 @@ impl CompositorToken { /// /// If the region is not managed by the CompositorGlobal that provided this token, this /// 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) -> RegionAttributes { assert!( - resource_is_registered(region, &self::handlers::region_implementation()), - "Accessing the data of foreign surfaces is not supported." + region.is_implemented_with::(), + "Accessing the data of foreign regions is not supported." ); unsafe { RegionData::get_attributes(region) } } @@ -528,46 +492,50 @@ impl CompositorToken { /// /// It also returns the two global handles, in case you whish to remove these /// globals from the event loop in the future. -pub fn compositor_init( - evlh: &mut EventLoopHandle, implem: SurfaceUserImplementation, idata: ID, logger: L +pub fn compositor_init( + display: &mut Display, + token: LoopToken, + implem: Impl, + logger: L, ) -> ( - CompositorToken, - Global>, - Global, + CompositorToken, + Global, + Global, ) where L: Into>, U: Default + 'static, R: Default + RoleType + Role + 'static, - ID: 'static, + Impl: Implementation<(Resource, CompositorToken), SurfaceEvent> + 'static, { - let log = ::slog_or_stdlog(logger); - let idata = self::handlers::SurfaceIData::make( - log.new(o!("smithay_module" => "compositor_handler")), - implem, - idata, - ); - let compositor_global_token = - evlh.register_global::(4, self::handlers::compositor_bind, idata); - let subcompositor_global_token = evlh.register_global::( - 1, - self::handlers::subcompositor_bind::, - (), - ); + let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "compositor_handler")); + let implem = Rc::new(RefCell::new(implem)); - ( - CompositorToken::make(), - compositor_global_token, - subcompositor_global_token, - ) + let comp_token = token.clone(); + let sub_token = token.clone(); + + let compositor = display.create_global(&token, 4, move |_version, new_compositor| { + self::handlers::implement_compositor::( + new_compositor, + comp_token.clone(), + log.clone(), + implem.clone(), + ); + }); + + let subcompositor = display.create_global(&token, 1, move |_version, new_subcompositor| { + self::handlers::implement_subcompositor::(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 -/// are forwarded directly to this implementation that you must provide -/// at creation of the compositor global. -pub struct SurfaceUserImplementation { +/// The global provided by smithay cannot process these events for you, so +/// they are forwarded directly via your provided implementation, and are +/// described by this global. +pub enum SurfaceEvent { /// The double-buffered state has been validated by the client /// /// At this point, the pending state that has been accumulated in the `SurfaceAttributes` associated @@ -575,12 +543,7 @@ pub struct SurfaceUserImplementation { /// /// 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 - pub commit: fn( - evlh: &mut EventLoopHandle, - idata: &mut ID, - surface: &wl_surface::WlSurface, - token: CompositorToken, - ), + Commit, /// 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 @@ -588,18 +551,8 @@ pub struct SurfaceUserImplementation { /// /// 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 - pub frame: fn( - evlh: &mut EventLoopHandle, - idata: &mut ID, - surface: &wl_surface::WlSurface, - callback: wl_callback::WlCallback, - token: CompositorToken, - ), -} - -impl Copy for SurfaceUserImplementation {} -impl Clone for SurfaceUserImplementation { - fn clone(&self) -> SurfaceUserImplementation { - *self - } + Frame { + /// The created `WlCallback` + callback: NewResource, + }, } diff --git a/src/wayland/compositor/region.rs b/src/wayland/compositor/region.rs index 437477b..62d93b5 100644 --- a/src/wayland/compositor/region.rs +++ b/src/wayland/compositor/region.rs @@ -10,29 +10,33 @@ pub struct RegionData { impl RegionData { /// 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) { 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 - pub unsafe fn cleanup(region: &wl_region::WlRegion) { + pub unsafe fn cleanup(region: &Resource) { let ptr = region.get_user_data(); region.set_user_data(::std::ptr::null_mut()); let _my_data_mutex: Box> = Box::from_raw(ptr as *mut _); } - unsafe fn get_data(region: &wl_region::WlRegion) -> &Mutex { + unsafe fn get_data(region: &Resource) -> &Mutex { let ptr = region.get_user_data(); &*(ptr as *mut _) } - pub unsafe fn get_attributes(region: &wl_region::WlRegion) -> RegionAttributes { + pub unsafe fn get_attributes(region: &Resource) -> RegionAttributes { let data_mutex = Self::get_data(region); let data_guard = data_mutex.lock().unwrap(); data_guard.attributes.clone() } - pub unsafe fn add_rectangle(region: &wl_region::WlRegion, kind: RectangleKind, rect: Rectangle) { + pub unsafe fn add_rectangle( + region: &Resource, + kind: RectangleKind, + rect: Rectangle, + ) { let data_mutex = Self::get_data(region); let mut data_guard = data_mutex.lock().unwrap(); data_guard.attributes.rects.push((kind, rect)); diff --git a/src/wayland/compositor/roles.rs b/src/wayland/compositor/roles.rs index 847c48c..ef99c50 100644 --- a/src/wayland/compositor/roles.rs +++ b/src/wayland/compositor/roles.rs @@ -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 { Ok(data) } 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 { Ok(data) } 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 let temp = ::std::mem::replace(self, $enum_name::NoRole); if let $enum_name::$role_name(data) = temp { diff --git a/src/wayland/compositor/tree.rs b/src/wayland/compositor/tree.rs index 10fd2a1..d0fda29 100644 --- a/src/wayland/compositor/tree.rs +++ b/src/wayland/compositor/tree.rs @@ -1,8 +1,8 @@ use super::{SubsurfaceRole, SurfaceAttributes}; use super::roles::*; use std::sync::Mutex; -use wayland_server::{Liveness, Resource}; -use wayland_server::protocol::wl_surface; +use wayland_server::Resource; +use wayland_server::protocol::wl_surface::WlSurface; /// Node of a subsurface tree, holding some user specified data type U /// 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 /// is correctly initialized regarding its `user_data`. pub struct SurfaceData { - parent: Option, - children: Vec, + parent: Option>, + children: Vec>, role: R, attributes: SurfaceAttributes, } @@ -56,7 +56,7 @@ impl SurfaceData { } /// 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) { surface.set_user_data(Box::into_raw(Box::new(Mutex::new(SurfaceData::::new()))) as *mut _) } } @@ -66,13 +66,13 @@ where U: 'static, R: 'static, { - unsafe fn get_data(surface: &wl_surface::WlSurface) -> &Mutex> { + unsafe fn get_data(surface: &Resource) -> &Mutex> { let ptr = surface.get_user_data(); &*(ptr as *mut _) } /// 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) { let ptr = surface.get_user_data(); surface.set_user_data(::std::ptr::null_mut()); let my_data_mutex: Box>> = Box::from_raw(ptr as *mut _); @@ -99,31 +99,31 @@ where } impl SurfaceData { - pub unsafe fn has_a_role(surface: &wl_surface::WlSurface) -> bool { - debug_assert!(surface.status() == Liveness::Alive); + pub unsafe fn has_a_role(surface: &Resource) -> bool { + debug_assert!(surface.is_alive()); let data_mutex = Self::get_data(surface); let data_guard = data_mutex.lock().unwrap(); ::has_role(&data_guard.role) } /// Check wether a surface has a given role - pub unsafe fn has_role(surface: &wl_surface::WlSurface) -> bool + pub unsafe fn has_role(surface: &Resource) -> bool where R: Role, { - debug_assert!(surface.status() == Liveness::Alive); + debug_assert!(surface.is_alive()); let data_mutex = Self::get_data(surface); let data_guard = data_mutex.lock().unwrap(); >::has(&data_guard.role) } /// Register that this surface has a role, fails if it already has one - pub unsafe fn give_role(surface: &wl_surface::WlSurface) -> Result<(), ()> + pub unsafe fn give_role(surface: &Resource) -> Result<(), ()> where R: Role, RoleData: Default, { - debug_assert!(surface.status() == Liveness::Alive); + debug_assert!(surface.is_alive()); let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); >::set(&mut data_guard.role) @@ -133,12 +133,13 @@ impl SurfaceData { /// /// Fails if it already has one and returns the data pub unsafe fn give_role_with( - surface: &wl_surface::WlSurface, data: RoleData + surface: &Resource, + data: RoleData, ) -> Result<(), RoleData> where R: Role, { - debug_assert!(surface.status() == Liveness::Alive); + debug_assert!(surface.is_alive()); let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); >::set_with(&mut data_guard.role, data) @@ -148,25 +149,23 @@ impl SurfaceData { /// /// 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. - pub unsafe fn remove_role(surface: &wl_surface::WlSurface) -> Result + pub unsafe fn remove_role(surface: &Resource) -> Result where R: Role, { - debug_assert!(surface.status() == Liveness::Alive); + debug_assert!(surface.is_alive()); let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); >::unset(&mut data_guard.role) } /// Access to the role data - pub unsafe fn with_role_data( - surface: &wl_surface::WlSurface, f: F - ) -> Result + pub unsafe fn with_role_data(surface: &Resource, f: F) -> Result where R: Role, F: FnOnce(&mut RoleData) -> T, { - debug_assert!(surface.status() == Liveness::Alive); + debug_assert!(surface.is_alive()); let data_mutex = Self::get_data(surface); let mut data_guard = data_mutex.lock().unwrap(); let data = >::data_mut(&mut data_guard.role)?; @@ -179,11 +178,9 @@ impl + 'static> SurfaceData /// /// if this surface already has a role, does nothing and fails, otherwise /// its role is now to be a subsurface - pub unsafe fn set_parent( - child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface - ) -> Result<(), ()> { - debug_assert!(child.status() == Liveness::Alive); - debug_assert!(parent.status() == Liveness::Alive); + pub unsafe fn set_parent(child: &Resource, parent: &Resource) -> Result<(), ()> { + debug_assert!(child.is_alive()); + debug_assert!(parent.is_alive()); // change child's parent { @@ -192,14 +189,14 @@ impl + 'static> SurfaceData // if surface already has a role, it cannot become a subsurface >::set(&mut child_guard.role)?; 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 // double scoping is to be robust to have a child be its own parent { let parent_mutex = Self::get_data(parent); let mut parent_guard = parent_mutex.lock().unwrap(); - parent_guard.children.push(child.clone_unchecked()) + parent_guard.children.push(child.clone()) } Ok(()) } @@ -207,8 +204,8 @@ impl + 'static> SurfaceData /// Remove a pre-existing parent of this child /// /// Does nothing if it has no parent - pub unsafe fn unset_parent(child: &wl_surface::WlSurface) { - debug_assert!(child.status() == Liveness::Alive); + pub unsafe fn unset_parent(child: &Resource) { + debug_assert!(child.is_alive()); let old_parent = { let child_mutex = Self::get_data(child); let mut child_guard = child_mutex.lock().unwrap(); @@ -229,44 +226,38 @@ impl + 'static> SurfaceData } /// Retrieve the parent surface (if any) of this surface - pub unsafe fn get_parent(child: &wl_surface::WlSurface) -> Option { + pub unsafe fn get_parent(child: &Resource) -> Option> { let child_mutex = Self::get_data(child); 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 - pub unsafe fn get_children(child: &wl_surface::WlSurface) -> Vec { + pub unsafe fn get_children(child: &Resource) -> Vec> { let child_mutex = Self::get_data(child); let child_guard = child_mutex.lock().unwrap(); - child_guard - .children - .iter() - .map(|p| p.clone_unchecked()) - .collect() + child_guard.children.iter().map(|p| p.clone()).collect() } /// Reorders a surface relative to one of its sibling /// /// Fails if `relative_to` is not a sibling or parent of `surface`. pub unsafe fn reorder( - surface: &wl_surface::WlSurface, to: Location, relative_to: &wl_surface::WlSurface + surface: &Resource, + to: Location, + relative_to: &Resource, ) -> Result<(), ()> { let parent = { let data_mutex = Self::get_data(surface); let data_guard = data_mutex.lock().unwrap(); - data_guard - .parent - .as_ref() - .map(|p| p.clone_unchecked()) - .unwrap() + data_guard.parent.as_ref().map(|p| p.clone()).unwrap() }; if parent.equals(relative_to) { // TODO: handle positioning relative to parent return Ok(()); } - fn index_of(surface: &wl_surface::WlSurface, slice: &[wl_surface::WlSurface]) -> Option { + fn index_of(surface: &Resource, slice: &[Resource]) -> Option { for (i, s) in slice.iter().enumerate() { if s.equals(surface) { return Some(i); @@ -300,7 +291,7 @@ impl SurfaceData { /// /// Note that an internal lock is taken during access of this data, /// so the tree cannot be manipulated at the same time - pub unsafe fn with_data(surface: &wl_surface::WlSurface, f: F) -> T + pub unsafe fn with_data(surface: &Resource, f: F) -> T where F: FnOnce(&mut SurfaceAttributes) -> T, { @@ -317,17 +308,20 @@ impl SurfaceData { /// /// The callback returns wether the traversal should continue or not. Returning /// false will cause an early-stopping. - pub unsafe fn map_tree(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool) + pub unsafe fn map_tree(root: &Resource, initial: T, mut f: F, reverse: bool) where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, + F: FnMut(&Resource, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { // helper function for recursion unsafe fn map( - surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, f: &mut F, + surface: &Resource, + root: &Resource, + initial: &T, + f: &mut F, reverse: bool, ) -> bool where - F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, + F: FnMut(&Resource, &mut SurfaceAttributes, &mut R, &T) -> TraversalAction, { // stop if we met the root, so to not deadlock/inifinte loop if surface.equals(root) { diff --git a/src/wayland/output/mod.rs b/src/wayland/output/mod.rs index 3acf5fc..ee60dfd 100644 --- a/src/wayland/output/mod.rs +++ b/src/wayland/output/mod.rs @@ -10,9 +10,9 @@ //! You need to instanciate an `Output` for each output global you want //! to advertize to clients. //! -//! Just insert it in your event loop using the `Output::new(..)` method. -//! It returns a state token that gives you access to the `Output` in order -//! to change it if needed (if the current resolution mode changes for example), +//! Just add it to your Display using the `Output::new(..)` method. +//! You can use the returned `Output` to change the properties of your +//! output (if the current resolution mode changes for example), //! it'll automatically forward any changes to the clients. //! //! ``` @@ -22,10 +22,11 @@ //! use wayland_server::protocol::wl_output; //! //! # fn main() { -//! # let (display, mut event_loop) = wayland_server::create_display(); -//! // Insert the Output with given name and physical properties -//! let (output_state_token, _output_global) = Output::new( -//! &mut event_loop, // the event loop +//! # let (mut display, event_loop) = wayland_server::Display::new(); +//! // Create the Output with given name and physical properties +//! let (output, _output_global) = Output::new( +//! &mut display, // the display +//! event_loop.token(), // the LoopToken //! "output-0".into(), // the name of this output, //! PhysicalProperties { //! width: 200, // width in mm @@ -37,33 +38,33 @@ //! None // insert a logger here //! ); //! // Now you can configure it -//! { -//! let output = event_loop.state().get_mut(&output_state_token); -//! // set the current state -//! output.change_current_state( -//! Some(Mode { width: 1902, height: 1080, refresh: 60000 }), // the resolution mode, -//! Some(wl_output::Transform::Normal), // global screen transformation -//! Some(1), // global screen scaling factor -//! ); -//! // set the prefered mode -//! output.set_preferred(Mode { width: 1920, height: 1080, 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 }); -//! } +//! output.change_current_state( +//! Some(Mode { width: 1902, height: 1080, refresh: 60000 }), // the resolution mode, +//! Some(wl_output::Transform::Normal), // global screen transformation +//! Some(1), // global screen scaling factor +//! ); +//! // set the prefered mode +//! output.set_preferred(Mode { width: 1920, height: 1080, 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 wayland_server::protocol::wl_output; +use std::sync::{Arc, Mutex}; + +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 /// /// A possible combination of dimensions and refresh rate for an output. /// /// This should only describe the characteristics of the video driver, /// not taking into account any global scaling. +#[derive(Copy, Clone, PartialEq)] pub struct Mode { /// The width in pixels pub width: i32, @@ -82,68 +83,28 @@ pub struct PhysicalProperties { /// The height in milimeters pub height: i32, /// The subpixel geometry - pub subpixel: wl_output::Subpixel, + pub subpixel: Subpixel, /// Textual representation of the manufacturer pub maker: String, /// Textual representation of the model pub model: String, } -/// 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 { +struct Inner { name: String, log: ::slog::Logger, - instances: Vec, + instances: Vec>, physical: PhysicalProperties, location: (i32, i32), - transform: wl_output::Transform, + transform: Transform, scale: i32, modes: Vec, current_mode: Option, preferred_mode: Option, } -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( - evlh: &mut EventLoopHandle, name: String, physical: PhysicalProperties, logger: L - ) -> ( - StateToken, - Global>, - ) - where - L: Into>, - { - 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) { +impl Inner { + fn new_global(&mut self, output: Resource) { trace!(self.log, "New global instanciated."); if self.modes.is_empty() { @@ -158,51 +119,140 @@ impl Output { self.send_geometry(&output); for &mode in &self.modes { - let mut flags = wl_output::Mode::empty(); + let mut flags = WMode::empty(); if Some(mode) == self.current_mode { - flags |= wl_output::Mode::Current; + flags |= WMode::Current; } 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 { - output.scale(self.scale); - output.done(); + output.send(Event::Scale { factor: self.scale }); + output.send(Event::Done); } self.instances.push(output); } - fn send_geometry(&self, output: &wl_output::WlOutput) { - output.geometry( - self.location.0, - self.location.1, - self.physical.width, - self.physical.height, - self.physical.subpixel, - self.physical.maker.clone(), - self.physical.model.clone(), - self.transform, - ); + fn send_geometry(&self, output: &Resource) { + output.send(Event::Geometry { + x: self.location.0, + y: self.location.1, + physical_width: self.physical.width, + physical_height: self.physical.height, + subpixel: self.physical.subpixel, + make: self.physical.maker.clone(), + model: self.physical.model.clone(), + transform: self.transform, + }); + } +} + +struct InnerWrapper { + inner: Arc>, +} + +// 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, Request> for InnerWrapper { + fn receive(&mut self, req: Request, _res: Resource) { + // 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>, +} + +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( + display: &mut Display, + token: LoopToken, + name: String, + physical: PhysicalProperties, + logger: L, + ) -> (Output, Global) + where + L: Into>, + { + 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 = + 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 /// /// If the provided mode was not previously known to this output, it is added to its /// internal list. - pub fn set_preferred(&mut self, mode: Mode) { - self.preferred_mode = Some(mode); - if self.modes.iter().find(|&m| *m == mode).is_none() { - self.modes.push(mode); + pub fn set_preferred(&self, mode: Mode) { + let mut inner = self.inner.lock().unwrap(); + inner.preferred_mode = Some(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 - pub fn add_mode(&mut self, mode: Mode) { - if self.modes.iter().find(|&m| *m == mode).is_none() { - self.modes.push(mode); + pub fn add_mode(&self, mode: Mode) { + let mut inner = self.inner.lock().unwrap(); + 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 /// allow it), but it won't be advertized to now clients from now on. - pub fn delete_mode(&mut self, mode: Mode) { - self.modes.retain(|&m| m != mode); - if self.current_mode == Some(mode) { - self.current_mode = None; + pub fn delete_mode(&self, mode: Mode) { + let mut inner = self.inner.lock().unwrap(); + inner.modes.retain(|&m| m != mode); + if inner.current_mode == Some(mode) { + inner.current_mode = None; } - if self.preferred_mode == Some(mode) { - self.preferred_mode = None; + if inner.preferred_mode == Some(mode) { + inner.preferred_mode = None; } } @@ -230,75 +281,58 @@ impl Output { /// /// By default, transform status is `Normal`, and scale is `1`. pub fn change_current_state( - &mut self, new_mode: Option, new_transform: Option, + &self, + new_mode: Option, + new_transform: Option, new_scale: Option, ) { + let mut inner = self.inner.lock().unwrap(); if let Some(mode) = new_mode { - if self.modes.iter().find(|&m| *m == mode).is_none() { - self.modes.push(mode); + if inner.modes.iter().find(|&m| *m == mode).is_none() { + inner.modes.push(mode); } - self.current_mode = new_mode; + inner.current_mode = new_mode; } if let Some(transform) = new_transform { - self.transform = transform; + inner.transform = transform; } if let Some(scale) = new_scale { - self.scale = scale; + inner.scale = scale; } - let mut flags = wl_output::Mode::Current; - if self.preferred_mode == new_mode { - flags |= wl_output::Mode::Preferred; + let mut flags = WMode::Current; + if inner.preferred_mode == new_mode { + flags |= WMode::Preferred; } - for output in &self.instances { + for output in &inner.instances { 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() { - self.send_geometry(output); + inner.send_geometry(output); } if let Some(scale) = new_scale { if output.version() >= 2 { - output.scale(scale); + output.send(Event::Scale { factor: scale }); } } if output.version() >= 2 { - output.done(); + output.send(Event::Done); } } } /// Chech is given wl_output instance is managed by this `Output`. - pub fn owns(&self, output: &wl_output::WlOutput) -> bool { - self.instances.iter().any(|o| o.equals(output)) - } - - /// Cleanup internal `wl_output` instances list - /// - /// Clients do not necessarily notify the server on the destruction - /// 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, _: &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> { - wl_output::Implementation { - release: |evlh, token, _, output| { - evlh.state() - .get_mut(token) - .instances - .retain(|o| !o.equals(output)); - }, + pub fn owns(&self, output: &Resource) -> bool { + self.inner + .lock() + .unwrap() + .instances + .iter() + .any(|o| o.equals(output)) } } diff --git a/src/wayland/seat/keyboard.rs b/src/wayland/seat/keyboard.rs index 08ba1a3..f7996cc 100644 --- a/src/wayland/seat/keyboard.rs +++ b/src/wayland/seat/keyboard.rs @@ -3,8 +3,9 @@ use std::io::{Error as IoError, Write}; use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use tempfile::tempfile; -use wayland_server::{Liveness, Resource}; -use wayland_server::protocol::{wl_keyboard, wl_surface}; +use wayland_server::{NewResource, Resource}; +use wayland_server::protocol::wl_surface::WlSurface; +use wayland_server::protocol::wl_keyboard::{Event, KeyState as WlKeyState, KeymapFormat, Request, WlKeyboard}; use xkbcommon::xkb; pub use xkbcommon::xkb::{keysyms, Keysym}; @@ -55,8 +56,8 @@ impl ModifiersState { } struct KbdInternal { - known_kbds: Vec, - focus: Option, + known_kbds: Vec>, + focus: Option>, pressed_keys: Vec, mods_state: ModifiersState, keymap: xkb::Keymap, @@ -65,9 +66,18 @@ struct KbdInternal { repeat_delay: i32, } +// This is OK because all parts of `xkb` will remain on the +// same thread +unsafe impl Send for KbdInternal {} + impl KbdInternal { fn new( - rules: &str, model: &str, layout: &str, variant: &str, options: Option, repeat_rate: i32, + rules: &str, + model: &str, + layout: &str, + variant: &str, + options: Option, + repeat_rate: i32, repeat_delay: i32, ) -> Result { // we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe @@ -147,7 +157,7 @@ impl KbdInternal { fn with_focused_kbds(&self, mut f: F) where - F: FnMut(&wl_keyboard::WlKeyboard, &wl_surface::WlSurface), + F: FnMut(&Resource, &Resource), { if let Some(ref surface) = self.focus { for kbd in &self.known_kbds { @@ -170,8 +180,14 @@ pub enum Error { /// Create a keyboard handler from a set of RMLVO rules pub(crate) fn create_keyboard_handler( - rules: &str, model: &str, layout: &str, variant: &str, options: Option, repeat_delay: i32, - repeat_rate: i32, logger: &::slog::Logger, + rules: &str, + model: &str, + layout: &str, + variant: &str, + options: Option, + repeat_delay: i32, + repeat_rate: i32, + logger: &::slog::Logger, ) -> Result { let log = logger.new(o!("smithay_module" => "xkbcommon_handler")); 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 /// to be compared against. This includes non-characted keysyms, such as XF86 special keys. - pub fn input(&self, keycode: u32, state: KeyState, serial: u32, filter: F) + pub fn input(&self, keycode: u32, state: KeyState, serial: u32, time: u32, filter: F) where F: FnOnce(&ModifiersState, Keysym) -> bool, { @@ -282,14 +298,25 @@ impl KeyboardHandle { None }; let wl_state = match state { - KeyState::Pressed => wl_keyboard::KeyState::Pressed, - KeyState::Released => wl_keyboard::KeyState::Released, + KeyState::Pressed => WlKeyState::Pressed, + KeyState::Released => WlKeyState::Released, }; guard.with_focused_kbds(|kbd, _| { 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() { 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 /// will be sent a `wl_keyboard::leave` event, and if the new focus is not `None`, /// 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>, serial: u32) { let mut guard = self.arc.internal.lock().unwrap(); let same = guard @@ -315,16 +342,29 @@ impl KeyboardHandle { if !same { // unset old focus guard.with_focused_kbds(|kbd, s| { - kbd.leave(serial, s); + kbd.send(Event::Leave { + serial, + surface: s.clone(), + }); }); // 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 keys = guard.serialize_pressed_keys(); - guard.with_focused_kbds(|kbd, s| { - kbd.modifiers(serial, dep, la, lo, gr); - kbd.enter(serial, s, keys.clone()); + guard.with_focused_kbds(|kbd, surface| { + kbd.send(Event::Modifiers { + 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() { trace!(self.arc.logger, "Focus set to new surface"); @@ -341,16 +381,19 @@ impl KeyboardHandle { /// The keymap will automatically be sent to it /// /// 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) { trace!(self.arc.logger, "Sending keymap to client"); - kbd.keymap( - wl_keyboard::KeymapFormat::XkbV1, - self.arc.keymap_file.as_raw_fd(), - self.arc.keymap_len, - ); + kbd.send(Event::Keymap { + format: KeymapFormat::XkbV1, + fd: self.arc.keymap_file.as_raw_fd(), + size: self.arc.keymap_len, + }); let mut guard = self.arc.internal.lock().unwrap(); 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); } @@ -361,17 +404,36 @@ impl KeyboardHandle { guard.repeat_delay = delay; guard.repeat_rate = rate; for kbd in &guard.known_kbds { - kbd.repeat_info(rate, delay); + kbd.send(Event::RepeatInfo { rate, delay }); } } - - /// Performs an internal cleanup of known kbds - /// - /// Drops any wl_keyboard that is no longer alive - pub(crate) fn cleanup_old_kbds(&self) { - let mut guard = self.arc.internal.lock().unwrap(); - guard - .known_kbds - .retain(|kbd| kbd.status() != Liveness::Dead); - } +} + +pub(crate) fn implement_keyboard( + new_keyboard: NewResource, + handle: Option<&KeyboardHandle>, +) -> Resource { + let destructor = match handle { + Some(h) => { + 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, + ) } diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 092b85b..9684636 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -10,14 +10,14 @@ //! ``` //! # extern crate wayland_server; //! # #[macro_use] extern crate smithay; -//! //! use smithay::wayland::seat::Seat; //! //! # fn main(){ -//! # let (_display, mut event_loop) = wayland_server::create_display(); +//! # let (mut display, event_loop) = wayland_server::Display::new(); //! // insert the seat: -//! let (seat_state_token, seat_global) = Seat::new( -//! &mut event_loop, +//! let (seat, seat_global) = Seat::new( +//! &mut display, // the display +//! event_loop.token(), // a LoopToken //! "seat-0".into(), // the name of the seat, will be advertize to clients //! None /* insert a logger here*/ //! ); @@ -31,8 +31,7 @@ //! Currently, only pointer and keyboard capabilities are supported by //! smithay. //! -//! You can add these capabilities via methods of the `Seat` struct that was -//! inserted in the event loop, that you can retreive via its token: +//! You can add these capabilities via methods of the `Seat` struct: //! //! ``` //! # extern crate wayland_server; @@ -41,28 +40,59 @@ //! # use smithay::wayland::seat::Seat; //! # //! # fn main(){ -//! # let (_display, mut event_loop) = wayland_server::create_display(); -//! # let (seat_state_token, seat_global) = Seat::new( -//! # &mut event_loop, +//! # let (mut display, event_loop) = wayland_server::Display::new(); +//! # let (mut seat, seat_global) = Seat::new( +//! # &mut display, +//! # event_loop.token(), //! # "seat-0".into(), // the name of the seat, will be advertize to clients //! # 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 //! in your event-handling code to forward inputs to your clients. +use std::sync::{Arc, Mutex}; + mod keyboard; mod pointer; pub use self::keyboard::{Error as KeyboardError, KeyboardHandle, ModifiersState}; pub use self::pointer::{PointerAxisHandle, PointerHandle}; -use wayland_server::{Client, EventLoopHandle, Global, Liveness, Resource, StateToken}; -use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat}; +use wayland_server::{Display, Global, LoopToken, NewResource, Resource}; +use wayland_server::protocol::wl_seat; -/// Internal data of a seat global +struct Inner { + log: ::slog::Logger, + name: String, + pointer: Option, + keyboard: Option, + known_seats: Vec>, +} + +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 /// 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. pub struct Seat { - log: ::slog::Logger, - name: String, - pointer: Option, - keyboard: Option, - known_seats: Vec, + inner: Arc>, } impl Seat { @@ -88,22 +114,39 @@ impl Seat { /// you to add or remove capabilities from it), and the global handle, /// in case you want to remove it. pub fn new( - evlh: &mut EventLoopHandle, name: String, logger: L - ) -> (StateToken, Global>) + display: &mut Display, + token: LoopToken, + name: String, + logger: L, + ) -> (Seat, Global) where L: Into>, { 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())), name: name, pointer: None, keyboard: None, known_seats: Vec::new(), + })); + let seat = Seat { + inner: inner.clone(), }; - let token = evlh.state().insert(seat); - let global = evlh.register_global(5, seat_global_bind, token.clone()); - (token, global) + let global = display.create_global(&token, 5, move |_version, new_seat| { + let seat = implement_seat(new_seat, inner.clone()); + 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 @@ -115,21 +158,16 @@ impl Seat { /// will overwrite it, and will be seen by the clients as if the /// mouse was unplugged and a new one was plugged. pub fn add_pointer(&mut self) -> PointerHandle { + let mut inner = self.inner.lock().unwrap(); 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 // of the change - self.pointer = None; - let caps = self.compute_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 = None; + inner.send_all_caps(); } + inner.pointer = Some(pointer.clone()); + inner.send_all_caps(); pointer } @@ -137,12 +175,10 @@ impl Seat { /// /// Clients will be appropriately notified. pub fn remove_pointer(&mut self) { - if self.pointer.is_some() { - self.pointer = None; - let caps = self.compute_caps(); - for seat in &self.known_seats { - seat.capabilities(caps); - } + let mut inner = self.inner.lock().unwrap(); + if inner.pointer.is_some() { + inner.pointer = None; + inner.send_all_caps(); } } @@ -159,9 +195,15 @@ impl Seat { /// will overwrite it, and will be seen by the clients as if the /// keyboard was unplugged and a new one was plugged. pub fn add_keyboard( - &mut self, model: &str, layout: &str, variant: &str, options: Option, repeat_delay: i32, + &mut self, + model: &str, + layout: &str, + variant: &str, + options: Option, + repeat_delay: i32, repeat_rate: i32, ) -> Result { + let mut inner = self.inner.lock().unwrap(); let keyboard = self::keyboard::create_keyboard_handler( "evdev", // we need this one model, @@ -170,22 +212,16 @@ impl Seat { options, repeat_delay, 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 // of the change - self.keyboard = None; - let caps = self.compute_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 = None; + inner.send_all_caps(); } + inner.keyboard = Some(keyboard.clone()); + inner.send_all_caps(); Ok(keyboard) } @@ -193,95 +229,60 @@ impl Seat { /// /// Clients will be appropriately notified. pub fn remove_keyboard(&mut self) { - if self.keyboard.is_some() { - self.keyboard = None; - let caps = self.compute_caps(); - for seat in &self.known_seats { - seat.capabilities(caps); - } + let mut inner = self.inner.lock().unwrap(); + if inner.keyboard.is_some() { + inner.keyboard = None; + inner.send_all_caps(); } } /// Checks wether a given `WlSeat` is associated with this `Seat` - pub fn owns(&self, seat: &wl_seat::WlSeat) -> bool { - self.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 + pub fn owns(&self, seat: &Resource) -> bool { + let inner = self.inner.lock().unwrap(); + inner.known_seats.iter().any(|s| s.equals(seat)) } } -fn seat_global_bind( - evlh: &mut EventLoopHandle, token: &mut StateToken, _: &Client, seat: wl_seat::WlSeat -) { - evlh.register(&seat, seat_implementation(), token.clone(), None); - let seat_mgr = evlh.state().get_mut(token); - if seat.version() >= 2 { - seat.name(seat_mgr.name.clone()); - } - seat.capabilities(seat_mgr.compute_caps()); - seat_mgr.known_seats.push(seat); -} - -fn seat_implementation() -> wl_seat::Implementation> { - wl_seat::Implementation { - get_pointer: |evlh, token, _, _, pointer| { - evlh.register(&pointer, pointer_implementation(), (), None); - if let Some(ref ptr_handle) = evlh.state().get(token).pointer { - ptr_handle.new_pointer(pointer); - } else { - // we should send a protocol error... but the protocol does not allow - // us, so this pointer will just remain inactive ¯\_(ツ)_/¯ +fn implement_seat( + new_seat: NewResource, + inner: Arc>, +) -> Resource { + let dest_inner = inner.clone(); + new_seat.implement( + move |request, _seat| { + let inner = inner.lock().unwrap(); + match request { + wl_seat::Request::GetPointer { id } => { + let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref()); + if let Some(ref ptr_handle) = inner.pointer { + ptr_handle.new_pointer(pointer); + } else { + // we should send a protocol error... but the protocol does not allow + // us, so this pointer will just remain inactive ¯\_(ツ)_/¯ + } + } + wl_seat::Request::GetKeyboard { id } => { + let keyboard = self::keyboard::implement_keyboard(id, inner.keyboard.as_ref()); + 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| { - evlh.register(&keyboard, keyboard_implementation(), (), None); - if let Some(ref kbd_handle) = evlh.state().get(token).keyboard { - kbd_handle.new_kbd(keyboard); - } else { - // same, should error but cant - } - }, - 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: |_, _, _, _| {}, - } + Some(move |seat, _| { + dest_inner + .lock() + .unwrap() + .known_seats + .retain(|s| !s.equals(&seat)); + }), + ) } diff --git a/src/wayland/seat/pointer.rs b/src/wayland/seat/pointer.rs index 10522e2..9ed7a5d 100644 --- a/src/wayland/seat/pointer.rs +++ b/src/wayland/seat/pointer.rs @@ -1,12 +1,13 @@ use std::sync::{Arc, Mutex, MutexGuard}; -use wayland_server::{Liveness, Resource}; -use wayland_server::protocol::{wl_pointer, wl_surface}; +use wayland_server::{NewResource, Resource}; +use wayland_server::protocol::wl_surface::WlSurface; +use wayland_server::protocol::wl_pointer::{Axis, AxisSource, ButtonState, Event, Request, WlPointer}; // TODO: handle pointer surface role struct PointerInternal { - known_pointers: Vec, - focus: Option, + known_pointers: Vec>, + focus: Option>, } impl PointerInternal { @@ -19,7 +20,7 @@ impl PointerInternal { fn with_focused_pointers(&self, mut f: F) where - F: FnMut(&wl_pointer::WlPointer, &wl_surface::WlSurface), + F: FnMut(&Resource, &Resource), { if let Some(ref focus) = self.focus { for ptr in &self.known_pointers { @@ -44,7 +45,7 @@ pub struct PointerHandle { } impl PointerHandle { - pub(crate) fn new_pointer(&self, pointer: wl_pointer::WlPointer) { + pub(crate) fn new_pointer(&self, pointer: Resource) { let mut guard = self.inner.lock().unwrap(); guard.known_pointers.push(pointer); } @@ -59,7 +60,7 @@ impl PointerHandle { /// /// This will internally take care of notifying the appropriate client objects /// 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, f64, f64)>, serial: u32, time: u32) { let mut guard = self.inner.lock().unwrap(); // do we leave a surface ? let mut leave = true; @@ -72,9 +73,12 @@ impl PointerHandle { } if leave { guard.with_focused_pointers(|pointer, surface| { - pointer.leave(serial, surface); + pointer.send(Event::Leave { + serial, + surface: surface.clone(), + }); if pointer.version() >= 5 { - pointer.frame(); + pointer.send(Event::Frame); } }); guard.focus = None; @@ -83,19 +87,28 @@ impl PointerHandle { // do we enter one ? if let Some((surface, x, y)) = location { if guard.focus.is_none() { - guard.focus = surface.clone(); + guard.focus = Some(surface.clone()); 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 { - pointer.frame(); + pointer.send(Event::Frame); } }) } else { // we were on top of a surface and remained on it 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 { - pointer.frame(); + pointer.send(Event::Frame); } }) } @@ -106,12 +119,17 @@ impl PointerHandle { /// /// This will internally send the appropriate button event to the client /// 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(); guard.with_focused_pointers(|pointer, _| { - pointer.button(serial, time, button, state); + pointer.send(Event::Button { + serial, + time, + button, + state, + }); if pointer.version() >= 5 { - pointer.frame(); + pointer.send(Event::Frame); } }) } @@ -125,18 +143,12 @@ impl PointerHandle { 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. /// /// Can be used with the builder pattern, e.g.: +/// /// ```ignore /// pointer.axis() /// .source(AxisSource::Wheel) @@ -156,10 +168,12 @@ impl<'a> PointerAxisHandle<'a> { /// /// Using the `AxisSource::Finger` requires a stop event to be send, /// 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, _| { if pointer.version() >= 5 { - pointer.axis_source(source); + pointer.send(Event::AxisSource { + axis_source: source, + }); } }); self @@ -170,10 +184,13 @@ impl<'a> PointerAxisHandle<'a> { /// 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, /// 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, _| { if pointer.version() >= 5 { - pointer.axis_discrete(axis, steps); + pointer.send(Event::AxisDiscrete { + axis, + discrete: steps, + }); } }); self @@ -181,9 +198,9 @@ impl<'a> PointerAxisHandle<'a> { /// 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. - 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, _| { - pointer.axis(time, axis, value); + pointer.send(Event::Axis { time, axis, value }); }); self } @@ -192,10 +209,10 @@ impl<'a> PointerAxisHandle<'a> { /// /// This event is required for sources of the `AxisSource::Finger` type /// 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, _| { if pointer.version() >= 5 { - pointer.axis_stop(time, axis); + pointer.send(Event::AxisStop { time, axis }); } }); self @@ -209,7 +226,7 @@ impl<'a> PointerAxisHandle<'a> { pub fn done(&mut self) { self.inner.with_focused_pointers(|pointer, _| { 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())), } } + +pub(crate) fn implement_pointer( + new_pointer: NewResource, + handle: Option<&PointerHandle>, +) -> Resource { + 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, + ) +} diff --git a/src/wayland/shell/legacy/mod.rs b/src/wayland/shell/legacy/mod.rs new file mode 100644 index 0000000..c851410 --- /dev/null +++ b/src/wayland/shell/legacy/mod.rs @@ -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] +//! ); +//! +//! # 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 { + /// 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 { + wl_surface: Resource, + shell_surface: Resource, + token: CompositorToken, + _d: ::std::marker::PhantomData, +} + +impl ShellSurface +where + U: 'static, + R: Role> + '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> { + 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(&self, f: F) -> Result + 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, + /// 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>, + }, + /// A popup surface + /// + /// Short-lived surface, typically refrered as "tooltips" in many + /// contexts. + Popup { + /// The parent surface of this popup + parent: Resource, + /// 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, + }, + /// 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>, + }, +} + +/// A request triggered by a wl_shell_surface +pub enum ShellRequest { + /// A new shell surface was created + /// + /// by default it has no kind and this should not be displayed + NewShellSurface { + /// The created surface + surface: ShellSurface, + }, + /// 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, + }, + /// Start of an interactive move + /// + /// The surface requests that an interactive move is started on it + Move { + /// The surface requesting the move + surface: ShellSurface, + /// Serial of the implicit grab that initiated the move + serial: u32, + /// Seat associated with the move + seat: Resource, + }, + /// Start of an interactive resize + /// + /// The surface requests that an interactive resize is started on it + Resize { + /// The surface requesting the resize + surface: ShellSurface, + /// Serial of the implicit grab that initiated the resize + serial: u32, + /// Seat associated with the resize + seat: Resource, + /// Direction of the resize + edges: wl_shell_surface::Resize, + }, + /// The surface changed its kind + SetKind { + /// The surface + surface: ShellSurface, + /// 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 { + known_surfaces: Vec>, +} + +impl ShellState +where + U: 'static, + R: Role> + '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] { + &self.known_surfaces[..] + } +} + +/// Create a new `wl_shell` global +pub fn wl_shell_init( + display: &mut Display, + ltoken: LoopToken, + ctoken: CompositorToken, + implementation: Impl, + logger: L, +) -> (Arc>>, Global) +where + U: 'static, + D: Default + 'static, + R: Role> + 'static, + L: Into>, + Impl: Implementation<(), ShellRequest>, +{ + let _log = ::slog_or_stdlog(logger); + + let implementation = Rc::new(RefCell::new(implementation)); + + let ltoken2 = ltoken.clone(); + + let state = Arc::new(Mutex::new(ShellState { + known_surfaces: Vec::new(), + })); + let state2 = state.clone(); + + let global = display.create_global(<oken2, 1, move |_version, shell| { + self::wl_handlers::implement_shell( + shell, + ltoken.clone(), + ctoken.clone(), + implementation.clone(), + state2.clone(), + ); + }); + + (state, global) +} diff --git a/src/wayland/shell/legacy/wl_handlers.rs b/src/wayland/shell/legacy/wl_handlers.rs new file mode 100644 index 0000000..3dbc0e6 --- /dev/null +++ b/src/wayland/shell/legacy/wl_handlers.rs @@ -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( + shell: NewResource, + ltoken: LoopToken, + ctoken: CompositorToken, + implementation: Rc>, + state: Arc>>, +) where + U: 'static, + D: Default + 'static, + R: Role> + 'static, + Impl: Implementation<(), ShellRequest> + '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::, + <oken2, + ); +} + +fn make_handle( + shell_surface: &Resource, + token: CompositorToken, +) -> ShellSurface { + let data = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) }; + ShellSurface { + wl_surface: data.surface.clone(), + shell_surface: shell_surface.clone(), + token, + _d: ::std::marker::PhantomData, + } +} + +pub(crate) struct ShellSurfaceUserData { + surface: Resource, + state: Arc>>, +} + +fn implement_shell_surface( + shell_surface: NewResource, + surface: Resource, + implementation: Rc>, + ltoken: LoopToken, + ctoken: CompositorToken, + state: Arc>>, +) -> Resource +where + U: 'static, + D: 'static, + R: Role> + 'static, + Impl: Implementation<(), ShellRequest> + '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) }; + 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) }; + data.state.lock().unwrap().cleanup_surfaces(); + }), + <oken, + ); + shell_surface.set_user_data(Box::into_raw(Box::new(ShellSurfaceUserData { surface, state })) as *mut ()); + shell_surface +} diff --git a/src/wayland/shell/mod.rs b/src/wayland/shell/mod.rs index c3c7fe7..e34306c 100644 --- a/src/wayland/shell/mod.rs +++ b/src/wayland/shell/mod.rs @@ -1,940 +1,2 @@ -//! Utilities for handling shell surfaces, toplevel and popups -//! -//! 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, - /// 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, - /// 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, - /// 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 { - 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 { - log: ::slog::Logger, - compositor_token: CompositorToken, - implementation: ShellSurfaceUserImplementation, - idata: Rc>, - state_token: StateToken>, -} - -impl Clone for ShellSurfaceIData { - fn clone(&self) -> ShellSurfaceIData { - 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( - evlh: &mut EventLoopHandle, token: CompositorToken, - implementation: ShellSurfaceUserImplementation, idata: SID, logger: L, -) -> ( - StateToken>, - Global>, - Global>, -) -where - U: 'static, - R: Role + 'static, - CID: 'static, - SID: 'static, - SD: Default + 'static, - L: Into>, -{ - 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::, - shell_surface_idata.clone(), - ); - let xdg_shell_global = evlh.register_global( - 1, - self::xdg_handlers::xdg_shell_bind::, - 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 { - known_toplevels: Vec>, - known_popups: Vec>, -} - -impl ShellState -where - U: 'static, - R: Role + '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] { - &self.known_toplevels[..] - } - - /// Access all the popup surfaces known by this handler - pub fn popup_surfaces(&self) -> &[PopupSurface] { - &self.known_popups[..] - } -} - -/* - * User interaction - */ - -enum ShellClientKind { - Wl(wl_shell::WlShell), - Xdg(zxdg_shell_v6::ZxdgShellV6), -} - -pub(crate) struct ShellClientData { - pending_ping: u32, - data: SD, -} - -fn make_shell_client_data() -> ShellClientData { - 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 { - kind: ShellClientKind, - _data: ::std::marker::PhantomData<*mut SD>, -} - -impl ShellClient { - /// 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) }; - 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) }; - 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(&self, f: F) -> Result - 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) }; - 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) }; - 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 { - wl_surface: wl_surface::WlSurface, - shell_surface: SurfaceKind, - token: CompositorToken, - _shell_data: ::std::marker::PhantomData, -} - -impl ToplevelSurface -where - U: 'static, - R: Role + '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> { - 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::(&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 { - if !self.alive() { - return None; - } - self.token - .with_role_data::(&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 { - wl_surface: wl_surface::WlSurface, - shell_surface: SurfaceKind, - token: CompositorToken, - _shell_data: ::std::marker::PhantomData, -} - -impl PopupSurface -where - U: 'static, - R: Role + '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> { - 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::(&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 { - if !self.alive() { - return None; - } - self.token - .with_role_data::(&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, - /// 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 { - /// A new shell client was instanciated - pub new_client: fn(evlh: &mut EventLoopHandle, idata: &mut SID, client: ShellClient), - /// 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), - /// 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) - -> 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) - -> PopupConfigure, - /// The client requested the start of an interactive move for this surface - pub move_: fn( - evlh: &mut EventLoopHandle, - idata: &mut SID, - surface: ToplevelSurface, - 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, - 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, - 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, - maximized: Option, - minimized: Option, - fullscreen: Option, - 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, - seat: &wl_seat::WlSeat, - serial: u32, - x: i32, - y: i32, - ), -} - -impl Copy for ShellSurfaceUserImplementation {} -impl Clone for ShellSurfaceUserImplementation { - fn clone(&self) -> ShellSurfaceUserImplementation { - *self - } -} +pub mod legacy; +pub mod xdg; diff --git a/src/wayland/shell/wl_handlers.rs b/src/wayland/shell/wl_handlers.rs deleted file mode 100644 index 28307d4..0000000 --- a/src/wayland/shell/wl_handlers.rs +++ /dev/null @@ -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( - evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData, _: &Client, - shell: wl_shell::WlShell, -) where - U: 'static, - R: Role + 'static, - CID: 'static, - SID: 'static, - SD: Default + 'static, -{ - shell.set_user_data(Box::into_raw(Box::new(Mutex::new(( - make_shell_client_data::(), - Vec::::new(), - )))) as *mut _); - evlh.register( - &shell, - shell_implementation(), - idata.clone(), - Some(destroy_shell::), - ); - 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 = Mutex<(ShellClientData, Vec)>; - -fn destroy_shell(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) }; - // explicitly call drop to not forget what we're doing here - ::std::mem::drop(data); -} - -pub fn make_shell_client(resource: &wl_shell::WlShell) -> ShellClient { - ShellClient { - kind: super::ShellClientKind::Wl(unsafe { resource.clone_unchecked() }), - _data: ::std::marker::PhantomData, - } -} - -fn shell_implementation( -) -> wl_shell::Implementation> -where - U: 'static, - R: Role + '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) }; - 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( - token: CompositorToken, resource: &wl_shell_surface::WlShellSurface -) -> super::ToplevelSurface { - 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( - token: CompositorToken, resource: &wl_shell_surface::WlShellSurface -) -> super::PopupSurface { - 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( - evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData, - shell_surface: &wl_shell_surface::WlShellSurface, maximized: Option, minimized: Option, - fullscreen: Option, 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( - idata: &ShellSurfaceIData, shell_surface: &wl_shell_surface::WlShellSurface, - parent: Option, -) where - U: 'static, - R: Role + '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::(wl_surface, |data| match data.pending_state { - ShellSurfacePendingState::Toplevel(ref mut state) => { - state.parent = parent; - } - _ => unreachable!(), - }) - .unwrap(); -} - -fn wl_ensure_toplevel( - evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData, - shell_surface: &wl_shell_surface::WlShellSurface, -) where - U: 'static, - R: Role + '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::(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( -) -> wl_shell_surface::Implementation> -where - U: 'static, - R: Role + '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) }; - 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?!"); - }, - } -} diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs new file mode 100644 index 0000000..b4b281c --- /dev/null +++ b/src/wayland/shell/xdg/mod.rs @@ -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>`) 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, + /// 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, + /// 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>, + /// 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>, + /// 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 { + log: ::slog::Logger, + compositor_token: CompositorToken, + loop_token: LoopToken, + user_impl: Rc>>>, + shell_state: Arc>>, +} + +impl Clone for ShellImplementation { + 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( + display: &mut Display, + ltoken: LoopToken, + ctoken: CompositorToken, + implementation: Impl, + logger: L, +) -> ( + Arc>>, + Global, + Global, +) +where + U: 'static, + R: Role + 'static, + SD: Default + 'static, + L: Into>, + Impl: Implementation<(), XdgRequest>, +{ + 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(<oken, 1, move |_version, shell| { + self::xdg_handlers::implement_wm_base(shell, &shell_impl); + }); + + let zxdgv6_shell_global = display.create_global(<oken, 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 { + known_toplevels: Vec>, + known_popups: Vec>, +} + +impl ShellState +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + /// Access all the shell surfaces known by this handler + pub fn toplevel_surfaces(&self) -> &[ToplevelSurface] { + &self.known_toplevels[..] + } + + /// Access all the popup surfaces known by this handler + pub fn popup_surfaces(&self) -> &[PopupSurface] { + &self.known_popups[..] + } +} + +/* + * User interaction + */ + +enum ShellClientKind { + Xdg(Resource), + ZxdgV6(Resource), +} + +pub(crate) struct ShellClientData { + pending_ping: u32, + data: SD, +} + +fn make_shell_client_data() -> ShellClientData { + 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 { + kind: ShellClientKind, + _data: ::std::marker::PhantomData<*mut SD>, +} + +impl ShellClient { + /// 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) }; + 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) }; + 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(&self, f: F) -> Result + 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) }; + 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) }; + let mut guard = mutex.lock().unwrap(); + Ok(f(&mut guard.data)) + } + } + } +} + +pub(crate) enum ToplevelKind { + Xdg(Resource), + ZxdgV6(Resource), +} + +/// A handle to a toplevel surface +pub struct ToplevelSurface { + wl_surface: Resource, + shell_surface: ToplevelKind, + token: CompositorToken, + _shell_data: ::std::marker::PhantomData, +} + +impl ToplevelSurface +where + U: 'static, + R: Role + '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> { + 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::(&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> { + 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 { + if !self.alive() { + return None; + } + self.token + .with_role_data::(&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), + ZxdgV6(Resource), +} + +/// 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 { + wl_surface: Resource, + shell_surface: PopupKind, + token: CompositorToken, + _shell_data: ::std::marker::PhantomData, +} + +impl PopupSurface +where + U: 'static, + R: Role + '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> { + 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::(&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> { + 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 { + if !self.alive() { + return None; + } + self.token + .with_role_data::(&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, + /// 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 { + /// A new shell client was instanciated + NewClient { + /// the client + client: ShellClient, + }, + /// 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, + }, + /// 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, + }, + /// 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, + }, + /// The client requested the start of an interactive move for this surface + Move { + /// the surface + surface: ToplevelSurface, + /// the seat associated to this move + seat: Resource, + /// the grab serial + serial: u32, + }, + /// The client requested the start of an interactive resize for this surface + Resize { + /// The surface + surface: ToplevelSurface, + /// The seat associated with this resize + seat: Resource, + /// 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, + /// The seat to grab + seat: Resource, + /// The grab serial + serial: u32, + }, + /// A toplevel surface requested to be maximized + Maximize { + /// The surface + surface: ToplevelSurface, + }, + /// A toplevel surface requested to stop being maximized + UnMaximize { + /// The surface + surface: ToplevelSurface, + }, + /// A toplevel surface requested to be set fullscreen + Fullscreen { + /// The surface + surface: ToplevelSurface, + /// The output (if any) on which the fullscreen is requested + output: Option>, + }, + /// A toplevel surface request to stop being fullscreen + UnFullscreen { + /// The surface + surface: ToplevelSurface, + }, + /// A toplevel surface requested to be minimized + Minimize { + /// The surface + surface: ToplevelSurface, + }, + /// 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, + /// The seat associated with this input grab + seat: Resource, + /// the grab serial + serial: u32, + /// location of the menu request + location: (i32, i32), + }, +} diff --git a/src/wayland/shell/xdg/xdg_handlers.rs b/src/wayland/shell/xdg/xdg_handlers.rs new file mode 100644 index 0000000..68c11e0 --- /dev/null +++ b/src/wayland/shell/xdg/xdg_handlers.rs @@ -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( + shell: NewResource, + implem: &ShellImplementation, +) -> Resource +where + U: 'static, + R: Role + 'static, + SD: Default + 'static, +{ + let shell = shell.implement_nonsend( + implem.clone(), + Some(|shell, _| destroy_shell::(&shell)), + &implem.loop_token, + ); + shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::()))) 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 = Mutex>; + +fn destroy_shell(shell: &Resource) { + let ptr = shell.get_user_data(); + shell.set_user_data(::std::ptr::null_mut()); + let data = unsafe { Box::from_raw(ptr as *mut ShellUserData) }; + // explicit call to drop to not forget what we're doing here + ::std::mem::drop(data); +} + +pub(crate) fn make_shell_client(resource: &Resource) -> ShellClient { + ShellClient { + kind: super::ShellClientKind::Xdg(resource.clone()), + _data: ::std::marker::PhantomData, + } +} + +impl Implementation, xdg_wm_base::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive(&mut self, request: xdg_wm_base::Request, shell: Resource) { + 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::), + &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) }; + 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) { + 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, + token: &LoopToken, +) -> Resource { + 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, + Resource, +); + +fn destroy_surface( + surface: Resource, + implem: Box, xdg_surface::Request>>, +) where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + let implem: ShellImplementation = *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::(&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 Implementation, xdg_surface::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive(&mut self, request: xdg_surface::Request, xdg_surface: Resource) { + 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::(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::), + &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::(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::), + &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::(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::(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, + Resource, + Resource, +); + +// Utility functions allowing to factor out a lot of the upcoming logic +fn with_surface_toplevel_data( + implem: &ShellImplementation, + toplevel: &Resource, + f: F, +) where + U: 'static, + R: Role + '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::(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( + token: CompositorToken, + resource: &Resource, + configure: ToplevelConfigure, +) where + U: 'static, + R: Role + '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 (which is really a Vec) into Vec + 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::(surface, |data| data.pending_configures.push(serial)) + .expect("xdg_toplevel exists but surface has not shell_surface role?!"); +} + +fn make_toplevel_handle( + token: CompositorToken, + resource: &Resource, +) -> super::ToplevelSurface { + 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 Implementation, xdg_toplevel::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive(&mut self, request: xdg_toplevel::Request, toplevel: Resource) { + 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( + toplevel: Resource, + implem: Box, xdg_toplevel::Request>>, +) where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + let implem: ShellImplementation = *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::(&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( + token: CompositorToken, + resource: &Resource, + configure: PopupConfigure, +) where + U: 'static, + R: Role + '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::(surface, |data| data.pending_configures.push(serial)) + .expect("xdg_toplevel exists but surface has not shell_surface role?!"); +} + +fn make_popup_handle( + token: CompositorToken, + resource: &Resource, +) -> super::PopupSurface { + 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 Implementation, xdg_popup::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive(&mut self, request: xdg_popup::Request, popup: Resource) { + 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( + popup: Resource, + implem: Box, xdg_popup::Request>>, +) where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + let implem: ShellImplementation = *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::(&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()); +} diff --git a/src/wayland/shell/xdg/zxdgv6_handlers.rs b/src/wayland/shell/xdg/zxdgv6_handlers.rs new file mode 100644 index 0000000..8e25154 --- /dev/null +++ b/src/wayland/shell/xdg/zxdgv6_handlers.rs @@ -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( + shell: NewResource, + implem: &ShellImplementation, +) -> Resource +where + U: 'static, + R: Role + 'static, + SD: Default + 'static, +{ + let shell = shell.implement_nonsend( + implem.clone(), + Some(|shell, _| destroy_shell::(&shell)), + &implem.loop_token, + ); + shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::()))) 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 = Mutex>; + +fn destroy_shell(shell: &Resource) { + let ptr = shell.get_user_data(); + shell.set_user_data(::std::ptr::null_mut()); + let data = unsafe { Box::from_raw(ptr as *mut ShellUserData) }; + // explicit call to drop to not forget what we're doing here + ::std::mem::drop(data); +} + +pub(crate) fn make_shell_client(resource: &Resource) -> ShellClient { + ShellClient { + kind: super::ShellClientKind::ZxdgV6(resource.clone()), + _data: ::std::marker::PhantomData, + } +} + +impl Implementation, zxdg_shell_v6::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive(&mut self, request: zxdg_shell_v6::Request, shell: Resource) { + 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::), + &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) }; + 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) { + 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, + token: &LoopToken, +) -> Resource { + 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, + Resource, +); + +fn destroy_surface( + surface: Resource, + implem: Box, zxdg_surface_v6::Request>>, +) where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + let implem: ShellImplementation = *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::(&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 Implementation, zxdg_surface_v6::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive( + &mut self, + request: zxdg_surface_v6::Request, + xdg_surface: Resource, + ) { + 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::(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::), + &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::(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::), + &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::(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::(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, + Resource, + Resource, +); + +// Utility functions allowing to factor out a lot of the upcoming logic +fn with_surface_toplevel_data( + implem: &ShellImplementation, + toplevel: &Resource, + f: F, +) where + U: 'static, + R: Role + '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::(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( + token: CompositorToken, + resource: &Resource, + configure: ToplevelConfigure, +) where + U: 'static, + R: Role + '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 (which is really a Vec) into Vec + 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::(surface, |data| data.pending_configures.push(serial)) + .expect("xdg_toplevel exists but surface has not shell_surface role?!"); +} + +fn make_toplevel_handle( + token: CompositorToken, + resource: &Resource, +) -> super::ToplevelSurface { + 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 Implementation, zxdg_toplevel_v6::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive( + &mut self, + request: zxdg_toplevel_v6::Request, + toplevel: Resource, + ) { + 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( + toplevel: Resource, + implem: Box, zxdg_toplevel_v6::Request>>, +) where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + let implem: ShellImplementation = *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::(&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( + token: CompositorToken, + resource: &Resource, + configure: PopupConfigure, +) where + U: 'static, + R: Role + '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::(surface, |data| data.pending_configures.push(serial)) + .expect("xdg_toplevel exists but surface has not shell_surface role?!"); +} + +fn make_popup_handle( + token: CompositorToken, + resource: &Resource, +) -> super::PopupSurface { + 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 Implementation, zxdg_popup_v6::Request> + for ShellImplementation +where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + fn receive(&mut self, request: zxdg_popup_v6::Request, popup: Resource) { + 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( + popup: Resource, + implem: Box, zxdg_popup_v6::Request>>, +) where + U: 'static, + R: Role + 'static, + SD: 'static, +{ + let implem: ShellImplementation = *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::(&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 { + 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 { + 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, + } +} diff --git a/src/wayland/shell/xdg_handlers.rs b/src/wayland/shell/xdg_handlers.rs deleted file mode 100644 index baedaf7..0000000 --- a/src/wayland/shell/xdg_handlers.rs +++ /dev/null @@ -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( - evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData, _: &Client, - shell: zxdg_shell_v6::ZxdgShellV6, -) where - U: 'static, - R: Role + 'static, - CID: 'static, - SID: 'static, - SD: Default + 'static, -{ - shell.set_user_data(Box::into_raw(Box::new(Mutex::new(make_shell_client_data::()))) as *mut _); - evlh.register( - &shell, - shell_implementation(), - idata.clone(), - Some(destroy_shell::), - ); - 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 = Mutex>; - -fn destroy_shell(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) }; - // explicit call to drop to not forget what we're doing here - ::std::mem::drop(data); -} - -pub(crate) fn make_shell_client(resource: &zxdg_shell_v6::ZxdgShellV6) -> ShellClient { - ShellClient { - kind: super::ShellClientKind::Xdg(unsafe { resource.clone_unchecked() }), - _data: ::std::marker::PhantomData, - } -} - -fn shell_implementation( -) -> zxdg_shell_v6::Implementation> -where - U: 'static, - R: Role + '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) }; - 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( -) -> zxdg_surface_v6::Implementation> -where - U: 'static, - R: Role + '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::(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::(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::(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::(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::(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( - idata: &ShellSurfaceIData, toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F -) where - U: 'static, - R: Role + '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::(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( - evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData, - toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, maximized: Option, minimized: Option, - fullscreen: Option, output: Option<&wl_output::WlOutput>, -) where - U: 'static, - R: Role + '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( - token: CompositorToken, resource: &zxdg_toplevel_v6::ZxdgToplevelV6, - configure: ToplevelConfigure, -) where - U: 'static, - R: Role + '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 (which is really a Vec) into Vec - 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::(surface, |data| data.pending_configures.push(serial)) - .expect("xdg_toplevel exists but surface has not shell_surface role?!"); -} - -fn make_toplevel_handle( - token: CompositorToken, resource: &zxdg_toplevel_v6::ZxdgToplevelV6 -) -> super::ToplevelSurface { - 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( -) -> zxdg_toplevel_v6::Implementation> -where - U: 'static, - R: Role + '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::(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( - token: CompositorToken, resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure -) where - U: 'static, - R: Role + '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::(surface, |data| data.pending_configures.push(serial)) - .expect("xdg_toplevel exists but surface has not shell_surface role?!"); -} - -fn make_popup_handle( - token: CompositorToken, resource: &zxdg_popup_v6::ZxdgPopupV6 -) -> super::PopupSurface { - 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( -) -> zxdg_popup_v6::Implementation> -where - U: 'static, - R: Role + '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::(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) - }, - } -} diff --git a/src/wayland/shm/mod.rs b/src/wayland/shm/mod.rs index 7ce136a..a148ca9 100644 --- a/src/wayland/shm/mod.rs +++ b/src/wayland/shm/mod.rs @@ -11,7 +11,7 @@ //! [the similar helpers](https://cgit.freedesktop.org/wayland/wayland/tree/src/wayland-shm.c) //! 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, //! as specified by the wayland protocol) and obtain its `ShmToken`. //! @@ -23,12 +23,13 @@ //! use wayland_server::protocol::wl_shm::Format; //! //! # 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 //! // Here, we specify that Yuyv and C8 format are supported //! // additionnaly to the standart Argb8888 and Xrgb8888. //! let shm_global = init_shm_global( -//! &mut event_loop, +//! &mut display, +//! event_loop.token(), //! vec![Format::Yuyv, Format::C8], //! None // we don't provide a logger here //! ); @@ -42,7 +43,8 @@ //! # extern crate wayland_server; //! # extern crate smithay; //! # use wayland_server::protocol::wl_buffer::WlBuffer; -//! # fn wrap(buffer: &WlBuffer) { +//! # use wayland_server::Resource; +//! # fn wrap(buffer: &Resource) { //! use smithay::wayland::shm::{with_buffer_contents, BufferData}; //! //! with_buffer_contents(&buffer, @@ -64,7 +66,8 @@ use self::pool::{Pool, ResizeError}; use std::rc::Rc; 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}; mod pool; @@ -77,6 +80,7 @@ mod pool; pub struct ShmGlobalData { formats: Rc>, log: ::slog::Logger, + token: LoopToken, } /// 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 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 /// the future. pub fn init_shm_global( - evlh: &mut EventLoopHandle, mut formats: Vec, logger: L -) -> Global + display: &mut Display, + token: LoopToken, + mut formats: Vec, + logger: L, +) -> Global where L: Into>, { @@ -102,9 +109,16 @@ where let data = ShmGlobalData { formats: Rc::new(formats), log: log.new(o!("smithay_module" => "shm_handler")), + token: token.clone(), }; - evlh.register_global::(1, shm_global_bind, data) + display.create_global::(&token, 1, move |_version, shm_new: NewResource<_>| { + let shm = shm_new.implement_nonsend(data.clone(), None::, &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 @@ -131,11 +145,11 @@ pub enum BufferAccessError { /// /// 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). -pub fn with_buffer_contents(buffer: &wl_buffer::WlBuffer, f: F) -> Result<(), BufferAccessError> +pub fn with_buffer_contents(buffer: &Resource, f: F) -> Result<(), BufferAccessError> where F: FnOnce(&[u8], BufferData), { - if !resource_is_registered(buffer, &buffer_implementation()) { + if !buffer.is_implemented_with::() { return Err(BufferAccessError::NotManaged); } let data = unsafe { &*(buffer.get_user_data() as *mut InternalBufferData) }; @@ -151,52 +165,43 @@ where Ok(()) } -fn shm_global_bind(evlh: &mut EventLoopHandle, data: &mut ShmGlobalData, _: &Client, global: wl_shm::WlShm) { - // register an handler for this shm - evlh.register(&global, shm_implementation(), data.clone(), None); - // and then the custom formats - for f in &data.formats[..] { - global.format(*f); - } -} +impl Implementation, wl_shm::Request> for ShmGlobalData { + fn receive(&mut self, request: wl_shm::Request, shm: Resource) { + use self::wl_shm::{Error, Request}; -fn shm_implementation() -> wl_shm::Implementation { - wl_shm::Implementation { - create_pool: |evlh, data, _, shm, pool, fd, size| { - 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(()) => { + match request { + Request::CreatePool { id: pool, fd, size } => { + if size <= 0 { shm.post_error( - wl_shm::Error::InvalidFd as u32, - format!("Failed mmap of fd {}.", fd), + Error::InvalidFd as u32, + "Invalid size for a new wl_shm_pool.".into(), ); return; } - }; - let arc_pool = Box::new(Arc::new(mmap_pool)); - evlh.register( - &pool, - shm_pool_implementation(), - data.clone(), - Some(destroy_shm_pool), - ); - pool.set_user_data(Box::into_raw(arc_pool) as *mut ()); - }, + let mmap_pool = match Pool::new(fd, size as usize, self.log.clone()) { + Ok(p) => p, + Err(()) => { + shm.post_error( + wl_shm::Error::InvalidFd as u32, + format!("Failed mmap of fd {}.", fd), + ); + return; + } + }; + let arc_pool = Box::new(Arc::new(mmap_pool)); + let pool = pool.implement_nonsend( + self.clone(), + Some(|pool: Resource<_>, _| { + drop(unsafe { Box::from_raw(pool.get_user_data() as *mut Arc) }) + }), + &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) }; - drop(arc_pool) -} - /// Details of the contents of a buffer relative to its pool #[derive(Copy, Clone, Debug)] pub struct BufferData { @@ -217,30 +222,48 @@ struct InternalBufferData { data: BufferData, } -fn shm_pool_implementation() -> wl_shm_pool::Implementation { - wl_shm_pool::Implementation { - create_buffer: |evlh, data, _, pool, buffer, offset, width, height, stride, format| { - if !data.formats.contains(&format) { - buffer.post_error(wl_shm::Error::InvalidFormat as u32, String::new()); - return; +impl Implementation, wl_shm_pool::Request> for ShmGlobalData { + fn receive(&mut self, request: wl_shm_pool::Request, pool: Resource) { + use self::wl_shm_pool::Request; + + let arc_pool = unsafe { &*(pool.get_user_data() as *mut Arc) }; + + 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) }; - 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) }; - match arc_pool.resize(size) { + Request::Resize { size } => match arc_pool.resize(size) { Ok(()) => {} Err(ResizeError::InvalidSize) => { pool.post_error( @@ -251,19 +274,15 @@ fn shm_pool_implementation() -> wl_shm_pool::Implementation { Err(ResizeError::MremapFailed) => { pool.post_error(wl_shm::Error::InvalidFd as u32, "mremap failed.".into()); } - } - }, - destroy: |_, _, _, _| {}, + }, + Request::Destroy => {} + } } } -fn destroy_buffer(buffer: &wl_buffer::WlBuffer) { - let buffer_data = unsafe { Box::from_raw(buffer.get_user_data() as *mut InternalBufferData) }; - drop(buffer_data) -} - -fn buffer_implementation() -> wl_buffer::Implementation<()> { - wl_buffer::Implementation { - destroy: |_, _, _, _| {}, +impl Implementation, wl_buffer::Request> for ShmGlobalData { + fn receive(&mut self, request: wl_buffer::Request, _pool: Resource) { + // this will break if new requests are added to buffer =) + let wl_buffer::Request::Destroy = request; } } diff --git a/src/wayland/shm/pool.rs b/src/wayland/shm/pool.rs index b70442a..2bbcaab 100644 --- a/src/wayland/shm/pool.rs +++ b/src/wayland/shm/pool.rs @@ -161,8 +161,8 @@ unsafe fn map(fd: RawFd, size: usize) -> Result<*mut u8, ()> { let ret = mman::mmap( ptr::null_mut(), size, - mman::PROT_READ, - mman::MAP_SHARED, + mman::ProtFlags::PROT_READ, + mman::MapFlags::MAP_SHARED, fd, 0, ); @@ -179,8 +179,8 @@ unsafe fn nullify_map(ptr: *mut u8, size: usize) -> Result<(), ()> { let ret = mman::mmap( ptr as *mut _, size, - mman::PROT_READ, - mman::MAP_ANONYMOUS | mman::MAP_PRIVATE | mman::MAP_FIXED, + mman::ProtFlags::PROT_READ, + mman::MapFlags::MAP_ANONYMOUS | mman::MapFlags::MAP_PRIVATE | mman::MapFlags::MAP_FIXED, -1, 0, ); @@ -191,7 +191,7 @@ unsafe fn place_sigbus_handler() { // create our sigbus handler let action = SigAction::new( SigHandler::SigAction(sigbus_handler), - signal::SA_NODEFER, + signal::SaFlags::SA_NODEFER, signal::SigSet::empty(), ); match signal::sigaction(Signal::SIGBUS, &action) {