From d10312c6b149f366c89c3e0cba1e21311f176fdc Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 20 Oct 2017 09:46:42 +0200 Subject: [PATCH] Rewrite of wayland backend to new wayland-client API (#325) * wayland: clean state for rewrite to new wayland-client API * wayland: context init * wayland: Monitors logic * wayland: Basic event loop logic * wayland: Keyboard handling * wayland: pointer handling * wayland: refactor to remove WaylandContext * wayland: window logic * wayland: event dispatching logic * wayland: update changelog --- CHANGELOG.md | 2 + Cargo.toml | 8 +- src/platform/linux/mod.rs | 8 +- src/platform/linux/wayland/context.rs | 431 -------- src/platform/linux/wayland/event_loop.rs | 1132 ++++++++++++---------- src/platform/linux/wayland/keyboard.rs | 162 +++- src/platform/linux/wayland/mod.rs | 21 +- src/platform/linux/wayland/window.rs | 347 ++++--- 8 files changed, 953 insertions(+), 1158 deletions(-) delete mode 100644 src/platform/linux/wayland/context.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ccb22d..1d5be754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ - **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints, but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`. - `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`. +- Rewrite of the wayland backend to use wayland-client-0.11 +- Support for dead keys on wayland for keyboard utf8 input # Version 0.8.3 (2017-10-11) diff --git a/Cargo.toml b/Cargo.toml index dd8df742..57af14d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,9 @@ 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.9.9", features = ["dlopen"] } -wayland-protocols = { version = "0.9.9", features = ["unstable_protocols"] } -wayland-kbd = "0.9.1" -wayland-window = "0.8.0" +wayland-client = { version = "0.11.2", features = ["dlopen"] } +wayland-protocols = { version = "0.11.2", features = ["unstable_protocols"] } +wayland-kbd = "0.12.0" +wayland-window = "0.11.0" tempfile = "2.1" x11-dl = "2.8" diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 33f7c3cc..520e75ba 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -332,8 +332,8 @@ impl EventsLoop { } pub fn new_wayland() -> Result { - wayland::WaylandContext::init() - .map(|ctx| EventsLoop::Wayland(wayland::EventsLoop::new(ctx))) + wayland::EventsLoop::new() + .map(EventsLoop::Wayland) .ok_or(()) } @@ -347,7 +347,7 @@ impl EventsLoop { #[inline] pub fn get_available_monitors(&self) -> VecDeque { match *self { - EventsLoop::Wayland(ref evlp) => wayland::get_available_monitors(evlp.context()) + EventsLoop::Wayland(ref evlp) => evlp.get_available_monitors() .into_iter() .map(MonitorId::Wayland) .collect(), @@ -361,7 +361,7 @@ impl EventsLoop { #[inline] pub fn get_primary_monitor(&self) -> MonitorId { match *self { - EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(wayland::get_primary_monitor(evlp.context())), + EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()), EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())), } } diff --git a/src/platform/linux/wayland/context.rs b/src/platform/linux/wayland/context.rs deleted file mode 100644 index 57018e80..00000000 --- a/src/platform/linux/wayland/context.rs +++ /dev/null @@ -1,431 +0,0 @@ -use std::collections::VecDeque; -use std::fs::File; -use std::io::Write; -use std::os::unix::io::AsRawFd; -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_surface, wl_buffer}; - -use super::wayland_protocols::unstable::xdg_shell::client::zxdg_shell_v6; - -use super::{wayland_window, tempfile}; - -use super::wayland_window::Shell; - -/* - * Registry and globals handling - */ - -wayland_env!(InnerEnv, - compositor: wl_compositor::WlCompositor, - shm: wl_shm::WlShm, - subcompositor: wl_subcompositor::WlSubcompositor -); - -struct WaylandEnv { - registry: wl_registry::WlRegistry, - inner: EnvHandler, - shell: Option, - monitors: Vec, - my_id: usize, -} - -struct OutputInfo { - output: wl_output::WlOutput, - id: u32, - scale: f32, - pix_size: (u32, u32), - pix_pos: (i32, i32), - name: String -} - -impl OutputInfo { - fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo { - OutputInfo { - output: output, - id: id, - scale: 1.0, - pix_size: (0, 0), - pix_pos: (0, 0), - name: "".into() - } - } -} - -impl WaylandEnv { - fn new(registry: wl_registry::WlRegistry) -> WaylandEnv { - WaylandEnv { - registry: registry, - inner: EnvHandler::new(), - shell: None, - monitors: Vec::new(), - my_id: 0, - } - } - - fn get_seat(&self) -> Option { - for &(name, ref interface, version) in self.inner.globals() { - if interface == "wl_seat" { - if version < 5 { - panic!("Winit requires at least version 5 of the wl_seat global."); - } - let seat = self.registry.bind::(5, name); - return Some(seat) - } - } - None - } - - fn ensure_shell(&mut self) -> bool { - if self.shell.is_some() { - return true; - } - // xdg_shell is not available, so initialize wl_shell - for &(name, ref interface, _) in self.inner.globals() { - if interface == "wl_shell" { - self.shell = Some(Shell::Wl(self.registry.bind::(1, name))); - return true; - } - } - return false; - } - - fn get_shell(&self) -> &Shell { - self.shell.as_ref().expect("Shell was not properly initialized") - } -} - -impl Init for WaylandEnv { - fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) { - evqh.register::<_, WaylandEnv>(&self.registry, index); - self.my_id = index - } -} - -impl wl_registry::Handler for WaylandEnv { - fn global(&mut self, - evqh: &mut EventQueueHandle, - registry: &wl_registry::WlRegistry, - name: u32, - interface: String, - version: u32) - { - if interface == wl_output::WlOutput::interface_name() { - // intercept outputs - let output = self.registry.bind::(1, name); - evqh.register::<_, WaylandEnv>(&output, self.my_id); - self.monitors.push(OutputInfo::new(output, name)); - } else if interface == zxdg_shell_v6::ZxdgShellV6::interface_name() { - let xdg_shell = self.registry.bind::(1, name); - let xdg_ping_hid = evqh.add_handler(XdgShellPingHandler); - evqh.register::<_, XdgShellPingHandler>(&xdg_shell, xdg_ping_hid); - self.shell = Some(Shell::Xdg(xdg_shell)); - } - - self.inner.global(evqh, registry, name, interface, version); - } - - fn global_remove(&mut self, - evqh: &mut EventQueueHandle, - registry: &wl_registry::WlRegistry, - name: u32) - { - // prune old monitors - self.monitors.retain(|m| m.id != name); - self.inner.global_remove(evqh, registry, name); - } -} - -struct XdgShellPingHandler; - -impl zxdg_shell_v6::Handler for XdgShellPingHandler { - fn ping(&mut self, _: &mut EventQueueHandle, proxy: &zxdg_shell_v6::ZxdgShellV6, serial: u32) { - proxy.pong(serial); - } -} - -declare_handler!(XdgShellPingHandler, zxdg_shell_v6::Handler, zxdg_shell_v6::ZxdgShellV6); - -declare_handler!(WaylandEnv, wl_registry::Handler, wl_registry::WlRegistry); - -impl wl_output::Handler for WaylandEnv { - fn geometry(&mut self, - _: &mut EventQueueHandle, - proxy: &wl_output::WlOutput, - x: i32, y: i32, - _physical_width: i32, _physical_height: i32, - _subpixel: wl_output::Subpixel, - make: String, model: String, - _transform: wl_output::Transform) - { - for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) { - m.name = format!("{} ({})", model, make); - m.pix_pos = (x, y); - break; - } - } - fn mode(&mut self, - _: &mut EventQueueHandle, - proxy: &wl_output::WlOutput, - flags: wl_output::Mode, - width: i32, height: i32, - _refresh: i32) - { - if flags.contains(wl_output::Current) { - for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) { - m.pix_size = (width as u32, height as u32); - break; - } - } - } - fn scale(&mut self, - _: &mut EventQueueHandle, - proxy: &wl_output::WlOutput, - factor: i32) - { - for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) { - m.scale = factor as f32; - break; - } - } -} - -declare_handler!(WaylandEnv, wl_output::Handler, wl_output::WlOutput); - -/* - * Main context struct - */ - -pub struct WaylandContext { - pub display: wl_display::WlDisplay, - pub evq: Mutex, - env_id: usize, -} - -impl WaylandContext { - pub fn init() -> Option { - // attempt to connect to the wayland server - // this handles both "no libwayland" and "no compositor" cases - let (display, mut event_queue) = match default_connect() { - Ok(ret) => ret, - Err(_) => return None - }; - - // this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69) - let registry = display.get_registry(); - let env_id = event_queue.add_handler_with_init(WaylandEnv::new(registry)); - // two round trips to fully initialize - event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); - event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); - - { - let mut state = event_queue.state(); - let mut env = state.get_mut_handler::(env_id); - if !env.ensure_shell() { - // This is a compositor bug, it _must_ at least support xl_shell - panic!("Compositor didi not advertize xdg_shell not wl_shell."); - } - } - - Some(WaylandContext { - evq: Mutex::new(event_queue), - display: display, - env_id: env_id - }) - } - - pub fn read_events(&self) { - let evq_guard = self.evq.lock().unwrap(); - // read some events from the socket if some are waiting & queue is empty - if let Some(guard) = evq_guard.prepare_read() { - guard.read_events().expect("Wayland connection unexpectedly lost"); - } - } - - 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) { - let _ = self.display.flush(); - } - - pub fn get_seat(&self) -> Option { - let mut guard = self.evq.lock().unwrap(); - let state = guard.state(); - state.get_handler::(self.env_id).get_seat() - } - - 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 - } - } - - fn blank_surface(&self, surface: &wl_surface::WlSurface, evq: &mut EventQueue, 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 pool = { - let mut state = evq.state(); - let env = state.get_mut_handler::(self.env_id); - env.inner.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(); - - // create a handler to clean up initial buffer - let init_buffer_handler = InitialBufferHandler { - initial_buffer: Some((buffer.clone().unwrap(), tmp)) - }; - let initial_buffer_handler_id = evq.add_handler(init_buffer_handler); - // register the buffer to it - evq.register::<_, InitialBufferHandler>(&buffer, initial_buffer_handler_id); - } - - pub fn create_window(&self, width: u32, height: u32, decorated: bool) - -> (Arc, wayland_window::DecoratedSurface, bool) - { - let mut guard = self.evq.lock().unwrap(); - let (surface, decorated, xdg) = { - let mut state = guard.state(); - let env = state.get_mut_handler::(self.env_id); - let surface = Arc::new(env.inner.compositor.create_surface()); - let decorated = wayland_window::DecoratedSurface::new( - &*surface, 800, 600, - &env.inner.compositor, - &env.inner.subcompositor, - &env.inner.shm, - env.get_shell(), - env.get_seat(), - decorated - ).expect("Failed to create a tmpfile buffer."); - let xdg = match env.get_shell() { - &Shell::Xdg(_) => true, - &Shell::Wl(_) => false - }; - (surface, decorated, xdg) - }; - - 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, &mut *guard, width as i32, height as i32); - } - (surface, decorated, xdg) - } -} - -/* - * Monitors API - */ - -pub fn get_primary_monitor(ctxt: &Arc) -> MonitorId { - let mut guard = ctxt.evq.lock().unwrap(); - let state = guard.state(); - let env = state.get_handler::(ctxt.env_id); - if let Some(ref monitor) = env.monitors.iter().next() { - MonitorId { - id: monitor.id, - ctxt: ctxt.clone() - } - } else { - panic!("No monitor is available.") - } -} - -pub fn get_available_monitors(ctxt: &Arc) -> VecDeque { - let mut guard = ctxt.evq.lock().unwrap(); - let state = guard.state(); - let env = state.get_handler::(ctxt.env_id); - env.monitors.iter() - .map(|m| MonitorId { id: m.id, ctxt: ctxt.clone() }) - .collect() -} - -#[derive(Clone)] -pub struct MonitorId { - id: u32, - ctxt: Arc -} - -impl MonitorId { - pub fn get_name(&self) -> Option { - let mut guard = self.ctxt.evq.lock().unwrap(); - let state = guard.state(); - let env = state.get_handler::(self.ctxt.env_id); - for m in env.monitors.iter().filter(|m| m.id == self.id) { - return Some(m.name.clone()) - } - // if we reach here, this monitor does not exist any more - None - } - - #[inline] - pub fn get_native_identifier(&self) -> u32 { - self.id - } - - pub fn get_dimensions(&self) -> (u32, u32) { - let mut guard = self.ctxt.evq.lock().unwrap(); - let state = guard.state(); - let env = state.get_handler::(self.ctxt.env_id); - for m in env.monitors.iter().filter(|m| m.id == self.id) { - return m.pix_size - } - // if we reach here, this monitor does not exist any more - (0,0) - } - - pub fn get_position(&self) -> (i32, i32) { - let mut guard = self.ctxt.evq.lock().unwrap(); - let state = guard.state(); - let env = state.get_handler::(self.ctxt.env_id); - for m in env.monitors.iter().filter(|m| m.id == self.id) { - return m.pix_pos - } - // if we reach here, this monitor does not exist any more - (0,0) - } - - #[inline] - pub fn get_hidpi_factor(&self) -> f32 { - 1.0 - } -} - -// a handler to release the ressources acquired to draw the initial white screen as soon as -// the compositor does not use them any more -pub struct InitialBufferHandler { - initial_buffer: Option<(wl_buffer::WlBuffer, File)> -} - -impl wl_buffer::Handler for InitialBufferHandler { - fn release(&mut self, _: &mut EventQueueHandle, buffer: &wl_buffer::WlBuffer) { - // release the ressources we've acquired for initial white window - buffer.destroy(); - self.initial_buffer = None; - } -} - -declare_handler!(InitialBufferHandler, wl_buffer::Handler, wl_buffer::WlBuffer); diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index bab2aeda..c35cb4fe 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -1,30 +1,27 @@ -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, - KeyboardInput, EventsLoopClosed, ControlFlow}; - +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::{self, AtomicBool}; +use std::sync::atomic::{AtomicBool, Ordering}; -use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext}; +use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, EventsLoopClosed, ControlFlow}; +use super::{WindowId, DeviceId}; +use super::window::WindowStore; +use super::keyboard::init_keyboard; -use wayland_client::{EventQueue, EventQueueHandle, Init, Proxy, Liveness}; -use wayland_client::protocol::{wl_seat, wl_surface, wl_pointer, wl_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_pointer, wl_keyboard}; -use super::make_wid; -use super::wayland_window::DecoratedSurface; -use super::wayland_kbd::MappedKeyboard; -use super::keyboard::KbdHandler; +use super::wayland_window::{DecoratedSurface, Shell, init_decorated_surface, DecoratedSurfaceImplementation}; +use super::wayland_protocols::unstable::xdg_shell::client::zxdg_shell_v6; + +use super::tempfile; -/// This struct is used as a holder for the callback -/// during the dispatching of events. -/// -/// The proper ay to use it is: -/// - set a callback in it (and retrieve the noop one it contains) -/// - dispatch the EventQueue -/// - put back the noop callback in it -/// -/// Failure to do so is unsafeā„¢ pub struct EventsLoopSink { buffer: VecDeque<::Event> } @@ -32,7 +29,8 @@ pub struct EventsLoopSink { unsafe impl Send for EventsLoopSink { } impl EventsLoopSink { - pub fn new() -> EventsLoopSink{EventsLoopSink { + pub fn new() -> EventsLoopSink{ + EventsLoopSink { buffer: VecDeque::new() } } @@ -57,24 +55,29 @@ impl EventsLoopSink { } pub struct EventsLoop { - // the wayland context - ctxt: Arc, - // ids of the DecoratedHandlers of the surfaces we know - decorated_ids: Mutex)>>, + // The wayland display + pub display: Arc, + // The Event Queue + pub evq: RefCell, // our sink, shared with some handlers, buffering the events sink: Arc>, - // trigger cleanup of the dead surfaces - cleanup_needed: Arc, // Whether or not there is a pending `Awakened` event to be emitted. pending_wakeup: Arc, - hid: usize, + // The window store + pub store: StateToken, + // the env + env_token: StateToken>, + // the ctxt + pub ctxt_token: StateToken, + // a cleanup switch to prune dead windows + pub cleanup_needed: Arc> } // A handle that can be sent across threads and used to wake up the `EventsLoop`. // // We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs. pub struct EventsLoopProxy { - ctxt: Weak, + display: Weak, pending_wakeup: Weak, } @@ -83,15 +86,15 @@ impl EventsLoopProxy { // // Returns `Err` if the associated `EventsLoop` no longer exists. pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { - let ctxt = self.ctxt.upgrade(); + let display = self.display.upgrade(); let wakeup = self.pending_wakeup.upgrade(); - match (ctxt, wakeup) { - (Some(ctxt), Some(wakeup)) => { + match (display, wakeup) { + (Some(display), Some(wakeup)) => { // Update the `EventsLoop`'s `pending_wakeup` flag. - wakeup.store(true, atomic::Ordering::Relaxed); + wakeup.store(true, Ordering::Relaxed); // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. - ctxt.display.sync(); - ctxt.display.flush().ok(); + display.sync(); + display.flush().map_err(|_| EventsLoopClosed)?; Ok(()) }, _ => Err(EventsLoopClosed), @@ -100,121 +103,93 @@ impl EventsLoopProxy { } impl EventsLoop { - pub fn new(mut ctxt: WaylandContext) -> EventsLoop { - let sink = Arc::new(Mutex::new(EventsLoopSink::new())); - let inputh = InputHandler::new(&ctxt, sink.clone()); - let hid = ctxt.evq.get_mut().unwrap().add_handler_with_init(inputh); - let ctxt = Arc::new(ctxt); + pub fn new() -> Option { + let (display, mut event_queue) = match default_connect() { + Ok(ret) => ret, + Err(_) => return None + }; - EventsLoop { - ctxt: ctxt, - decorated_ids: Mutex::new(Vec::new()), + let registry = display.get_registry(); + let ctxt_token = event_queue.state().insert( + StateContext::new(registry.clone().unwrap()) + ); + let env_token = EnvHandler::init_with_notify( + &mut event_queue, + ®istry, + env_notify(), + ctxt_token.clone() + ); + + // two round trips to fully initialize + event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); + event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost"); + + event_queue.state().with_value(&ctxt_token, |proxy, ctxt| { + ctxt.ensure_shell(proxy.get_mut(&env_token)) + }); + + let sink = Arc::new(Mutex::new(EventsLoopSink::new())); + + let store = event_queue.state().insert(WindowStore::new()); + + let seat_idata = SeatIData { + sink: sink.clone(), + keyboard: None, + pointer: None, + windows_token: store.clone() + }; + + let mut me = EventsLoop { + display: Arc::new(display), + evq: RefCell::new(event_queue), sink: sink, pending_wakeup: Arc::new(AtomicBool::new(false)), - cleanup_needed: Arc::new(AtomicBool::new(false)), - hid: hid - } - } + store: store, + ctxt_token: ctxt_token, + env_token: env_token, + cleanup_needed: Arc::new(Mutex::new(false)) + }; - #[inline] - pub fn context(&self) -> &Arc { - &self.ctxt + me.init_seat(|evqh, seat| { + evqh.register(seat, seat_implementation(), seat_idata); + }); + + Some(me) } pub fn create_proxy(&self) -> EventsLoopProxy { EventsLoopProxy { - ctxt: Arc::downgrade(&self.ctxt), + display: Arc::downgrade(&self.display), pending_wakeup: Arc::downgrade(&self.pending_wakeup), } } - // some internals that Window needs access to - pub fn get_window_init(&self) -> Arc { - self.cleanup_needed.clone() - } - - pub fn register_window(&self, decorated_id: usize, surface: Arc) { - self.decorated_ids.lock().unwrap().push((decorated_id, surface.clone())); - let mut guard = self.ctxt.evq.lock().unwrap(); - let mut state = guard.state(); - state.get_mut_handler::(self.hid).windows.push(surface); - } - - fn process_resize(evq: &mut EventQueue, ids: &[(usize, Arc)], sink: &mut EventsLoopSink) - { - let mut state = evq.state(); - for &(decorated_id, ref window) in ids { - let decorated = state.get_mut_handler::>(decorated_id); - if let Some((w, h)) = decorated.handler().as_mut().and_then(|h| h.take_newsize()) { - decorated.resize(w as i32, h as i32); - sink.send_event( - ::WindowEvent::Resized(w,h), - make_wid(&window) - ); - } - if decorated.handler().as_mut().map(|h| h.take_refresh()).unwrap_or(false) { - sink.send_event( - ::WindowEvent::Refresh, - make_wid(&window) - ); - } - if decorated.handler().as_ref().map(|h| h.is_closed()).unwrap_or(false) { - sink.send_event( - ::WindowEvent::Closed, - make_wid(&window) - ); - - } - } - } - - fn prune_dead_windows(&self) { - self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.status() == Liveness::Alive); - let mut evq_guard = self.ctxt.evq.lock().unwrap(); - let mut state = evq_guard.state(); - let handler = state.get_mut_handler::(self.hid); - handler.windows.retain(|w| w.status() == Liveness::Alive); - if let Some(w) = handler.mouse_focus.take() { - if w.status() == Liveness::Alive { - handler.mouse_focus = Some(w) - } - } - } - - fn post_dispatch_triggers(&self) { - let mut evq_guard = self.ctxt.evq.lock().unwrap(); - let mut sink_guard = self.sink.lock().unwrap(); - let ids_guard = self.decorated_ids.lock().unwrap(); - self.emit_pending_wakeup(&mut sink_guard); - Self::process_resize(&mut evq_guard, &ids_guard, &mut sink_guard); - } - pub fn poll_events(&mut self, mut callback: F) where F: FnMut(::Event) { - // send pending requests to the server... - self.ctxt.flush(); + // send pending events to the server + self.display.flush().expect("Wayland connection lost."); // dispatch any pre-buffered events self.sink.lock().unwrap().empty_with(&mut callback); - // try dispatching events without blocking - self.ctxt.read_events(); - self.ctxt.dispatch_pending(); + // try to read pending events + if let Some(h) = self.evq.get_mut().prepare_read() { + h.read_events().expect("Wayland connection lost."); + } + // dispatch wayland events + self.evq.get_mut().dispatch_pending().expect("Wayland connection lost."); self.post_dispatch_triggers(); + // dispatch buffered events to client self.sink.lock().unwrap().empty_with(&mut callback); - - if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) { - self.prune_dead_windows() - } } pub fn run_forever(&mut self, mut callback: F) where F: FnMut(::Event) -> ControlFlow, { - // send pending requests to the server... - self.ctxt.flush(); + // send pending events to the server + self.display.flush().expect("Wayland connection lost."); // Check for control flow by wrapping the callback. let control_flow = ::std::cell::Cell::new(ControlFlow::Continue); @@ -222,432 +197,589 @@ impl EventsLoop { control_flow.set(ControlFlow::Break); }; - // dispatch pre-buffered events + // dispatch any pre-buffered events + self.post_dispatch_triggers(); self.sink.lock().unwrap().empty_with(&mut callback); loop { // dispatch events blocking if needed - self.ctxt.dispatch(); + self.evq.get_mut().dispatch().expect("Wayland connection lost."); self.post_dispatch_triggers(); // empty buffer of events self.sink.lock().unwrap().empty_with(&mut callback); - if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) { - self.prune_dead_windows() - } - if let ControlFlow::Break = control_flow.get() { break; } } } - // If an `EventsLoopProxy` has signalled a wakeup, emit an event and reset the flag. - fn emit_pending_wakeup(&self, sink: &mut EventsLoopSink) { - if self.pending_wakeup.load(atomic::Ordering::Relaxed) { + pub fn get_primary_monitor(&self) -> MonitorId { + let mut guard = self.evq.borrow_mut(); + let state = guard.state(); + let state_ctxt = state.get(&self.ctxt_token); + if let Some(info) = state_ctxt.monitors.iter().next() { + MonitorId { + info: info.clone() + } + } else { + panic!("No monitor is available.") + } + } + + pub fn get_available_monitors(&self) -> VecDeque { + let mut guard = self.evq.borrow_mut(); + let state = guard.state(); + let state_ctxt = state.get(&self.ctxt_token); + state_ctxt.monitors.iter() + .map(|m| MonitorId { info: m.clone() }) + .collect() + } +} + +/* + * Private EventsLoop Internals + */ + +wayland_env!(InnerEnv, + compositor: wl_compositor::WlCompositor, + shm: wl_shm::WlShm, + subcompositor: wl_subcompositor::WlSubcompositor +); + +pub struct StateContext { + registry: wl_registry::WlRegistry, + seat: Option, + shell: Option, + monitors: Vec>> +} + +impl StateContext { + fn new(registry: wl_registry::WlRegistry) -> StateContext { + StateContext { + registry: registry, + seat: None, + shell: None, + monitors: Vec::new() + } + } + + /// Ensures a shell is available + /// + /// If a shell is already bound, do nothing. Otherwise, + /// try to bind wl_shell as a fallback. If this fails, + /// panic, as this is a bug from the compositor. + fn ensure_shell(&mut self, env: &mut EnvHandler) { + if self.shell.is_some() { + return; + } + // xdg_shell is not available, so initialize wl_shell + for &(name, ref interface, _) in env.globals() { + if interface == "wl_shell" { + self.shell = Some(Shell::Wl(self.registry.bind::(1, name))); + return; + } + } + // This is a compositor bug, it _must_ at least support wl_shell + panic!("Compositor didi not advertize xdg_shell not wl_shell."); + } + + pub fn monitor_id_for(&self, output: &wl_output::WlOutput) -> MonitorId { + for info in &self.monitors { + let guard = info.lock().unwrap(); + if guard.output.equals(output) { + return MonitorId { + info: info.clone() + }; + } + } + panic!("Received an inexistent wl_output?!"); + } +} + +impl EventsLoop { + pub fn init_seat(&mut self, f: F) + where F: FnOnce(&mut EventQueueHandle, &wl_seat::WlSeat) + { + let mut guard = self.evq.borrow_mut(); + if guard.state().get(&self.ctxt_token).seat.is_some() { + // seat has already been init + return; + } + + // clone the token to make borrow checker happy + let ctxt_token = self.ctxt_token.clone(); + let seat = guard.state().with_value(&self.env_token, |proxy, env| { + let ctxt = proxy.get(&ctxt_token); + for &(name, ref interface, _) in env.globals() { + if interface == wl_seat::WlSeat::interface_name() { + return Some(ctxt.registry.bind::(5, name)); + } + } + None + }); + + if let Some(seat) = seat { + f(&mut *guard, &seat); + guard.state().get_mut(&self.ctxt_token).seat = Some(seat) + } + } + + fn post_dispatch_triggers(&mut self) { + let mut sink = self.sink.lock().unwrap(); + let evq = self.evq.get_mut(); + // process a possible pending wakeup call + if self.pending_wakeup.load(Ordering::Relaxed) { sink.send_raw_event(::Event::Awakened); - self.pending_wakeup.store(false, atomic::Ordering::Relaxed); + self.pending_wakeup.store(false, Ordering::Relaxed); + } + // prune possible dead windows + { + let mut cleanup_needed = self.cleanup_needed.lock().unwrap(); + if *cleanup_needed { + evq.state().get_mut(&self.store).cleanup(); + *cleanup_needed = false; + } + } + // 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); + } + if refresh { + sink.send_event(::WindowEvent::Refresh, wid); + } + if closed { + sink.send_event(::WindowEvent::Closed, wid); + } + } + ) + } + + /// 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) + where F: FnOnce(&wl_surface::WlSurface) -> ID + { + let (surface, decorated, xdg) = { + 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), + 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( + &mut guard, + implem, + idata(&surface), + &surface, width as i32, height as i32, + &env.compositor, + &env.subcompositor, + &env.shm, + &shell, + seat, + decorated + ).expect("Failed to create a tmpfile buffer."); + (surface, decorated, xdg) + }; + + 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) + } +} + +/* + * Wayland protocol implementations + */ + +fn env_notify() -> EnvNotify> { + EnvNotify { + new_global: |evqh, token, registry, id, interface, version| { + use std::cmp::min; + if interface == wl_output::WlOutput::interface_name() { + // a new output is available + let output = registry.bind::(min(version, 3), id); + evqh.register(&output, output_impl(), token.clone()); + evqh.state().get_mut(&token).monitors.push( + Arc::new(Mutex::new(OutputInfo::new(output, id))) + ); + } else if interface == zxdg_shell_v6::ZxdgShellV6::interface_name() { + // We have an xdg_shell, bind it + let xdg_shell = registry.bind::(1, id); + evqh.register(&xdg_shell, xdg_ping_implementation(), ()); + evqh.state().get_mut(&token).shell = Some(Shell::Xdg(xdg_shell)); + } + }, + del_global: |evqh, token, _, id| { + // maybe this was a monitor, cleanup + evqh.state().get_mut(&token).monitors.retain( + |m| m.lock().unwrap().id != id + ); + }, + ready: |_, _, _| {} + } +} + +fn xdg_ping_implementation() -> zxdg_shell_v6::Implementation<()> { + zxdg_shell_v6::Implementation { + ping: |_, _, shell, serial| { + shell.pong(serial); } } } -enum KbdType { - Mapped(MappedKeyboard), - Plain(Option) +fn free_buffer() -> wl_buffer::Implementation> { + wl_buffer::Implementation { + release: |_, data, buffer| { + buffer.destroy(); + *data = None; + } + } } -struct InputHandler { - my_id: usize, - windows: Vec>, - seat: Option, - mouse: Option, - mouse_focus: Option>, - mouse_location: (f64, f64), +struct SeatIData { + sink: Arc>, + pointer: Option, + keyboard: Option, + windows_token: StateToken +} + +fn seat_implementation() -> wl_seat::Implementation { + wl_seat::Implementation { + name: |_, _, _, _| {}, + capabilities: |evqh, idata, seat, capabilities| { + // create pointer if applicable + if capabilities.contains(wl_seat::Capability::Pointer) && idata.pointer.is_none() { + let pointer = seat.get_pointer().expect("Seat is not dead"); + let p_idata = PointerIData::new(&idata.sink, idata.windows_token.clone()); + evqh.register(&pointer, pointer_implementation(), p_idata); + idata.pointer = Some(pointer); + } + // destroy pointer if applicable + if !capabilities.contains(wl_seat::Capability::Pointer) { + if let Some(pointer) = idata.pointer.take() { + pointer.release(); + } + } + // create keyboard if applicable + if capabilities.contains(wl_seat::Capability::Keyboard) && idata.keyboard.is_none() { + let kbd = seat.get_keyboard().expect("Seat is not dead"); + init_keyboard(evqh, &kbd, &idata.sink); + idata.keyboard = Some(kbd); + } + // destroy keyboard if applicable + if !capabilities.contains(wl_seat::Capability::Keyboard) { + if let Some(kbd) = idata.keyboard.take() { + kbd.release(); + } + } + // TODO: Handle touch + } + } +} + +struct PointerIData { + sink: Arc>, + windows_token: StateToken, + mouse_focus: Option, axis_buffer: Option<(f32, f32)>, axis_discrete_buffer: Option<(i32, i32)>, axis_state: TouchPhase, - kbd: Option, - kbd_handler: KbdType, - callback: Arc> } -impl InputHandler { - fn new(ctxt: &WaylandContext, sink: Arc>) -> InputHandler { - let kbd_handler = match MappedKeyboard::new(KbdHandler::new(sink.clone())) { - Ok(h) => KbdType::Mapped(h), - Err(_) => KbdType::Plain(None) - }; - InputHandler { - my_id: 0, - windows: Vec::new(), - seat: ctxt.get_seat(), - mouse: None, +impl PointerIData { + fn new(sink: &Arc>, token: StateToken) + -> PointerIData + { + PointerIData { + sink: sink.clone(), + windows_token: token, mouse_focus: None, - mouse_location: (0.0,0.0), axis_buffer: None, axis_discrete_buffer: None, - axis_state: TouchPhase::Started, - kbd: None, - kbd_handler: kbd_handler, - callback: sink + axis_state: TouchPhase::Cancelled } } } -impl Init for InputHandler { - fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) { - if let Some(ref seat) = self.seat { - evqh.register::<_, InputHandler>(seat, index); - } - self.my_id = index; - } -} - -impl wl_seat::Handler for InputHandler { - fn capabilities(&mut self, - evqh: &mut EventQueueHandle, - seat: &wl_seat::WlSeat, - capabilities: wl_seat::Capability) - { - // create pointer if applicable - if capabilities.contains(wl_seat::Pointer) && self.mouse.is_none() { - let pointer = seat.get_pointer().expect("Seat is not dead"); - evqh.register::<_, InputHandler>(&pointer, self.my_id); - self.mouse = Some(pointer); - } - // destroy pointer if applicable - if !capabilities.contains(wl_seat::Pointer) { - if let Some(pointer) = self.mouse.take() { - pointer.release(); - } - } - // create keyboard if applicable - if capabilities.contains(wl_seat::Keyboard) && self.kbd.is_none() { - let kbd = seat.get_keyboard().expect("Seat is not dead"); - evqh.register::<_, InputHandler>(&kbd, self.my_id); - self.kbd = Some(kbd); - } - // destroy keyboard if applicable - if !capabilities.contains(wl_seat::Keyboard) { - if let Some(kbd) = self.kbd.take() { - kbd.release(); - } - } - } -} - -declare_handler!(InputHandler, wl_seat::Handler, wl_seat::WlSeat); - -/* - * Pointer Handling - */ - -impl wl_pointer::Handler for InputHandler { - fn enter(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _serial: u32, - surface: &wl_surface::WlSurface, - surface_x: f64, - surface_y: f64) - { - self.mouse_location = (surface_x, surface_y); - for window in &self.windows { - if window.equals(surface) { - self.mouse_focus = Some(window.clone()); - let (w, h) = self.mouse_location; - let mut guard = self.callback.lock().unwrap(); - guard.send_event(Event::MouseEntered { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)) }, - make_wid(window)); - guard.send_event(Event::MouseMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (w, h) }, - make_wid(window)); - break; - } - } - } - - fn leave(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _serial: u32, - surface: &wl_surface::WlSurface) - { - self.mouse_focus = None; - for window in &self.windows { - if window.equals(surface) { - self.callback.lock().unwrap().send_event(Event::MouseLeft { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)) }, - make_wid(window)); - } - } - } - - fn motion(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _time: u32, - surface_x: f64, - surface_y: f64) - { - self.mouse_location = (surface_x, surface_y); - if let Some(ref window) = self.mouse_focus { - let (w,h) = self.mouse_location; - self.callback.lock().unwrap().send_event(Event::MouseMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (w, h) }, make_wid(window)); - } - } - - fn button(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _serial: u32, - _time: u32, - button: u32, - state: wl_pointer::ButtonState) - { - if let Some(ref window) = self.mouse_focus { - let state = match state { - wl_pointer::ButtonState::Pressed => ElementState::Pressed, - wl_pointer::ButtonState::Released => ElementState::Released - }; - let button = match button { - 0x110 => MouseButton::Left, - 0x111 => MouseButton::Right, - 0x112 => MouseButton::Middle, - // TODO figure out the translation ? - _ => return - }; - self.callback.lock().unwrap().send_event( - Event::MouseInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - state: state, - button: button, - }, - make_wid(window) - ); - } - } - - fn axis(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _time: u32, - axis: wl_pointer::Axis, - value: f64) - { - let (mut x, mut y) = self.axis_buffer.unwrap_or((0.0, 0.0)); - match axis { - // wayland vertical sign convention is the inverse of winit - wl_pointer::Axis::VerticalScroll => y -= value as f32, - wl_pointer::Axis::HorizontalScroll => x += value as f32 - } - self.axis_buffer = Some((x,y)); - self.axis_state = match self.axis_state { - TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, - _ => TouchPhase::Started - } - } - - fn frame(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer) - { - let axis_buffer = self.axis_buffer.take(); - let axis_discrete_buffer = self.axis_discrete_buffer.take(); - if let Some(ref window) = self.mouse_focus { - if let Some((x, y)) = axis_discrete_buffer { - self.callback.lock().unwrap().send_event( - Event::MouseWheel { +fn pointer_implementation() -> wl_pointer::Implementation { + wl_pointer::Implementation { + enter: |evqh, idata, _, _, surface, x, y| { + let wid = evqh.state().get(&idata.windows_token).find_wid(surface); + if let Some(wid) = wid { + idata.mouse_focus = Some(wid); + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::MouseEntered { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - delta: MouseScrollDelta::LineDelta(x as f32, y as f32), - phase: self.axis_state, }, - make_wid(window) + wid, ); - } else if let Some((x, y)) = axis_buffer { - self.callback.lock().unwrap().send_event( - Event::MouseWheel { + guard.send_event( + Event::MouseMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), - phase: self.axis_state, + position: (x, y), }, - make_wid(window) + wid, ); } - } - } - - fn axis_source(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _axis_source: wl_pointer::AxisSource) - { - } - - fn axis_stop(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - _time: u32, - _axis: wl_pointer::Axis) - { - self.axis_state = TouchPhase::Ended; - } - - fn axis_discrete(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_pointer::WlPointer, - axis: wl_pointer::Axis, - discrete: i32) - { - let (mut x, mut y) = self.axis_discrete_buffer.unwrap_or((0,0)); - match axis { - // wayland vertical sign convention is the inverse of winit - wl_pointer::Axis::VerticalScroll => y -= discrete, - wl_pointer::Axis::HorizontalScroll => x += discrete - } - self.axis_discrete_buffer = Some((x,y)); - self.axis_state = match self.axis_state { - TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, - _ => TouchPhase::Started - } - } -} - -declare_handler!(InputHandler, wl_pointer::Handler, wl_pointer::WlPointer); - -/* - * Keyboard Handling - */ - -impl wl_keyboard::Handler for InputHandler { - // mostly pass-through - fn keymap(&mut self, - evqh: &mut EventQueueHandle, - proxy: &wl_keyboard::WlKeyboard, - format: wl_keyboard::KeymapFormat, - fd: ::std::os::unix::io::RawFd, - size: u32) - { - match self.kbd_handler { - KbdType::Mapped(ref mut h) => h.keymap(evqh, proxy, format, fd, size), - _ => () - } - } - - fn enter(&mut self, - evqh: &mut EventQueueHandle, - proxy: &wl_keyboard::WlKeyboard, - serial: u32, - surface: &wl_surface::WlSurface, - keys: Vec) - { - for window in &self.windows { - if window.equals(surface) { - self.callback.lock().unwrap().send_event(Event::Focused(true), make_wid(window)); - match self.kbd_handler { - KbdType::Mapped(ref mut h) => { - h.handler().target = Some(make_wid(window)); - h.enter(evqh, proxy, serial, surface, keys); - }, - KbdType::Plain(ref mut target) => { - *target = Some(make_wid(window)) - } - } - break; - } - } - } - - fn leave(&mut self, - evqh: &mut EventQueueHandle, - proxy: &wl_keyboard::WlKeyboard, - serial: u32, - surface: &wl_surface::WlSurface) - { - for window in &self.windows { - if window.equals(surface) { - self.callback.lock().unwrap().send_event(Event::Focused(false), make_wid(window)); - match self.kbd_handler { - KbdType::Mapped(ref mut h) => { - h.handler().target = None; - h.leave(evqh, proxy, serial, surface); - }, - KbdType::Plain(ref mut target) => { - *target = None - } - } - break; - } - } - } - - fn key(&mut self, - evqh: &mut EventQueueHandle, - proxy: &wl_keyboard::WlKeyboard, - serial: u32, - time: u32, - key: u32, - state: wl_keyboard::KeyState) - { - match self.kbd_handler { - KbdType::Mapped(ref mut h) => h.key(evqh, proxy, serial, time, key, state), - KbdType::Plain(Some(wid)) => { - let state = match state { - wl_keyboard::KeyState::Pressed => ElementState::Pressed, - wl_keyboard::KeyState::Released => ElementState::Released, - }; - // This is fallback impl if libxkbcommon was not available - // This case should probably never happen, as most wayland - // compositors _need_ libxkbcommon anyway... - // - // In this case, we don't have the modifiers state information - // anyway, as we need libxkbcommon to interpret it (it is - // supposed to be serialized by the compositor using libxkbcommon) - self.callback.lock().unwrap().send_event( - Event::KeyboardInput { + }, + leave: |evqh, idata, _, _, surface| { + idata.mouse_focus = None; + let wid = evqh.state().get(&idata.windows_token).find_wid(surface); + if let Some(wid) = wid { + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::MouseLeft { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - input: KeyboardInput { - state: state, - scancode: key, - virtual_keycode: None, - modifiers: ModifiersState::default(), - }, + }, + wid, + ); + } + }, + motion: |_, idata, _, _, x, y| { + if let Some(wid) = idata.mouse_focus { + idata.sink.lock().unwrap().send_event( + Event::MouseMoved { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + position: (x, y) }, wid ); - }, - KbdType::Plain(None) => () - } + } + }, + button: |_, idata, _, _, _, button, state| { + if let Some(wid) = idata.mouse_focus { + let state = match state { + wl_pointer::ButtonState::Pressed => ElementState::Pressed, + wl_pointer::ButtonState::Released => ElementState::Released + }; + let button = match button { + 0x110 => MouseButton::Left, + 0x111 => MouseButton::Right, + 0x112 => MouseButton::Middle, + // TODO figure out the translation ? + _ => return + }; + idata.sink.lock().unwrap().send_event( + Event::MouseInput { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + state: state, + button: button, + }, + wid + ); + } + }, + axis: |_, idata, pointer, _, axis, value| { + if let Some(wid) = idata.mouse_focus { + if pointer.version() < 5 { + let (mut x, mut y) = (0.0, 0.0); + // old seat compatibility + match axis { + // wayland vertical sign convention is the inverse of winit + wl_pointer::Axis::VerticalScroll => y -= value as f32, + wl_pointer::Axis::HorizontalScroll => x += value as f32 + } + idata.sink.lock().unwrap().send_event( + Event::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), + phase: TouchPhase::Moved, + }, + wid + ); + } else { + let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0)); + match axis { + // wayland vertical sign convention is the inverse of winit + wl_pointer::Axis::VerticalScroll => y -= value as f32, + wl_pointer::Axis::HorizontalScroll => x += value as f32 + } + idata.axis_buffer = Some((x,y)); + idata.axis_state = match idata.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started + } + } + } + }, + frame: |_, idata, _| { + let axis_buffer = idata.axis_buffer.take(); + let axis_discrete_buffer = idata.axis_discrete_buffer.take(); + if let Some(wid) = idata.mouse_focus { + if let Some((x, y)) = axis_discrete_buffer { + idata.sink.lock().unwrap().send_event( + Event::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::LineDelta(x as f32, y as f32), + phase: idata.axis_state, + }, + wid + ); + } else if let Some((x, y)) = axis_buffer { + idata.sink.lock().unwrap().send_event( + Event::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), + phase: idata.axis_state, + }, + wid + ); + } + } + }, + axis_source: |_, _, _, _| {}, + axis_stop: |_, idata, _, _, _| { + idata.axis_state = TouchPhase::Ended; + }, + axis_discrete: |_, idata, _, axis, discrete| { + let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0)); + match axis { + // wayland vertical sign convention is the inverse of winit + wl_pointer::Axis::VerticalScroll => y -= discrete, + wl_pointer::Axis::HorizontalScroll => x += discrete + } + idata.axis_discrete_buffer = Some((x,y)); + idata.axis_state = match idata.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started + } + }, } +} - fn modifiers(&mut self, - evqh: &mut EventQueueHandle, - proxy: &wl_keyboard::WlKeyboard, - serial: u32, - mods_depressed: u32, - mods_latched: u32, - mods_locked: u32, - group: u32) - { - match self.kbd_handler { - KbdType::Mapped(ref mut h) => h.modifiers(evqh, proxy, serial, mods_depressed, - mods_latched, mods_locked, group), - _ => () - } - } +/* + * Monitor stuff + */ - fn repeat_info(&mut self, - evqh: &mut EventQueueHandle, - proxy: &wl_keyboard::WlKeyboard, - rate: i32, - delay: i32) - { - match self.kbd_handler { - KbdType::Mapped(ref mut h) => h.repeat_info(evqh, proxy, rate, delay), - _ => () +fn output_impl() -> wl_output::Implementation> { + wl_output::Implementation { + geometry: |evqh, token, output, x, y, _, _, _, make, model, _| { + let ctxt = evqh.state().get_mut(token); + for info in &ctxt.monitors { + let mut guard = info.lock().unwrap(); + if guard.output.equals(output) { + guard.pix_pos = (x, y); + guard.name = format!("{} - {}", make, model); + return; + } + } + }, + mode: |evqh, token, output, flags, w, h, _refresh| { + if flags.contains(wl_output::Mode::Current) { + let ctxt = evqh.state().get_mut(token); + for info in &ctxt.monitors { + let mut guard = info.lock().unwrap(); + if guard.output.equals(output) { + guard.pix_size = (w as u32, h as u32); + return; + } + } + } + }, + done: |_, _, _| {}, + scale: |evqh, token, output, scale| { + let ctxt = evqh.state().get_mut(token); + for info in &ctxt.monitors { + let mut guard = info.lock().unwrap(); + if guard.output.equals(output) { + guard.scale = scale as f32; + return; + } + } } } } -declare_handler!(InputHandler, wl_keyboard::Handler, wl_keyboard::WlKeyboard); +pub struct OutputInfo { + pub output: wl_output::WlOutput, + pub id: u32, + pub scale: f32, + pub pix_size: (u32, u32), + pub pix_pos: (i32, i32), + pub name: String +} + +impl OutputInfo { + fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo { + OutputInfo { + output: output, + id: id, + scale: 1.0, + pix_size: (0, 0), + pix_pos: (0, 0), + name: "".into() + } + } +} + +#[derive(Clone)] +pub struct MonitorId { + pub info: Arc> +} + +impl MonitorId { + pub fn get_name(&self) -> Option { + Some(self.info.lock().unwrap().name.clone()) + } + + #[inline] + pub fn get_native_identifier(&self) -> u32 { + self.info.lock().unwrap().id + } + + pub fn get_dimensions(&self) -> (u32, u32) { + self.info.lock().unwrap().pix_size + } + + pub fn get_position(&self) -> (i32, i32) { + self.info.lock().unwrap().pix_pos + } + + #[inline] + pub fn get_hidpi_factor(&self) -> f32 { + self.info.lock().unwrap().scale + } +} diff --git a/src/platform/linux/wayland/keyboard.rs b/src/platform/linux/wayland/keyboard.rs index 237ead66..ce670a42 100644 --- a/src/platform/linux/wayland/keyboard.rs +++ b/src/platform/linux/wayland/keyboard.rs @@ -1,71 +1,131 @@ use std::sync::{Arc, Mutex}; -use {VirtualKeyCode, ElementState, WindowEvent as Event, KeyboardInput}; +use {VirtualKeyCode, ElementState, WindowEvent as Event, KeyboardInput, ModifiersState}; -use events::ModifiersState; - -use super::{wayland_kbd, EventsLoopSink, WindowId, DeviceId}; -use wayland_client::EventQueueHandle; +use super::{EventsLoopSink, WindowId, make_wid, DeviceId}; +use super::wayland_kbd::{MappedKeyboardImplementation, register_kbd}; use wayland_client::protocol::wl_keyboard; +use wayland_client::EventQueueHandle; -pub struct KbdHandler { - sink: Arc>, - pub target: Option -} +pub fn init_keyboard(evq: &mut EventQueueHandle, keyboard: &wl_keyboard::WlKeyboard, sink: &Arc>) { + let idata = KeyboardIData { + sink: sink.clone(), + target: None + }; -impl KbdHandler { - pub fn new(sink: Arc>) -> KbdHandler { - KbdHandler { sink: sink, target: None } + if register_kbd(evq, keyboard, mapped_keyboard_impl(), idata).is_err() { + // initializing libxkbcommon failed :( + // fallback implementation + let idata = KeyboardIData { + sink: sink.clone(), + target: None + }; + evq.register(keyboard, raw_keyboard_impl(), idata); } } -impl wayland_kbd::Handler for KbdHandler { - fn key(&mut self, - _evqh: &mut EventQueueHandle, - _proxy: &wl_keyboard::WlKeyboard, - _serial: u32, - _time: u32, - mods: &wayland_kbd::ModifiersState, - rawkey: u32, - keysym: u32, - state: wl_keyboard::KeyState, - utf8: Option) - { - if let Some(wid) = self.target { - let state = match state { - wl_keyboard::KeyState::Pressed => ElementState::Pressed, - wl_keyboard::KeyState::Released => ElementState::Released, - }; - let vkcode = key_to_vkey(rawkey, keysym); - let mut guard = self.sink.lock().unwrap(); - guard.send_event( - Event::KeyboardInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - input: KeyboardInput { - state: state, - scancode: rawkey, - virtual_keycode: vkcode, - modifiers: ModifiersState { - shift: mods.shift, - ctrl: mods.ctrl, - alt: mods.alt, - logo: mods.logo +struct KeyboardIData { + sink: Arc>, + target: Option +} + +fn mapped_keyboard_impl() -> MappedKeyboardImplementation { + MappedKeyboardImplementation { + enter: |_, idata, _, _, surface, _, _, _| { + let wid = make_wid(surface); + idata.sink.lock().unwrap().send_event(Event::Focused(true), wid); + idata.target = Some(wid); + }, + leave: |_, idata, _, _, surface| { + let wid = make_wid(surface); + idata.sink.lock().unwrap().send_event(Event::Focused(false), wid); + idata.target = None; + }, + key: |_, idata, _, _, _, mods, rawkey, keysym, state, utf8| { + if let Some(wid) = idata.target { + let state = match state { + wl_keyboard::KeyState::Pressed => ElementState::Pressed, + wl_keyboard::KeyState::Released => ElementState::Released, + }; + let vkcode = key_to_vkey(rawkey, keysym); + let mut guard = idata.sink.lock().unwrap(); + guard.send_event( + Event::KeyboardInput { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + input: KeyboardInput { + state: state, + scancode: rawkey, + virtual_keycode: vkcode, + modifiers: ModifiersState { + shift: mods.shift, + ctrl: mods.ctrl, + alt: mods.alt, + logo: mods.logo + }, }, }, - }, - wid - ); - // send char event only on key press, not release - if let ElementState::Released = state { return } - if let Some(txt) = utf8 { - for chr in txt.chars() { - guard.send_event(Event::ReceivedCharacter(chr), wid); + wid + ); + // send char event only on key press, not release + if let ElementState::Released = state { return } + if let Some(txt) = utf8 { + for chr in txt.chars() { + guard.send_event(Event::ReceivedCharacter(chr), wid); + } } } + }, + repeat_info: |_, _idata, _, _rate, _delay| { + // TODO: handle repeat info } } } + +// This is fallback impl if libxkbcommon was not available +// This case should probably never happen, as most wayland +// compositors _need_ libxkbcommon anyway... +// +// In this case, we don't have the keymap information (it is +// supposed to be serialized by the compositor using libxkbcommon) +fn raw_keyboard_impl() -> wl_keyboard::Implementation { + wl_keyboard::Implementation { + enter: |_, idata, _, _, surface, _| { + let wid = make_wid(surface); + idata.sink.lock().unwrap().send_event(Event::Focused(true), wid); + idata.target = Some(wid); + }, + leave: |_, idata, _, _, surface| { + let wid = make_wid(surface); + idata.sink.lock().unwrap().send_event(Event::Focused(false), wid); + idata.target = None; + }, + key: |_, idata, _, _, _, key, state| { + if let Some(wid) = idata.target { + let state = match state { + wl_keyboard::KeyState::Pressed => ElementState::Pressed, + wl_keyboard::KeyState::Released => ElementState::Released, + }; + idata.sink.lock().unwrap().send_event( + Event::KeyboardInput { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + input: KeyboardInput { + state: state, + scancode: key, + virtual_keycode: None, + modifiers: ModifiersState::default(), + }, + }, + wid + ); + } + }, + repeat_info: |_, _idata, _, _rate, _delay| {}, + keymap: |_, _, _, _, _, _| {}, + modifiers: |_, _, _, _, _, _, _, _| {} + } +} + fn key_to_vkey(rawkey: u32, keysym: u32) -> Option { match rawkey { 1 => Some(VirtualKeyCode::Escape), diff --git a/src/platform/linux/wayland/mod.rs b/src/platform/linux/wayland/mod.rs index c789379b..1cf6964d 100644 --- a/src/platform/linux/wayland/mod.rs +++ b/src/platform/linux/wayland/mod.rs @@ -1,22 +1,27 @@ #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] -pub use self::window::{Window, WindowId}; -pub use self::event_loop::{EventsLoop, EventsLoopProxy}; -pub use self::context::{WaylandContext, MonitorId, get_available_monitors, - get_primary_monitor}; - -use self::window::{make_wid, DecoratedHandler}; -use self::event_loop::EventsLoopSink; +pub use self::window::Window; +pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId}; extern crate wayland_kbd; extern crate wayland_window; extern crate wayland_protocols; extern crate tempfile; -mod context; +use wayland_client::protocol::wl_surface; +use wayland_client::Proxy; + mod event_loop; mod keyboard; mod window; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WindowId(usize); + +#[inline] +fn make_wid(s: &wl_surface::WlSurface) -> WindowId { + WindowId(s.ptr() as usize) +} \ No newline at end of file diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index 0e633565..fb73a3e7 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -1,97 +1,86 @@ -use std::sync::{Arc, Mutex}; -use std::sync::atomic::{Ordering, AtomicBool}; -use std::cmp; +use std::sync::{Arc, Mutex, Weak}; -use wayland_client::{EventQueueHandle, Proxy}; use wayland_client::protocol::{wl_display,wl_surface}; +use wayland_client::{Proxy, StateToken}; use {CreationError, MouseCursor, CursorState, WindowAttributes}; use platform::MonitorId as PlatformMonitorId; use window::MonitorId as RootMonitorId; -use platform::wayland::MonitorId as WaylandMonitorId; -use platform::wayland::context::get_available_monitors; -use super::{WaylandContext, EventsLoop}; -use super::wayland_window; -use super::wayland_window::DecoratedSurface; +use super::{EventsLoop, WindowId, make_wid, MonitorId}; +use super::wayland_window::{DecoratedSurface, DecoratedSurfaceImplementation}; +use super::event_loop::StateContext; pub struct Window { - // the global wayland context - ctxt: Arc, - // signal to advertize the EventsLoop when we are destroyed - cleanup_signal: Arc, - // our wayland surface - surface: Arc, - // our current inner dimensions - size: Mutex<(u32, u32)>, - // the id of our DecoratedHandler in the EventQueue - decorated_id: usize -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(usize); - -#[inline] -pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId { - WindowId(s.ptr() as usize) + display: Arc, + surface: wl_surface::WlSurface, + decorated: Arc>, + monitors: Arc>, + ready: Arc>, + size: Arc>, + kill_switch: (Arc>, Arc>), } impl Window { pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result { - let ctxt = evlp.context().clone(); let (width, height) = attributes.dimensions.unwrap_or((800,600)); - let (surface, decorated, xdg) = ctxt.create_window::(width, height, attributes.decorations); - - // init DecoratedSurface - let cleanup_signal = evlp.get_window_init(); - - let mut fullscreen_monitor = None; + // 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(), + 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 { - ctxt.with_output(monitor_id.clone(), |output| { - fullscreen_monitor = output.clone(); + let info = monitor_id.info.lock().unwrap(); + decorated.set_fullscreen(Some(&info.output)); + } + // setup the monitor tracking + let monitor_list = Arc::new(Mutex::new(MonitorList::default())); + { + let mut evq = evlp.evq.borrow_mut(); + 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 mut evq = evlp.evq.borrow_mut(); + evq.state().get_mut(&store_token).windows.push(InternalWindow { + closed: false, + newsize: None, + need_refresh: false, + surface: surface.clone().unwrap(), + kill_switch: kill_switch.clone(), + decorated: Arc::downgrade(&decorated) }); + evq.sync_roundtrip().unwrap(); } - let decorated_id = { - let mut evq_guard = ctxt.evq.lock().unwrap(); - // store the DecoratedSurface handler - let decorated_id = evq_guard.add_handler_with_init(decorated); - { - let mut state = evq_guard.state(); - // initialize the DecoratedHandler - let decorated = state.get_mut_handler::>(decorated_id); - *(decorated.handler()) = Some(DecoratedHandler::new(!xdg)); - - // set fullscreen if necessary - if let Some(output) = fullscreen_monitor { - decorated.set_fullscreen(Some(&output)); - } - // Finally, set the decorations size - decorated.resize(width as i32, height as i32); - } - - evq_guard.sync_roundtrip().unwrap(); - - decorated_id - }; - // send our configuration to the compositor - // if we're in xdg mode, no buffer is attached yet, so this - // is fine (and more or less required actually) - surface.commit(); - let me = Window { - ctxt: ctxt, - cleanup_signal: cleanup_signal, + Ok(Window { + display: evlp.display.clone(), surface: surface, - size: Mutex::new((width, height)), - decorated_id: decorated_id - }; - - // register ourselves to the EventsLoop - evlp.register_window(me.decorated_id, me.surface.clone()); - - Ok(me) + decorated: decorated, + monitors: monitor_list, + ready: ready, + size: size, + kill_switch: (kill_switch, evlp.cleanup_needed.clone()) + }) } #[inline] @@ -100,10 +89,7 @@ impl Window { } pub fn set_title(&self, title: &str) { - let mut guard = self.ctxt.evq.lock().unwrap(); - let mut state = guard.state(); - let decorated = state.get_mut_handler::>(self.decorated_id); - decorated.set_title(title.into()) + self.decorated.lock().unwrap().set_title(title.into()); } #[inline] @@ -141,10 +127,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) { - let mut guard = self.ctxt.evq.lock().unwrap(); - let mut state = guard.state(); - let mut decorated = state.get_mut_handler::>(self.decorated_id); - decorated.resize(x as i32, y as i32); + self.decorated.lock().unwrap().resize(x as i32, y as i32); *(self.size.lock().unwrap()) = (x, y); } @@ -166,8 +149,13 @@ impl Window { #[inline] pub fn hidpi_factor(&self) -> f32 { - // TODO - 1.0 + let mut factor = 1.0; + let guard = self.monitors.lock().unwrap(); + for monitor_id in &guard.monitors { + let info = monitor_id.info.lock().unwrap(); + if info.scale > factor { factor = info.scale; } + } + factor } #[inline] @@ -177,109 +165,148 @@ impl Window { } pub fn get_display(&self) -> &wl_display::WlDisplay { - &self.ctxt.display + &*self.display } pub fn get_surface(&self) -> &wl_surface::WlSurface { &self.surface } - pub fn get_current_monitor(&self) -> WaylandMonitorId { - let monitors = get_available_monitors(&self.ctxt); - let default = monitors[0].clone(); - - let (wx,wy) = match self.get_position() { - Some(val) => (cmp::max(0,val.0) as u32, cmp::max(0,val.1) as u32), - None=> return default, - }; - let (ww,wh) = match self.get_outer_size() { - Some(val) => val, - None=> return default, - }; - // Opposite corner coordinates - let (wxo, wyo) = (wx+ww-1, wy+wh-1); - - // Find the monitor with the biggest overlap with the window - let mut overlap = 0; - let mut find = default; - for monitor in monitors { - let (mx, my) = monitor.get_position(); - let mx = mx as u32; - let my = my as u32; - let (mw, mh) = monitor.get_dimensions(); - let (mxo, myo) = (mx+mw-1, my+mh-1); - let (ox, oy) = (cmp::max(wx, mx), cmp::max(wy, my)); - let (oxo, oyo) = (cmp::min(wxo, mxo), cmp::min(wyo, myo)); - let osize = if ox <= oxo || oy <= oyo { 0 } else { (oxo-ox)*(oyo-oy) }; - - if osize > overlap { - overlap = osize; - find = monitor; - } - } - - find + pub fn get_current_monitor(&self) -> MonitorId { + // we don't know how much each monitor sees us so... + // just return the most recent one ? + let guard = self.monitors.lock().unwrap(); + guard.monitors.last().unwrap().clone() } pub fn is_ready(&self) -> bool { - let mut guard = self.ctxt.evq.lock().unwrap(); - let mut state = guard.state(); - let mut decorated = state.get_mut_handler::>(self.decorated_id); - decorated.handler().as_ref().unwrap().configured + *self.ready.lock().unwrap() } } impl Drop for Window { fn drop(&mut self) { - self.surface.destroy(); - self.cleanup_signal.store(true, Ordering::Relaxed); + *(self.kill_switch.0.lock().unwrap()) = true; + *(self.kill_switch.1.lock().unwrap()) = true; } } -pub struct DecoratedHandler { - newsize: Option<(u32, u32)>, - refresh: bool, +/* + * Internal store for windows + */ + +struct InternalWindow { + surface: wl_surface::WlSurface, + newsize: Option<(i32, i32)>, + need_refresh: bool, closed: bool, - configured: bool + kill_switch: Arc>, + decorated: Weak> } -impl DecoratedHandler { - fn new(configured: bool) -> DecoratedHandler { - DecoratedHandler { - newsize: None, - refresh: false, - closed: false, - configured: configured +pub struct WindowStore { + windows: Vec +} + +impl WindowStore { + pub fn new() -> WindowStore { + WindowStore { windows: Vec::new() } + } + + pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option { + for window in &self.windows { + if surface.equals(&window.surface) { + return Some(make_wid(surface)); + } + } + None + } + + pub fn cleanup(&mut self) { + self.windows.retain(|w| { + if *w.kill_switch.lock().unwrap() { + // window is dead, cleanup + w.surface.destroy(); + false + } else { + true + } + }); + } + + pub fn for_each(&mut self, mut f: F) + where F: FnMut(Option<(i32, i32)>, bool, bool, WindowId, Option<&mut DecoratedSurface>) + { + for window in &mut self.windows { + let opt_arc = window.decorated.upgrade(); + let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap()); + f( + window.newsize.take(), + window.need_refresh, + window.closed, + make_wid(&window.surface), + opt_mutex_lock.as_mut().map(|m| &mut **m) + ); + window.need_refresh = false; + // avoid re-spamming the event + window.closed = false; } } - - pub fn take_newsize(&mut self) -> Option<(u32, u32)> { - self.newsize.take() - } - - pub fn take_refresh(&mut self) -> bool { - let refresh = self.refresh; - self.refresh = false; - refresh - } - - pub fn is_closed(&self) -> bool { self.closed } } -impl wayland_window::Handler for DecoratedHandler { - fn configure(&mut self, - _: &mut EventQueueHandle, - _cfg: wayland_window::Configure, - newsize: Option<(i32, i32)>) - { - self.newsize = newsize.map(|(w, h)| (w as u32, h as u32)); - self.refresh = true; - self.configured = true; - } +/* + * Protocol implementation + */ - fn close(&mut self, _: &mut EventQueueHandle) { - self.closed = true; +struct DecoratedIData { + ready: Arc>, + store_token: StateToken, + surface: wl_surface::WlSurface +} + +fn decorated_impl() -> DecoratedSurfaceImplementation { + DecoratedSurfaceImplementation { + 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; + return; + } + } + }, + close: |evqh, idata| { + let store = evqh.state().get_mut(&idata.store_token); + for window in &mut store.windows { + if window.surface.equals(&idata.surface) { + window.closed = true; + return; + } + } + } } } +#[derive(Default)] +struct MonitorList { + monitors: Vec +} +fn surface_impl() -> wl_surface::Implementation<(StateToken, Arc>)> { + wl_surface::Implementation { + enter: |evqh, &mut (ref token, ref list), _, output| { + let mut guard = list.lock().unwrap(); + let ctxt = evqh.state().get(token); + let monitor = ctxt.monitor_id_for(output); + guard.monitors.push(monitor); + }, + leave: |evqh, &mut (ref token, ref list), _, output| { + let mut guard = list.lock().unwrap(); + let ctxt = evqh.state().get(token); + let monitor = ctxt.monitor_id_for(output); + guard.monitors.retain(|m| !Arc::ptr_eq(&m.info, &monitor.info)); + } + } +} \ No newline at end of file