From 61d25be3e08dc773b1315d5910895cf957e3ec01 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 3 Nov 2017 17:35:29 +0100 Subject: [PATCH] wayland: upgrade wayland-window (#339) * wayland: upgrade wayland-window This new version of wayland window considerably simplifies the window handling for winit, meaning much of the previous juggling is no longer needed, and the windows will appear even if nothing is drawn. * wayland: cleanup unused stuff --- Cargo.toml | 3 +- examples/cursor.rs | 6 -- examples/fullscreen.rs | 6 -- examples/grabbing.rs | 6 -- examples/min_max_size.rs | 6 -- examples/multiwindow.rs | 6 -- examples/proxy.rs | 6 -- examples/transparent.rs | 6 -- examples/window.rs | 6 -- src/os/unix.rs | 15 ++--- src/platform/linux/wayland/event_loop.rs | 86 ++++++------------------ src/platform/linux/wayland/mod.rs | 1 - src/platform/linux/wayland/window.rs | 72 +++++++++++--------- 13 files changed, 67 insertions(+), 158 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d74816cf0..d9f9ffec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,5 @@ dwmapi-sys = "0.1" wayland-client = { version = "0.12.0", features = ["dlopen"] } wayland-protocols = { version = "0.12.0", features = ["unstable_protocols"] } wayland-kbd = "0.13.0" -wayland-window = "0.12.0" -tempfile = "2.1" +wayland-window = "0.13.0" x11-dl = "2.8" diff --git a/examples/cursor.rs b/examples/cursor.rs index 2062d8a7a..bc7deea6d 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -11,12 +11,6 @@ fn main() { let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize]; let mut cursor_idx = 0; - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { match event { Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => { diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index bbabe34bf..933eceba6 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -31,12 +31,6 @@ fn main() { .build(&events_loop) .unwrap(); - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { println!("{:?}", event); diff --git a/examples/grabbing.rs b/examples/grabbing.rs index 68ef77013..f7e7c9b3f 100644 --- a/examples/grabbing.rs +++ b/examples/grabbing.rs @@ -10,12 +10,6 @@ fn main() { let mut grabbed = false; - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { println!("{:?}", event); diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index b5020e731..7500e8935 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -9,12 +9,6 @@ fn main() { .build(&events_loop) .unwrap(); - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { println!("{:?}", event); diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index c020f69df..9c5b93b82 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -9,12 +9,6 @@ fn main() { let mut num_windows = 3; - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { match event { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, window_id } => { diff --git a/examples/proxy.rs b/examples/proxy.rs index fa9ca0144..338c4675b 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -10,12 +10,6 @@ fn main() { let proxy = events_loop.create_proxy(); - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - std::thread::spawn(move || { // Wake up the `events_loop` once every second. loop { diff --git a/examples/transparent.rs b/examples/transparent.rs index 29dd94157..de6331757 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -9,12 +9,6 @@ fn main() { window.set_title("A fantastic window!"); - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { println!("{:?}", event); diff --git a/examples/window.rs b/examples/window.rs index 6dc909a8d..65f0ade2b 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -8,12 +8,6 @@ fn main() { .build(&events_loop) .unwrap(); - if cfg!(target_os = "linux") { - println!("Running this example under wayland may not display a window at all.\n\ - This is normal and because this example does not actually draw anything in the window,\ - thus the compositor does not display it."); - } - events_loop.run_forever(|event| { println!("{:?}", event); diff --git a/src/os/unix.rs b/src/os/unix.rs index e6d436284..eb0cb8b02 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -121,13 +121,11 @@ pub trait WindowExt { /// Check if the window is ready for drawing /// - /// On wayland, drawing on a surface before the server has configured - /// it using a special event is illegal. As a result, you should wait - /// until this method returns `true`. + /// It is a remnant of a previous implementation detail for the + /// wayland backend, and is no longer relevant. /// - /// Once it starts returning `true`, it can never return `false` again. - /// - /// If the window is X11-based, this will just always return `true`. + /// Always return true. + #[deprecated] fn is_ready(&self) -> bool; } @@ -195,10 +193,7 @@ impl WindowExt for Window { #[inline] fn is_ready(&self) -> bool { - match self.window { - LinuxWindow::Wayland(ref w) => w.is_ready(), - LinuxWindow::X(_) => true - } + true } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index a5bfc75d1..4da5c64a5 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -1,8 +1,5 @@ use std::cell::RefCell; use std::collections::VecDeque; -use std::fs::File; -use std::io::Write; -use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -14,14 +11,12 @@ use super::keyboard::init_keyboard; use wayland_client::{EnvHandler, EnvNotify, default_connect, EventQueue, EventQueueHandle, Proxy, StateToken}; use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor, - wl_display, wl_registry, wl_output, wl_surface, wl_buffer, + wl_display, wl_registry, wl_output, wl_surface, wl_pointer, wl_keyboard}; -use super::wayland_window::{DecoratedSurface, Shell, init_decorated_surface, DecoratedSurfaceImplementation}; +use super::wayland_window::{Frame, Shell, create_frame, FrameImplementation}; use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6; -use super::tempfile; - pub struct EventsLoopSink { buffer: VecDeque<::Event> } @@ -345,10 +340,15 @@ impl EventsLoop { } // process pending resize/refresh evq.state().get_mut(&self.store).for_each( - |newsize, refresh, closed, wid, decorated| { - if let (Some((w, h)), Some(decorated)) = (newsize, decorated) { - decorated.resize(w as i32, h as i32); - sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid); + |newsize, refresh, frame_refresh, closed, wid, frame| { + if let Some(frame) = frame { + if let Some((w, h)) = newsize { + frame.resize(w as i32, h as i32); + frame.refresh(); + sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid); + } else if frame_refresh { + frame.refresh(); + } } if refresh { sink.send_event(::WindowEvent::Refresh, wid); @@ -360,50 +360,24 @@ impl EventsLoop { ) } - /// Creates a buffer of given size and assign it to the surface - /// - /// This buffer only contains white pixels, and is needed when using wl_shell - /// to make sure the window actually exists and can receive events before the - /// use starts its event loop - fn blank_surface(&self, surface: &wl_surface::WlSurface, width: i32, height: i32) { - let mut tmp = tempfile::tempfile().expect("Failed to create a tmpfile buffer."); - for _ in 0..(width*height) { - tmp.write_all(&[0xff,0xff,0xff,0xff]).unwrap(); - } - tmp.flush().unwrap(); - let mut evq = self.evq.borrow_mut(); - let pool = evq.state() - .get(&self.env_token) - .shm - .create_pool(tmp.as_raw_fd(), width*height*4); - let buffer = pool.create_buffer(0, width, height, width, wl_shm::Format::Argb8888) - .expect("Pool cannot be already dead"); - surface.attach(Some(&buffer), 0, 0); - surface.commit(); - // the buffer will keep the contents alive as needed - pool.destroy(); - // register the buffer for freeing - evq.register(&buffer, free_buffer(), Some(tmp)); - } - /// Create a new window with given dimensions /// /// Grabs a lock on the event queue in the process - pub fn create_window(&self, width: u32, height: u32, decorated: bool, implem: DecoratedSurfaceImplementation, idata: F) - -> (wl_surface::WlSurface, DecoratedSurface, bool) + pub fn create_window(&self, width: u32, height: u32, implem: FrameImplementation, idata: F) + -> (wl_surface::WlSurface, Frame) where F: FnOnce(&wl_surface::WlSurface) -> ID { - let (surface, decorated, xdg) = { + let (surface, frame) = { let mut guard = self.evq.borrow_mut(); let env = guard.state().get(&self.env_token).clone_inner().unwrap(); - let (shell, xdg) = match guard.state().get(&self.ctxt_token).shell { - Some(Shell::Wl(ref wl_shell)) => (Shell::Wl(wl_shell.clone().unwrap()), false), - Some(Shell::Xdg(ref xdg_shell)) => (Shell::Xdg(xdg_shell.clone().unwrap()), true), + let shell = match guard.state().get(&self.ctxt_token).shell { + Some(Shell::Wl(ref wl_shell)) => Shell::Wl(wl_shell.clone().unwrap()), + Some(Shell::Xdg(ref xdg_shell)) => Shell::Xdg(xdg_shell.clone().unwrap()), None => unreachable!() }; let seat = guard.state().get(&self.ctxt_token).seat.as_ref().and_then(|s| s.clone()); let surface = env.compositor.create_surface(); - let decorated = init_decorated_surface( + let frame = create_frame( &mut guard, implem, idata(&surface), @@ -412,21 +386,12 @@ impl EventsLoop { &env.subcompositor, &env.shm, &shell, - seat, - decorated + seat ).expect("Failed to create a tmpfile buffer."); - (surface, decorated, xdg) + (surface, frame) }; - if !xdg { - // if using wl_shell, we need to draw something in order to kickstart - // the event loop - // if using xdg_shell, it is an error to do it now, and the events loop will not - // be stuck. We cannot draw anything before having received an appropriate event - // from the compositor - self.blank_surface(&surface, width as i32, height as i32); - } - (surface, decorated, xdg) + (surface, frame) } } @@ -470,15 +435,6 @@ fn xdg_ping_implementation() -> zxdg_shell_v6::Implementation<()> { } } -fn free_buffer() -> wl_buffer::Implementation> { - wl_buffer::Implementation { - release: |_, data, buffer| { - buffer.destroy(); - *data = None; - } - } -} - struct SeatIData { sink: Arc>, pointer: Option, diff --git a/src/platform/linux/wayland/mod.rs b/src/platform/linux/wayland/mod.rs index 1cf6964db..732bfc3ea 100644 --- a/src/platform/linux/wayland/mod.rs +++ b/src/platform/linux/wayland/mod.rs @@ -6,7 +6,6 @@ pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorI extern crate wayland_kbd; extern crate wayland_window; extern crate wayland_protocols; -extern crate tempfile; use wayland_client::protocol::wl_surface; use wayland_client::Proxy; diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index eb48819be..a5c2057a9 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -8,14 +8,13 @@ use platform::MonitorId as PlatformMonitorId; use window::MonitorId as RootMonitorId; use super::{EventsLoop, WindowId, make_wid, MonitorId}; -use super::wayland_window::{DecoratedSurface, DecoratedSurfaceImplementation}; +use super::wayland_window::{Frame, FrameImplementation, State as FrameState}; use super::event_loop::StateContext; pub struct Window { surface: wl_surface::WlSurface, - decorated: Arc>, + frame: Arc>, monitors: Arc>, - ready: Arc>, size: Arc>, kill_switch: (Arc>, Arc>), display: Arc, @@ -27,24 +26,30 @@ impl Window { let (width, height) = attributes.dimensions.unwrap_or((800,600)); // Create the decorated surface - let ready = Arc::new(Mutex::new(false)); let size = Arc::new(Mutex::new((width, height))); let store_token = evlp.store.clone(); - let (surface, mut decorated, xdg) = evlp.create_window( - width, height, attributes.decorations, decorated_impl(), - |surface| DecoratedIData { - ready: ready.clone(), + let (surface, mut frame) = evlp.create_window( + width, height, decorated_impl(), + |surface| FrameIData { surface: surface.clone().unwrap(), store_token: store_token.clone() } ); - // If we are using xdg, we are not ready yet - { *ready.lock().unwrap() = !xdg; } // Check for fullscreen requirements if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen { let info = monitor_id.info.lock().unwrap(); - decorated.set_fullscreen(Some(&info.output)); + frame.set_state(FrameState::Fullscreen(Some(&info.output))); + } else if attributes.maximized { + frame.set_state(FrameState::Maximized); } + + // set decorations + frame.set_decorate(attributes.decorations); + + // min-max dimensions + frame.set_min_size(attributes.min_dimensions.map(|(w, h)| (w as i32, h as i32))); + frame.set_max_size(attributes.max_dimensions.map(|(w, h)| (w as i32, h as i32))); + // setup the monitor tracking let monitor_list = Arc::new(Mutex::new(MonitorList::default())); { @@ -52,12 +57,9 @@ impl Window { let idata = (evlp.ctxt_token.clone(), monitor_list.clone()); evq.register(&surface, surface_impl(), idata); } - // a surface commit with no buffer so that the compositor don't - // forget to configure us - surface.commit(); let kill_switch = Arc::new(Mutex::new(false)); - let decorated = Arc::new(Mutex::new(decorated)); + let frame = Arc::new(Mutex::new(frame)); { let mut evq = evlp.evq.borrow_mut(); @@ -65,9 +67,10 @@ impl Window { closed: false, newsize: None, need_refresh: false, + need_frame_refresh: true, surface: surface.clone().unwrap(), kill_switch: kill_switch.clone(), - decorated: Arc::downgrade(&decorated) + frame: Arc::downgrade(&frame) }); evq.sync_roundtrip().unwrap(); } @@ -75,9 +78,8 @@ impl Window { Ok(Window { display: evlp.display.clone(), surface: surface, - decorated: decorated, + frame: frame, monitors: monitor_list, - ready: ready, size: size, kill_switch: (kill_switch, evlp.cleanup_needed.clone()) }) @@ -89,7 +91,7 @@ impl Window { } pub fn set_title(&self, title: &str) { - self.decorated.lock().unwrap().set_title(title.into()); + self.frame.lock().unwrap().set_title(title.into()); } #[inline] @@ -127,7 +129,7 @@ impl Window { #[inline] // NOTE: This will only resize the borders, the contents must be updated by the user pub fn set_inner_size(&self, x: u32, y: u32) { - self.decorated.lock().unwrap().resize(x as i32, y as i32); + self.frame.lock().unwrap().resize(x as i32, y as i32); *(self.size.lock().unwrap()) = (x, y); } @@ -178,10 +180,6 @@ impl Window { let guard = self.monitors.lock().unwrap(); guard.monitors.last().unwrap().clone() } - - pub fn is_ready(&self) -> bool { - *self.ready.lock().unwrap() - } } impl Drop for Window { @@ -199,9 +197,10 @@ struct InternalWindow { surface: wl_surface::WlSurface, newsize: Option<(i32, i32)>, need_refresh: bool, + need_frame_refresh: bool, closed: bool, kill_switch: Arc>, - decorated: Weak> + frame: Weak> } pub struct WindowStore { @@ -235,14 +234,15 @@ impl WindowStore { } pub fn for_each(&mut self, mut f: F) - where F: FnMut(Option<(i32, i32)>, bool, bool, WindowId, Option<&mut DecoratedSurface>) + where F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut Frame>) { for window in &mut self.windows { - let opt_arc = window.decorated.upgrade(); + let opt_arc = window.frame.upgrade(); let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap()); f( window.newsize.take(), window.need_refresh, + window.need_frame_refresh, window.closed, make_wid(&window.surface), opt_mutex_lock.as_mut().map(|m| &mut **m) @@ -258,21 +258,20 @@ impl WindowStore { * Protocol implementation */ -struct DecoratedIData { - ready: Arc>, +struct FrameIData { store_token: StateToken, surface: wl_surface::WlSurface } -fn decorated_impl() -> DecoratedSurfaceImplementation { - DecoratedSurfaceImplementation { +fn decorated_impl() -> FrameImplementation { + FrameImplementation { configure: |evqh, idata, _, newsize| { - *idata.ready.lock().unwrap() = true; let store = evqh.state().get_mut(&idata.store_token); for window in &mut store.windows { if window.surface.equals(&idata.surface) { window.newsize = newsize; window.need_refresh = true; + window.need_frame_refresh = true; return; } } @@ -285,6 +284,15 @@ fn decorated_impl() -> DecoratedSurfaceImplementation { return; } } + }, + refresh: |evqh, idata| { + let store = evqh.state().get_mut(&idata.store_token); + for window in &mut store.windows { + if window.surface.equals(&idata.surface) { + window.need_frame_refresh = true; + return; + } + } } } }