From 1cea6a65a44dee4d0bd85a35f8ac531017c4951f Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 16 Aug 2015 14:12:21 +0200 Subject: [PATCH 1/2] Update wayland backend to draw decorations. Integrate with wayland-window crate to draw decorations allowing resize & move of the window. Leaving the wayland backend as disabled until full usability is ensured. --- Cargo.toml | 12 ++- src/api/wayland/context.rs | 10 +- src/api/wayland/mod.rs | 182 +++++++++++++++++++++++++++++++------ 3 files changed, 172 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f4f0160..f6232f5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,24 +75,28 @@ dwmapi-sys = "0.1" [target.i686-unknown-linux-gnu.dependencies] osmesa-sys = "0.0.5" -wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] } +wayland-client = { version = "0.2.1", features = ["egl", "dlopen"] } wayland-kbd = "0.2.0" +wayland-window = "0.1.0" x11-dl = "~2.0" [target.x86_64-unknown-linux-gnu.dependencies] osmesa-sys = "0.0.5" -wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] } +wayland-client = { version = "0.2.1", features = ["egl", "dlopen"] } wayland-kbd = "0.2.0" +wayland-window = "0.1.0" x11-dl = "~2.0" [target.arm-unknown-linux-gnueabihf.dependencies] osmesa-sys = "0.0.5" -wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] } +wayland-client = { version = "0.2.1", features = ["egl", "dlopen"] } wayland-kbd = "0.2.0" +wayland-window = "0.1.0" x11-dl = "~2.0" [target.aarch64-unknown-linux-gnu.dependencies] osmesa-sys = "0.0.5" -wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] } +wayland-client = { version = "0.2.1", features = ["egl", "dlopen"] } wayland-kbd = "0.2.0" +wayland-window = "0.1.0" x11-dl = "~2.0" diff --git a/src/api/wayland/context.rs b/src/api/wayland/context.rs index 1af63115..6d7df86f 100644 --- a/src/api/wayland/context.rs +++ b/src/api/wayland/context.rs @@ -25,7 +25,7 @@ pub struct WaylandContext { pub compositor: Compositor, pub shell: Shell, pub seat: Seat, - pub pointer: Option>, + pointer: Option>>, keyboard: Option, windows_event_queues: Arc>>>>>, current_pointer_surface: Arc>>, @@ -196,7 +196,7 @@ impl WaylandContext { compositor: compositor, shell: shell, seat: seat, - pointer: pointer, + pointer: pointer.map(|p| Mutex::new(p)), keyboard: keyboard, windows_event_queues: windows_event_queues, current_pointer_surface: current_pointer_surface, @@ -207,9 +207,15 @@ impl WaylandContext { pub fn register_surface(&self, sid: SurfaceId, queue: Arc>>) { self.windows_event_queues.lock().unwrap().insert(sid, queue); + if let Some(ref p) = self.pointer { + p.lock().unwrap().add_handled_surface(sid); + } } pub fn deregister_surface(&self, sid: SurfaceId) { self.windows_event_queues.lock().unwrap().remove(&sid); + if let Some(ref p) = self.pointer { + p.lock().unwrap().remove_handled_surface(sid); + } } } \ No newline at end of file diff --git a/src/api/wayland/mod.rs b/src/api/wayland/mod.rs index 77ebc16d..12a909d5 100644 --- a/src/api/wayland/mod.rs +++ b/src/api/wayland/mod.rs @@ -6,6 +6,8 @@ use self::wayland::core::Surface; use self::wayland::core::output::Output; use self::wayland::core::shell::{ShellSurface, ShellFullscreenMethod}; +use self::wayland_window::{DecoratedSurface, SurfaceGuard, substract_borders}; + use libc; use api::dlopen; use api::egl; @@ -21,6 +23,7 @@ use MouseCursor; use GlContext; use std::collections::VecDeque; +use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Mutex}; use std::ffi::CString; @@ -30,6 +33,7 @@ use self::context::WaylandContext; extern crate wayland_client as wayland; extern crate wayland_kbd; +extern crate wayland_window; mod context; mod keyboard; @@ -44,12 +48,97 @@ pub fn is_available() -> bool { WAYLAND_CONTEXT.is_some() } +enum ShellWindow { + Plain(ShellSurface), + Decorated(DecoratedSurface) +} + +impl ShellWindow { + fn get_shell(&mut self) -> ShellGuard { + match self { + &mut ShellWindow::Plain(ref mut s) => { + ShellGuard::Plain(s) + }, + &mut ShellWindow::Decorated(ref mut s) => { + ShellGuard::Decorated(s.get_shell()) + } + } + } + + fn resize(&mut self, w: i32, h: i32, x: i32, y: i32) { + match self { + &mut ShellWindow::Plain(ref s) => s.resize(w, h, x, y), + &mut ShellWindow::Decorated(ref mut s) => { + s.resize(w, h); + s.get_shell().resize(w, h, x, y); + } + } + } + + fn set_cfg_callback(&mut self, arc: Arc>) { + match self { + &mut ShellWindow::Decorated(ref mut s) => { + s.get_shell().set_configure_callback(move |_, w, h| { + let (w, h) = substract_borders(w, h); + let mut guard = arc.lock().unwrap(); + *guard = (w, h, true); + }) + } + _ => {} + } + } +} + +enum ShellGuard<'a> { + Plain(&'a mut ShellSurface), + Decorated(SurfaceGuard<'a, EGLSurface>) +} + +impl<'a> Deref for ShellGuard<'a> { + type Target = ShellSurface; + fn deref(&self) -> &ShellSurface { + match self { + &ShellGuard::Plain(ref s) => s, + &ShellGuard::Decorated(ref s) => s.deref() + } + } +} + +impl<'a> DerefMut for ShellGuard<'a> { + fn deref_mut(&mut self) -> &mut ShellSurface { + match self { + &mut ShellGuard::Plain(ref mut s) => s, + &mut ShellGuard::Decorated(ref mut s) => s.deref_mut() + } + } +} + pub struct Window { - shell_surface: ShellSurface, + shell_window: Mutex, pending_events: Arc>>, + need_resize: Arc>, + resize_callback: Option, pub context: EglContext, } +// private methods of wayalnd windows + +impl Window { + fn resize_if_needed(&self) -> bool { + let mut guard = self.need_resize.lock().unwrap(); + let (w, h, b) = *guard; + *guard = (0, 0, false); + if b { + let mut guard = self.shell_window.lock().unwrap(); + guard.resize(w, h, 0, 0); + if let Some(f) = self.resize_callback { + f(w as u32, h as u32); + } + } + b + } +} + #[derive(Clone)] pub struct WindowProxy; @@ -113,7 +202,11 @@ impl<'a> Iterator for PollEventsIterator<'a> { if let Some(ref ctxt) = *WAYLAND_CONTEXT { ctxt.display.dispatch_pending(); } - self.window.pending_events.lock().unwrap().pop_front() + if self.window.resize_if_needed() { + Some(Event::Refresh) + } else { + self.window.pending_events.lock().unwrap().pop_front() + } } } @@ -125,10 +218,18 @@ impl<'a> Iterator for WaitEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option { - if let Some(ref ctxt) = *WAYLAND_CONTEXT { - ctxt.display.dispatch(); + let mut evt = None; + while evt.is_none() { + if let Some(ref ctxt) = *WAYLAND_CONTEXT { + ctxt.display.dispatch(); + } + evt = if self.window.resize_if_needed() { + Some(Event::Refresh) + } else { + self.window.pending_events.lock().unwrap().pop_front() + }; } - self.window.pending_events.lock().unwrap().pop_front() + evt } } @@ -151,12 +252,22 @@ impl Window { h as i32 ); - let shell_surface = wayland_context.shell.get_shell_surface(surface); - if let Some(PlatformMonitorID::Wayland(ref monitor)) = builder.monitor { + let mut shell_window = if let Some(PlatformMonitorID::Wayland(ref monitor)) = builder.monitor { + let shell_surface = wayland_context.shell.get_shell_surface(surface); shell_surface.set_fullscreen(ShellFullscreenMethod::Default, Some(&monitor.output)); + ShellWindow::Plain(shell_surface) } else { - shell_surface.set_toplevel(); - } + ShellWindow::Decorated(match DecoratedSurface::new( + surface, + w as i32, + h as i32, + &wayland_context.registry, + Some(&wayland_context.seat) + ) { + Ok(s) => s, + Err(_) => return Err(CreationError::NotSupported) + }) + }; let context = { let libegl = unsafe { dlopen::dlopen(b"libEGL.so\0".as_ptr() as *const _, dlopen::RTLD_NOW) }; @@ -171,26 +282,43 @@ impl Window { egl, &builder, egl::NativeDisplay::Wayland(Some(wayland_context.display.ptr() as *const _))) - .and_then(|p| p.finish((*shell_surface).ptr() as *const _)) + .and_then(|p| p.finish((**shell_window.get_shell()).ptr() as *const _)) ) }; - let events = Arc::new(Mutex::new(VecDeque::new())); + // create a queue already containing a refresh event to trigger first draw + // it's harmless and a removes the need to do a first swap_buffers() before + // starting the event loop + let events = Arc::new(Mutex::new({ + let mut v = VecDeque::new(); + v.push_back(Event::Refresh); + v + })); - wayland_context.register_surface(shell_surface.get_wsurface().get_id(), events.clone()); + wayland_context.register_surface(shell_window.get_shell().get_wsurface().get_id(), + events.clone()); + + let need_resize = Arc::new(Mutex::new((0, 0, false))); + + shell_window.set_cfg_callback(need_resize.clone()); wayland_context.display.flush().unwrap(); Ok(Window { - shell_surface: shell_surface, + shell_window: Mutex::new(shell_window), pending_events: events, + need_resize: need_resize, + resize_callback: None, context: context }) } pub fn set_title(&self, title: &str) { let ctitle = CString::new(title).unwrap(); - self.shell_surface.set_title(&ctitle); + // intermediate variable is forced, + // see https://github.com/rust-lang/rust/issues/22921 + let mut guard = self.shell_window.lock().unwrap(); + guard.get_shell().set_title(&ctitle); } pub fn show(&self) { @@ -211,7 +339,11 @@ impl Window { } pub fn get_inner_size(&self) -> Option<(u32, u32)> { - let (w, h) = self.shell_surface.get_attached_size(); + // intermediate variables are forced, + // see https://github.com/rust-lang/rust/issues/22921 + let mut guard = self.shell_window.lock().unwrap(); + let shell = guard.get_shell(); + let (w, h) = shell.get_attached_size(); Some((w as u32, h as u32)) } @@ -222,7 +354,7 @@ impl Window { } pub fn set_inner_size(&self, x: u32, y: u32) { - self.shell_surface.resize(x as i32, y as i32, 0, 0) + self.shell_window.lock().unwrap().resize(x as i32, y as i32, 0, 0) } pub fn create_window_proxy(&self) -> WindowProxy { @@ -242,15 +374,7 @@ impl Window { } pub fn set_window_resize_callback(&mut self, callback: Option) { - if let Some(callback) = callback { - self.shell_surface.set_configure_callback( - move |_,w,h| { callback(w as u32, h as u32) } - ); - } else { - self.shell_surface.set_configure_callback( - move |_,_,_| {} - ); - } + self.resize_callback = callback; } pub fn set_cursor(&self, cursor: MouseCursor) { @@ -309,7 +433,13 @@ impl GlContext for Window { impl Drop for Window { fn drop(&mut self) { if let Some(ref ctxt) = *WAYLAND_CONTEXT { - ctxt.deregister_surface(self.shell_surface.get_wsurface().get_id()) + // intermediate variable is forced, + // see https://github.com/rust-lang/rust/issues/22921 + let mut guard = self.shell_window.lock().unwrap(); + let shell = guard.get_shell(); + ctxt.deregister_surface( + shell.get_wsurface().get_id() + ) } } } From f7f52c21a02bdd26889093427adb9659b5bcf8dc Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 16 Aug 2015 16:21:26 +0200 Subject: [PATCH 2/2] wayland: properly handle 'decorated' option. --- src/api/wayland/mod.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/api/wayland/mod.rs b/src/api/wayland/mod.rs index 12a909d5..ba1931b1 100644 --- a/src/api/wayland/mod.rs +++ b/src/api/wayland/mod.rs @@ -257,16 +257,20 @@ impl Window { shell_surface.set_fullscreen(ShellFullscreenMethod::Default, Some(&monitor.output)); ShellWindow::Plain(shell_surface) } else { - ShellWindow::Decorated(match DecoratedSurface::new( - surface, - w as i32, - h as i32, - &wayland_context.registry, - Some(&wayland_context.seat) - ) { - Ok(s) => s, - Err(_) => return Err(CreationError::NotSupported) - }) + if builder.decorations { + ShellWindow::Decorated(match DecoratedSurface::new( + surface, + w as i32, + h as i32, + &wayland_context.registry, + Some(&wayland_context.seat) + ) { + Ok(s) => s, + Err(_) => return Err(CreationError::NotSupported) + }) + } else { + ShellWindow::Plain(wayland_context.shell.get_shell_surface(surface)) + } }; let context = {