diff --git a/Cargo.toml b/Cargo.toml index da966210..7d6c68ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ kernel32-sys = "0.2" dwmapi-sys = "0.1" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] -wayland-client = { version = "0.7.1", features = ["dlopen"] } +wayland-client = { version = "0.7.3", features = ["dlopen"] } wayland-kbd = "0.6" -wayland-window = "0.4" +wayland-window = "0.4.2" x11-dl = "2.8" diff --git a/src/api/wayland/context.rs b/src/api/wayland/context.rs index 28c4f204..73e4ac44 100644 --- a/src/api/wayland/context.rs +++ b/src/api/wayland/context.rs @@ -1,9 +1,13 @@ +use Event; + use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use wayland_client::{EnvHandler, default_connect, EventQueue, EventQueueHandle, Init, Proxy}; use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor, - wl_display, wl_registry, wl_output}; + wl_display, wl_registry, wl_output, wl_surface}; + +use super::wayland_window; /* * Registry and globals handling @@ -21,7 +25,8 @@ struct WaylandEnv { registry: wl_registry::WlRegistry, inner: EnvHandler, monitors: Vec, - my_id: usize + my_id: usize, + windows: Vec<(Arc,Arc>>)> } struct OutputInfo { @@ -50,9 +55,21 @@ impl WaylandEnv { registry: registry, inner: EnvHandler::new(), monitors: Vec::new(), - my_id: 0 + my_id: 0, + windows: Vec::new() } } + + fn get_seat(&self) -> Option { + for &(name, ref interface, version) in self.inner.globals() { + if interface == "wl_seat" { + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + let seat = self.registry.bind::(5, name).expect("Seat cannot be destroyed"); + return Some(seat) + } + } + None + } } impl Init for WaylandEnv { @@ -156,9 +173,11 @@ impl WaylandContext { Err(e) => return None }; - // this expect cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) let registry = display.get_registry().expect("Display cannot be already destroyed."); let env_id = event_queue.add_handler_with_init(WaylandEnv::new(registry)); + // two syncs fully initialize + event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); Some(WaylandContext { @@ -167,6 +186,59 @@ impl WaylandContext { env_id: env_id }) } + + pub fn dispatch_pending(&self) { + let mut guard = self.evq.lock().unwrap(); + guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); + } + + pub fn dispatch(&self) { + let mut guard = self.evq.lock().unwrap(); + guard.dispatch().expect("Wayland connection unexpectedly lost"); + } + + pub fn flush(&self) { + self.display.flush(); + } + + pub fn with_output(&self, id: MonitorId, f: F) where F: FnOnce(&wl_output::WlOutput) { + let mut guard = self.evq.lock().unwrap(); + let state = guard.state(); + let env = state.get_handler::(self.env_id); + for m in env.monitors.iter().filter(|m| m.id == id.id) { + f(&m.output); + break + } + } + + pub fn create_window(&self) + -> (Arc, Arc>>, wayland_window::DecoratedSurface) + { + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let env = state.get_mut_handler::(self.env_id); + // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) + let surface = Arc::new(env.inner.compositor.create_surface().expect("Compositor cannot be dead")); + let eventiter = Arc::new(Mutex::new(VecDeque::new())); + env.windows.push((surface.clone(), eventiter.clone())); + let decorated = wayland_window::DecoratedSurface::new( + &*surface, 800, 600, + &env.inner.compositor, + &env.inner.subcompositor, + &env.inner.shm, + &env.inner.shell, + env.get_seat(), + false + ).expect("Failed to create a tmpfile buffer."); + (surface, eventiter, decorated) + } + + pub fn prune_dead_windows(&self) { + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let env = state.get_mut_handler::(self.env_id); + env.windows.retain(|w| w.0.is_alive()); + } } pub fn get_primary_monitor(ctxt: &Arc) -> MonitorId { diff --git a/src/api/wayland/window.rs b/src/api/wayland/window.rs index 99b7e694..bdb063c9 100644 --- a/src/api/wayland/window.rs +++ b/src/api/wayland/window.rs @@ -1,10 +1,15 @@ -use std::sync::Arc; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; -use wayland_client::protocol::{wl_display,wl_surface}; +use wayland_client::{EventQueue, EventQueueHandle, Init}; +use wayland_client::protocol::{wl_display,wl_surface,wl_shell_surface}; use {CreationError, MouseCursor, CursorState, Event, WindowAttributes}; +use platform::MonitorId as PlatformMonitorId; use super::WaylandContext; +use super::wayland_window; +use super::wayland_window::DecoratedSurface; #[derive(Clone)] pub struct WindowProxy; @@ -17,7 +22,13 @@ impl WindowProxy { } pub struct Window { - resize_callback: Option + ctxt: Arc, + evq: Mutex, + eviter: Arc>>, + surface: Arc, + size: Mutex<(u32, u32)>, + handler_id: usize, + decorated_id: usize } pub struct PollEventsIterator<'a> { @@ -28,7 +39,7 @@ impl<'a> Iterator for PollEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option { - unimplemented!() + self.window.next_event(false) } } @@ -40,14 +51,112 @@ impl<'a> Iterator for WaitEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option { - unimplemented!() + self.window.next_event(true) } } impl Window { pub fn new(ctxt: Arc, attributes: &WindowAttributes) -> Result { - unimplemented!() + let (width, height) = attributes.dimensions.unwrap_or((800,600)); + + let mut evq = ctxt.display.create_event_queue(); + + let (surface, eviter, decorated) = ctxt.create_window::(); + + // init DecoratedSurface + let decorated_id = evq.add_handler_with_init(decorated); + { + let mut state = evq.state(); + let decorated = state.get_mut_handler::>(decorated_id); + *(decorated.handler()) = Some(DecoratedHandler::new()); + + if let Some(PlatformMonitorId::Wayland(ref monitor_id)) = attributes.monitor { + ctxt.with_output(monitor_id.clone(), |output| { + decorated.set_fullscreen( + wl_shell_surface::FullscreenMethod::Default, + 0, + Some(output) + ) + }); + } else if attributes.decorations { + decorated.set_decorate(true); + } + } + + // init general handler + let handler = WindowHandler::new(); + let handler_id = evq.add_handler_with_init(handler); + + Ok(Window { + ctxt: ctxt, + evq: Mutex::new(evq), + eviter: eviter, + surface: surface, + size: Mutex::new((width, height)), + handler_id: handler_id, + decorated_id: decorated_id + }) + } + + fn process_resize(&self) { + use std::cmp::max; + let mut evq_guard = self.evq.lock().unwrap(); + let mut state = evq_guard.state(); + let newsize = { + let decorated = state.get_mut_handler::>(self.decorated_id); + let newsize = decorated.handler().as_mut().and_then(|h| h.take_newsize()); + if let Some((w, h)) = newsize { + decorated.resize(w as i32, h as i32); + *self.size.lock().unwrap() = (w, h); + } + newsize + }; + // callback_resize if any + if let Some((w, h)) = newsize { + let mut handler = state.get_mut_handler::(self.handler_id); + if let Some(ref callback) = handler.resize_callback { + callback(w, h); + } + self.eviter.lock().unwrap().push_back(Event::Resized(w,h)); + } + } + + fn next_event(&self, block: bool) -> Option { + let mut evt = { + let mut guard = self.eviter.lock().unwrap(); + guard.pop_front() + }; + if evt.is_some() { return evt } + + // try a pending dispatch + // TODO: insert a non-blocking read from socket, overwise no new events will ever come + { + self.ctxt.dispatch_pending(); + let mut guard = self.evq.lock().unwrap().dispatch_pending(); + // some events were dispatched, need to process a potential resising + self.process_resize(); + } + + let mut evt = { + let mut guard = self.eviter.lock().unwrap(); + guard.pop_front() + }; + + while block && evt.is_none() { + // no event waiting, need to repopulate! + { + self.ctxt.flush(); + self.ctxt.dispatch(); + let mut guard = self.evq.lock().unwrap().dispatch(); + // some events were dispatched, need to process a potential resising + self.process_resize(); + } + // try again + let mut guard = self.eviter.lock().unwrap(); + evt = guard.pop_front() + } + evt } pub fn set_title(&self, title: &str) { @@ -112,7 +221,10 @@ impl Window { #[inline] pub fn set_window_resize_callback(&mut self, callback: Option) { - self.resize_callback = callback; + let mut guard = self.evq.lock().unwrap(); + let mut state = guard.state(); + let mut handler = state.get_mut_handler::(self.handler_id); + handler.resize_callback = callback; } #[inline] @@ -144,16 +256,59 @@ impl Window { } pub fn get_display(&self) -> &wl_display::WlDisplay { - unimplemented!() + &self.ctxt.display } pub fn get_surface(&self) -> &wl_surface::WlSurface { - unimplemented!() + &self.surface } } impl Drop for Window { fn drop(&mut self) { - // TODO + self.surface.destroy(); + self.ctxt.prune_dead_windows(); + } +} + +struct DecoratedHandler { + newsize: Option<(u32, u32)> +} + +impl DecoratedHandler { + fn new() -> DecoratedHandler { DecoratedHandler { newsize: None }} + fn take_newsize(&mut self) -> Option<(u32, u32)> { + self.newsize.take() + } +} + +impl wayland_window::Handler for DecoratedHandler { + fn configure(&mut self, + _: &mut EventQueueHandle, + _: wl_shell_surface::Resize, + width: i32, height: i32) + { + use std::cmp::max; + self.newsize = Some((max(width,1) as u32, max(height,1) as u32)); + } +} + +struct WindowHandler { + my_id: usize, + resize_callback: Option, +} + +impl WindowHandler { + fn new() -> WindowHandler { + WindowHandler { + my_id: 0, + resize_callback: None + } + } +} + +impl Init for WindowHandler { + fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) { + self.my_id = index; } }