diff --git a/CHANGELOG.md b/CHANGELOG.md index 66fdd75b..1c69be92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Fixed memory leak on X11 every time the mouse entered the window. - On X11, drag and drop now works reliably in release mode. - Added `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` to X11, allowing for more optional hints to be set. +- Rework of the wayland backend, migrating it to use [Smithay's Client Toolkit](https://github.com/Smithay/client-toolkit). # Version 0.13.1 (2018-04-26) diff --git a/Cargo.toml b/Cargo.toml index 7328a21e..7df51f3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,10 +44,8 @@ features = [ ] [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] -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.13.0" +wayland-client = { version = "0.20.2", features = [ "dlopen", "egl", "cursor"] } +smithay-client-toolkit = "0.2.1" x11-dl = "2.17.5" parking_lot = "0.5" percent-encoding = "1.0" diff --git a/src/lib.rs b/src/lib.rs index fd1de99a..cfa30e5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,8 +105,7 @@ extern crate parking_lot; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] extern crate percent_encoding; #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] -#[macro_use] -extern crate wayland_client; +extern crate smithay_client_toolkit as sctk; pub use events::*; pub use window::{AvailableMonitorsIter, MonitorId}; diff --git a/src/os/unix.rs b/src/os/unix.rs index 1d8814cb..2013c654 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -173,18 +173,16 @@ impl WindowExt for Window { #[inline] fn get_wayland_surface(&self) -> Option<*mut raw::c_void> { - use wayland_client::Proxy; match self.window { - LinuxWindow::Wayland(ref w) => Some(w.get_surface().ptr() as *mut _), + LinuxWindow::Wayland(ref w) => Some(w.get_surface().c_ptr() as *mut _), _ => None } } #[inline] fn get_wayland_display(&self) -> Option<*mut raw::c_void> { - use wayland_client::Proxy; match self.window { - LinuxWindow::Wayland(ref w) => Some(w.get_display().ptr() as *mut _), + LinuxWindow::Wayland(ref w) => Some(w.get_display().c_ptr() as *mut _), _ => None } } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 24e9e887..88ef90ef 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -255,19 +255,17 @@ impl Window { #[inline] pub fn platform_display(&self) -> *mut libc::c_void { - use wayland_client::Proxy; match self { &Window::X(ref w) => w.platform_display(), - &Window::Wayland(ref w) => w.get_display().ptr() as *mut _ + &Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _ } } #[inline] pub fn platform_window(&self) -> *mut libc::c_void { - use wayland_client::Proxy; match self { &Window::X(ref w) => w.platform_window(), - &Window::Wayland(ref w) => w.get_surface().ptr() as *mut _ + &Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _ } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index a96f8a91..2c02d18d 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -3,37 +3,35 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; -use {EventsLoopClosed, ControlFlow}; +use {ControlFlow, EventsLoopClosed}; use super::WindowId; use super::window::WindowStore; -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_pointer, wl_keyboard, wl_touch}; +use sctk::Environment; +use sctk::output::OutputMgr; +use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy}; +use sctk::reexports::client::commons::Implementation; +use sctk::reexports::client::protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, + wl_touch}; -use super::wayland_window::{Frame, Shell, create_frame, FrameImplementation}; -use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6; +use sctk::reexports::client::protocol::wl_display::RequestsTrait as DisplayRequests; pub struct EventsLoopSink { - buffer: VecDeque<::Event> + buffer: VecDeque<::Event>, } -unsafe impl Send for EventsLoopSink { } - impl EventsLoopSink { - pub fn new() -> EventsLoopSink{ + pub fn new() -> EventsLoopSink { EventsLoopSink { - buffer: VecDeque::new() + buffer: VecDeque::new(), } } pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) { let evt = ::Event::WindowEvent { event: evt, - window_id: ::WindowId(::platform::WindowId::Wayland(wid)) + window_id: ::WindowId(::platform::WindowId::Wayland(wid)), }; self.buffer.push_back(evt); } @@ -42,7 +40,10 @@ impl EventsLoopSink { self.buffer.push_back(evt); } - fn empty_with(&mut self, callback: &mut F) where F: FnMut(::Event) { + fn empty_with(&mut self, callback: &mut F) + where + F: FnMut(::Event), + { for evt in self.buffer.drain(..) { callback(evt) } @@ -57,15 +58,15 @@ pub struct EventsLoop { // Whether or not there is a pending `Awakened` event to be emitted. pending_wakeup: Arc, // The window store - pub store: StateToken, + pub store: Arc>, // the env - env_token: StateToken>, - // the ctxt - pub ctxt_token: StateToken, + pub env: Environment, // a cleanup switch to prune dead windows pub cleanup_needed: Arc>, // The wayland display - pub display: Arc, + pub display: Arc, + // The list of seats + pub seats: Arc)>>>, } // A handle that can be sent across threads and used to wake up the `EventsLoop`. @@ -73,7 +74,7 @@ pub struct EventsLoop { // We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs. #[derive(Clone)] pub struct EventsLoopProxy { - display: Weak, + display: Weak, pending_wakeup: Weak, } @@ -89,10 +90,10 @@ impl EventsLoopProxy { // Update the `EventsLoop`'s `pending_wakeup` flag. wakeup.store(true, Ordering::Relaxed); // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. - display.sync(); + let _ = display.sync(); display.flush().map_err(|_| EventsLoopClosed)?; Ok(()) - }, + } _ => Err(EventsLoopClosed), } } @@ -100,58 +101,35 @@ impl EventsLoopProxy { impl EventsLoop { pub fn new() -> Option { - let (display, mut event_queue) = match default_connect() { + let (display, mut event_queue) = match Display::connect_to_env() { Ok(ret) => ret, - Err(_) => return None + Err(_) => return None, }; - 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 = Arc::new(Mutex::new(WindowStore::new())); + let seats = Arc::new(Mutex::new(Vec::new())); - let store = event_queue.state().insert(WindowStore::new()); + let env = Environment::from_registry_with_cb( + display.get_registry().unwrap(), + &mut event_queue, + SeatManager { + sink: sink.clone(), + store: store.clone(), + seats: seats.clone(), + }, + ).unwrap(); - let seat_idata = SeatIData { - sink: sink.clone(), - keyboard: None, - pointer: None, - touch: None, - windows_token: store.clone() - }; - - let mut me = EventsLoop { + Some(EventsLoop { display: Arc::new(display), evq: RefCell::new(event_queue), sink: sink, pending_wakeup: Arc::new(AtomicBool::new(false)), store: store, - ctxt_token: ctxt_token, - env_token: env_token, - cleanup_needed: Arc::new(Mutex::new(false)) - }; - - me.init_seat(|evqh, seat| { - evqh.register(seat, seat_implementation(), seat_idata); - }); - - Some(me) + env: env, + cleanup_needed: Arc::new(Mutex::new(false)), + seats: seats, + }) } pub fn create_proxy(&self) -> EventsLoopProxy { @@ -162,7 +140,8 @@ impl EventsLoop { } pub fn poll_events(&mut self, mut callback: F) - where F: FnMut(::Event) + where + F: FnMut(::Event), { // send pending events to the server self.display.flush().expect("Wayland connection lost."); @@ -175,7 +154,10 @@ impl EventsLoop { h.read_events().expect("Wayland connection lost."); } // dispatch wayland events - self.evq.get_mut().dispatch_pending().expect("Wayland connection lost."); + self.evq + .get_mut() + .dispatch_pending() + .expect("Wayland connection lost."); self.post_dispatch_triggers(); // dispatch buffered events to client @@ -183,15 +165,18 @@ impl EventsLoop { } pub fn run_forever(&mut self, mut callback: F) - where F: FnMut(::Event) -> ControlFlow, + where + F: FnMut(::Event) -> ControlFlow, { // 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); - let mut callback = |event| if let ControlFlow::Break = callback(event) { - control_flow.set(ControlFlow::Break); + let mut callback = |event| { + if let ControlFlow::Break = callback(event) { + control_flow.set(ControlFlow::Break); + } }; // dispatch any pre-buffered events @@ -200,7 +185,10 @@ impl EventsLoop { loop { // dispatch events blocking if needed - self.evq.get_mut().dispatch().expect("Wayland connection lost."); + self.evq + .get_mut() + .dispatch() + .expect("Wayland connection lost."); self.post_dispatch_triggers(); // empty buffer of events @@ -213,25 +201,27 @@ impl EventsLoop { } 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() + self.env.outputs.with_all(|list| { + if let Some(&(_, ref proxy, _)) = list.first() { + MonitorId { + proxy: proxy.clone(), + mgr: self.env.outputs.clone(), + } + } else { + panic!("No monitor is available.") } - } 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() + self.env.outputs.with_all(|list| { + list.iter() + .map(|&(_, ref proxy, _)| MonitorId { + proxy: proxy.clone(), + mgr: self.env.outputs.clone(), + }) + .collect() + }) } } @@ -239,93 +229,9 @@ impl EventsLoop { * 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); @@ -335,7 +241,7 @@ impl EventsLoop { { let mut cleanup_needed = self.cleanup_needed.lock().unwrap(); if *cleanup_needed { - let pruned = evq.state().get_mut(&self.store).cleanup(); + let pruned = self.store.lock().unwrap().cleanup(); *cleanup_needed = false; for wid in pruned { sink.send_event(::WindowEvent::Destroyed, wid); @@ -343,11 +249,11 @@ impl EventsLoop { } } // process pending resize/refresh - evq.state().get_mut(&self.store).for_each( + self.store.lock().unwrap().for_each( |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.resize(w as u32, h as u32); frame.refresh(); sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid); } else if frame_refresh { @@ -360,140 +266,147 @@ impl EventsLoop { if closed { sink.send_event(::WindowEvent::CloseRequested, wid); } - } + }, ) } - - /// 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, implem: FrameImplementation, idata: F) - -> (wl_surface::WlSurface, Frame) - where F: FnOnce(&wl_surface::WlSurface) -> ID - { - let (surface, frame) = { - let mut guard = self.evq.borrow_mut(); - let env = guard.state().get(&self.env_token).clone_inner().unwrap(); - 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 frame = create_frame( - &mut guard, - implem, - idata(&surface), - &surface, width as i32, height as i32, - &env.compositor, - &env.subcompositor, - &env.shm, - &shell, - seat - ).expect("Failed to create a tmpfile buffer."); - (surface, frame) - }; - - (surface, frame) - } } /* * 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: |_, _, _| {} - } +struct SeatManager { + sink: Arc>, + store: Arc>, + seats: Arc)>>>, } -fn xdg_ping_implementation() -> zxdg_shell_v6::Implementation<()> { - zxdg_shell_v6::Implementation { - ping: |_, _, shell, serial| { - shell.pong(serial); +impl Implementation, GlobalEvent> for SeatManager { + fn receive(&mut self, evt: GlobalEvent, registry: Proxy) { + use self::wl_registry::RequestsTrait as RegistryRequests; + use self::wl_seat::RequestsTrait as SeatRequests; + match evt { + GlobalEvent::New { + id, + ref interface, + version, + } if interface == "wl_seat" => + { + use std::cmp::min; + let seat = registry + .bind::(min(version, 5), id) + .unwrap() + .implement(SeatData { + sink: self.sink.clone(), + store: self.store.clone(), + pointer: None, + keyboard: None, + touch: None, + }); + self.store.lock().unwrap().new_seat(&seat); + self.seats.lock().unwrap().push((id, seat)); + } + GlobalEvent::Removed { id, ref interface } if interface == "wl_seat" => { + let mut seats = self.seats.lock().unwrap(); + if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { + let (_, seat) = seats.swap_remove(idx); + if seat.version() >= 5 { + seat.release(); + } + } + } + _ => (), } } } -struct SeatIData { +struct SeatData { sink: Arc>, - pointer: Option, - keyboard: Option, - touch: Option, - windows_token: StateToken + store: Arc>, + pointer: Option>, + keyboard: Option>, + touch: Option>, } -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 = super::pointer::PointerIData::new( - &idata.sink, - idata.windows_token.clone() - ); - evqh.register(&pointer, super::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(); +impl Implementation, wl_seat::Event> for SeatData { + fn receive(&mut self, evt: wl_seat::Event, seat: Proxy) { + use self::wl_seat::RequestsTrait as SeatRequests; + match evt { + wl_seat::Event::Name { .. } => (), + wl_seat::Event::Capabilities { capabilities } => { + // create pointer if applicable + if capabilities.contains(wl_seat::Capability::Pointer) && self.pointer.is_none() { + self.pointer = Some(super::pointer::implement_pointer( + seat.get_pointer().unwrap(), + self.sink.clone(), + self.store.clone(), + )) + } + // destroy pointer if applicable + if !capabilities.contains(wl_seat::Capability::Pointer) { + if let Some(pointer) = self.pointer.take() { + if pointer.version() >= 3 { + use self::wl_pointer::RequestsTrait; + pointer.release(); + } + } + } + // create keyboard if applicable + if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() { + self.keyboard = Some(super::keyboard::init_keyboard( + seat.get_keyboard().unwrap(), + self.sink.clone(), + )) + } + // destroy keyboard if applicable + if !capabilities.contains(wl_seat::Capability::Keyboard) { + if let Some(kbd) = self.keyboard.take() { + if kbd.version() >= 3 { + use self::wl_keyboard::RequestsTrait; + kbd.release(); + } + } + } + // create touch if applicable + if capabilities.contains(wl_seat::Capability::Touch) && self.touch.is_none() { + self.touch = Some(super::touch::implement_touch( + seat.get_touch().unwrap(), + self.sink.clone(), + self.store.clone(), + )) + } + // destroy touch if applicable + if !capabilities.contains(wl_seat::Capability::Touch) { + if let Some(touch) = self.touch.take() { + if touch.version() >= 3 { + use self::wl_touch::RequestsTrait; + touch.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); + } + } +} + +impl Drop for SeatData { + fn drop(&mut self) { + if let Some(pointer) = self.pointer.take() { + if pointer.version() >= 3 { + use self::wl_pointer::RequestsTrait; + pointer.release(); } - // destroy keyboard if applicable - if !capabilities.contains(wl_seat::Capability::Keyboard) { - if let Some(kbd) = idata.keyboard.take() { - kbd.release(); - } + } + if let Some(kbd) = self.keyboard.take() { + if kbd.version() >= 3 { + use self::wl_keyboard::RequestsTrait; + kbd.release(); } - // create touch if applicable - if capabilities.contains(wl_seat::Capability::Touch) && idata.touch.is_none() { - let touch = seat.get_touch().expect("Seat is not dead"); - let t_idata = super::touch::TouchIData::new( - &idata.sink, - idata.windows_token.clone() - ); - evqh.register(&touch, super::touch::touch_implementation(), t_idata); - idata.touch = Some(touch); - } - // destroy touch if applicable - if !capabilities.contains(wl_seat::Capability::Touch) { - if let Some(touch) = idata.touch.take() { - touch.release(); - } + } + if let Some(touch) = self.touch.take() { + if touch.version() >= 3 { + use self::wl_touch::RequestsTrait; + touch.release(); } } } @@ -503,92 +416,54 @@ fn seat_implementation() -> wl_seat::Implementation { * Monitor stuff */ -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; - } - } - } - } -} - -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> + pub(crate) proxy: Proxy, + pub(crate) mgr: OutputMgr, +} + +impl Clone for MonitorId { + fn clone(&self) -> MonitorId { + MonitorId { + proxy: self.proxy.clone(), + mgr: self.mgr.clone(), + } + } } impl MonitorId { pub fn get_name(&self) -> Option { - Some(self.info.lock().unwrap().name.clone()) + self.mgr.with_info(&self.proxy, |_, info| { + format!("{} ({})", info.model, info.make) + }) } #[inline] pub fn get_native_identifier(&self) -> u32 { - self.info.lock().unwrap().id + self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0) } pub fn get_dimensions(&self) -> (u32, u32) { - self.info.lock().unwrap().pix_size + match self.mgr.with_info(&self.proxy, |_, info| { + info.modes + .iter() + .find(|m| m.is_current) + .map(|m| m.dimensions) + }) { + Some(Some((w, h))) => (w as u32, h as u32), + _ => (0, 0), + } } pub fn get_position(&self) -> (i32, i32) { - self.info.lock().unwrap().pix_pos + self.mgr + .with_info(&self.proxy, |_, info| info.location) + .unwrap_or((0, 0)) } #[inline] pub fn get_hidpi_factor(&self) -> f32 { - self.info.lock().unwrap().scale + self.mgr + .with_info(&self.proxy, |_, info| info.scale_factor as f32) + .unwrap_or(1.0) } } diff --git a/src/platform/linux/wayland/keyboard.rs b/src/platform/linux/wayland/keyboard.rs index 45fc3802..75a5b1c4 100644 --- a/src/platform/linux/wayland/keyboard.rs +++ b/src/platform/linux/wayland/keyboard.rs @@ -1,150 +1,154 @@ use std::sync::{Arc, Mutex}; -use {VirtualKeyCode, ElementState, WindowEvent as Event, KeyboardInput, ModifiersState}; +use {ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; -use super::{EventsLoopSink, WindowId, make_wid, DeviceId}; -use super::wayland_kbd::{MappedKeyboardImplementation, register_kbd}; -use wayland_client::protocol::wl_keyboard; -use wayland_client::EventQueueHandle; +use super::{make_wid, DeviceId, EventsLoopSink}; +use sctk::keyboard::{self, map_keyboard_auto, Event as KbEvent}; +use sctk::reexports::client::{NewProxy, Proxy}; +use sctk::reexports::client::protocol::wl_keyboard; -pub fn init_keyboard(evq: &mut EventQueueHandle, keyboard: &wl_keyboard::WlKeyboard, sink: &Arc>) { - let idata = KeyboardIData { - sink: sink.clone(), - 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); - } -} - -struct KeyboardIData { +pub fn init_keyboard( + keyboard: NewProxy, 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 { +) -> Proxy { + // { variables to be captured by the closure + let mut target = None; + let my_sink = sink.clone(); + // } + let ret = map_keyboard_auto(keyboard, move |evt: KbEvent, _| match evt { + KbEvent::Enter { surface, .. } => { + let wid = make_wid(&surface); + my_sink + .lock() + .unwrap() + .send_event(WindowEvent::Focused(true), wid); + target = Some(wid); + } + KbEvent::Leave { surface, .. } => { + let wid = make_wid(&surface); + my_sink + .lock() + .unwrap() + .send_event(WindowEvent::Focused(false), wid); + target = None; + } + KbEvent::Key { + modifiers, + rawkey, + keysym, + state, + utf8, + .. + } => { + if let Some(wid) = 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(); + let mut guard = my_sink.lock().unwrap(); guard.send_event( - Event::KeyboardInput { + WindowEvent::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 - }, + modifiers: modifiers.into(), }, }, - wid + wid, ); // send char event only on key press, not release - if let ElementState::Released = state { return } + if let ElementState::Released = state { + return; + } if let Some(txt) = utf8 { for chr in txt.chars() { - guard.send_event(Event::ReceivedCharacter(chr), wid); + guard.send_event(WindowEvent::ReceivedCharacter(chr), wid); } } } - }, - repeat_info: |_, _idata, _, _rate, _delay| { - // TODO: handle repeat info } - } -} + KbEvent::RepeatInfo { .. } => { /* TODO: handle repeat info */ } + }); + match ret { + Ok(keyboard) => keyboard, + Err((_, keyboard)) => { + // This is a 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) -// 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: |_, _, _, _, _, _, _, _| {} + // { variables to be captured by the closure + let mut target = None; + let my_sink = sink; + // } + keyboard.implement(move |evt, _| match evt { + wl_keyboard::Event::Enter { surface, .. } => { + let wid = make_wid(&surface); + my_sink + .lock() + .unwrap() + .send_event(WindowEvent::Focused(true), wid); + target = Some(wid); + } + wl_keyboard::Event::Leave { surface, .. } => { + let wid = make_wid(&surface); + my_sink + .lock() + .unwrap() + .send_event(WindowEvent::Focused(false), wid); + target = None; + } + wl_keyboard::Event::Key { key, state, .. } => { + if let Some(wid) = target { + let state = match state { + wl_keyboard::KeyState::Pressed => ElementState::Pressed, + wl_keyboard::KeyState::Released => ElementState::Released, + }; + my_sink.lock().unwrap().send_event( + WindowEvent::KeyboardInput { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + input: KeyboardInput { + state: state, + scancode: key, + virtual_keycode: None, + modifiers: ModifiersState::default(), + }, + }, + wid, + ); + } + } + _ => (), + }) + } } } fn key_to_vkey(rawkey: u32, keysym: u32) -> Option { match rawkey { - 1 => Some(VirtualKeyCode::Escape), - 2 => Some(VirtualKeyCode::Key1), - 3 => Some(VirtualKeyCode::Key2), - 4 => Some(VirtualKeyCode::Key3), - 5 => Some(VirtualKeyCode::Key4), - 6 => Some(VirtualKeyCode::Key5), - 7 => Some(VirtualKeyCode::Key6), - 8 => Some(VirtualKeyCode::Key7), - 9 => Some(VirtualKeyCode::Key8), + 1 => Some(VirtualKeyCode::Escape), + 2 => Some(VirtualKeyCode::Key1), + 3 => Some(VirtualKeyCode::Key2), + 4 => Some(VirtualKeyCode::Key3), + 5 => Some(VirtualKeyCode::Key4), + 6 => Some(VirtualKeyCode::Key5), + 7 => Some(VirtualKeyCode::Key6), + 8 => Some(VirtualKeyCode::Key7), + 9 => Some(VirtualKeyCode::Key8), 10 => Some(VirtualKeyCode::Key9), 11 => Some(VirtualKeyCode::Key0), - _ => keysym_to_vkey(keysym) + _ => keysym_to_vkey(keysym), } } fn keysym_to_vkey(keysym: u32) -> Option { - use super::wayland_kbd::keysyms; + use sctk::keyboard::keysyms; match keysym { // letters keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A), @@ -174,15 +178,15 @@ fn keysym_to_vkey(keysym: u32) -> Option { keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y), keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z), // F-- - keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1), - keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2), - keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3), - keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4), - keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5), - keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6), - keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7), - keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8), - keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9), + keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1), + keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2), + keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3), + keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4), + keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5), + keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6), + keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7), + keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8), + keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9), keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10), keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11), keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12), @@ -294,6 +298,17 @@ fn keysym_to_vkey(keysym: u32) -> Option { keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste), keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut), // fallback - _ => None + _ => None, + } +} + +impl From for ModifiersState { + fn from(mods: keyboard::ModifiersState) -> ModifiersState { + ModifiersState { + shift: mods.shift, + ctrl: mods.ctrl, + alt: mods.alt, + logo: mods.logo, + } } } diff --git a/src/platform/linux/wayland/mod.rs b/src/platform/linux/wayland/mod.rs index df3e3cb8..c54ceefe 100644 --- a/src/platform/linux/wayland/mod.rs +++ b/src/platform/linux/wayland/mod.rs @@ -1,14 +1,11 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] +#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd"))] 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; - -use wayland_client::protocol::wl_surface; -use wayland_client::Proxy; +use sctk::reexports::client::protocol::wl_surface; +use sctk::reexports::client::Proxy; mod event_loop; mod pointer; @@ -23,6 +20,6 @@ pub struct DeviceId; pub struct WindowId(usize); #[inline] -fn make_wid(s: &wl_surface::WlSurface) -> WindowId { - WindowId(s.ptr() as usize) -} \ No newline at end of file +fn make_wid(s: &Proxy) -> WindowId { + WindowId(s.c_ptr() as usize) +} diff --git a/src/platform/linux/wayland/pointer.rs b/src/platform/linux/wayland/pointer.rs index ba1ae3e6..48f2bdac 100644 --- a/src/platform/linux/wayland/pointer.rs +++ b/src/platform/linux/wayland/pointer.rs @@ -1,194 +1,190 @@ use std::sync::{Arc, Mutex}; -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase}; +use {ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}; use events::ModifiersState; -use super::{WindowId, DeviceId}; +use super::DeviceId; use super::event_loop::EventsLoopSink; use super::window::WindowStore; -use wayland_client::{Proxy, StateToken}; -use wayland_client::protocol::wl_pointer; +use sctk::reexports::client::{NewProxy, Proxy}; +use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer}; -pub struct PointerIData { +pub fn implement_pointer( + pointer: NewProxy, sink: Arc>, - windows_token: StateToken, - mouse_focus: Option, - axis_buffer: Option<(f32, f32)>, - axis_discrete_buffer: Option<(i32, i32)>, - axis_state: TouchPhase, -} + store: Arc>, +) -> Proxy { + let mut mouse_focus = None; + let mut axis_buffer = None; + let mut axis_discrete_buffer = None; + let mut axis_state = TouchPhase::Ended; -impl PointerIData { - pub fn new(sink: &Arc>, token: StateToken) - -> PointerIData - { - PointerIData { - sink: sink.clone(), - windows_token: token, - mouse_focus: None, - axis_buffer: None, - axis_discrete_buffer: None, - axis_state: TouchPhase::Cancelled + pointer.implement(move |evt, pointer: Proxy<_>| { + let mut sink = sink.lock().unwrap(); + let store = store.lock().unwrap(); + match evt { + PtrEvent::Enter { + surface, + surface_x, + surface_y, + .. + } => { + let wid = store.find_wid(&surface); + if let Some(wid) = wid { + mouse_focus = Some(wid); + sink.send_event( + WindowEvent::CursorEntered { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + }, + wid, + ); + sink.send_event( + WindowEvent::CursorMoved { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + position: (surface_x, surface_y), + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + wid, + ); + } + } + PtrEvent::Leave { surface, .. } => { + mouse_focus = None; + let wid = store.find_wid(&surface); + if let Some(wid) = wid { + sink.send_event( + WindowEvent::CursorLeft { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + }, + wid, + ); + } + } + PtrEvent::Motion { + surface_x, + surface_y, + .. + } => { + if let Some(wid) = mouse_focus { + sink.send_event( + WindowEvent::CursorMoved { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + position: (surface_x, surface_y), + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + wid, + ); + } + } + PtrEvent::Button { button, state, .. } => { + if let Some(wid) = 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, + }; + sink.send_event( + WindowEvent::MouseInput { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + state: state, + button: button, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + wid, + ); + } + } + PtrEvent::Axis { axis, value, .. } => { + if let Some(wid) = 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, + } + sink.send_event( + WindowEvent::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), + phase: TouchPhase::Moved, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + wid, + ); + } else { + let (mut x, mut y) = 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, + } + axis_buffer = Some((x, y)); + axis_state = match axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started, + } + } + } + } + PtrEvent::Frame => { + let axis_buffer = axis_buffer.take(); + let axis_discrete_buffer = axis_discrete_buffer.take(); + if let Some(wid) = mouse_focus { + if let Some((x, y)) = axis_discrete_buffer { + sink.send_event( + WindowEvent::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::LineDelta(x as f32, y as f32), + phase: axis_state, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + wid, + ); + } else if let Some((x, y)) = axis_buffer { + sink.send_event( + WindowEvent::MouseWheel { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), + phase: axis_state, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + wid, + ); + } + } + } + PtrEvent::AxisSource { .. } => (), + PtrEvent::AxisStop { .. } => { + axis_state = TouchPhase::Ended; + } + PtrEvent::AxisDiscrete { axis, discrete } => { + let (mut x, mut y) = 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, + } + axis_discrete_buffer = Some((x, y)); + axis_state = match axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started, + } + } } - } -} - -pub 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::CursorEntered { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - }, - wid, - ); - guard.send_event( - Event::CursorMoved { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (x, y), - // TODO: replace dummy value with actual modifier state - modifiers: ModifiersState::default(), - }, - wid, - ); - } - }, - 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::CursorLeft { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - }, - wid, - ); - } - }, - motion: |_, idata, _, _, x, y| { - if let Some(wid) = idata.mouse_focus { - idata.sink.lock().unwrap().send_event( - Event::CursorMoved { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (x, y), - // TODO: replace dummy value with actual modifier state - modifiers: ModifiersState::default(), - }, - wid - ); - } - }, - 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, - // TODO: replace dummy value with actual modifier state - modifiers: ModifiersState::default(), - }, - 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, - // TODO: replace dummy value with actual modifier state - modifiers: ModifiersState::default(), - }, - 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, - // TODO: replace dummy value with actual modifier state - modifiers: ModifiersState::default(), - }, - 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, - // TODO: replace dummy value with actual modifier state - modifiers: ModifiersState::default(), - }, - 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 - } - }, - } + }) } diff --git a/src/platform/linux/wayland/touch.rs b/src/platform/linux/wayland/touch.rs index 9aa9b4b4..bd29afe2 100644 --- a/src/platform/linux/wayland/touch.rs +++ b/src/platform/linux/wayland/touch.rs @@ -1,106 +1,93 @@ use std::sync::{Arc, Mutex}; -use {WindowEvent as Event, TouchPhase}; +use {TouchPhase, WindowEvent}; -use super::{WindowId, DeviceId}; +use super::{DeviceId, WindowId}; use super::event_loop::EventsLoopSink; use super::window::WindowStore; -use wayland_client::StateToken; -use wayland_client::protocol::wl_touch; - -pub struct TouchIData { - sink: Arc>, - windows_token: StateToken, - pending_ids: Vec, -} +use sctk::reexports::client::{NewProxy, Proxy}; +use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch}; struct TouchPoint { wid: WindowId, location: (f64, f64), - id: i32 + id: i32, } -impl TouchIData { - pub fn new(sink: &Arc>, token: StateToken) - -> TouchIData - { - TouchIData { - sink: sink.clone(), - windows_token: token, - pending_ids: Vec::new(), - } - } -} - -pub fn touch_implementation() -> wl_touch::Implementation { - wl_touch::Implementation { - down: |evqh, idata, _, _serial, _time, surface, touch_id, x, y| { - 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::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - phase: TouchPhase::Started, +pub(crate) fn implement_touch( + touch: NewProxy, + sink: Arc>, + store: Arc>, +) -> Proxy { + let mut pending_ids = Vec::new(); + touch.implement(move |evt, _| { + let mut sink = sink.lock().unwrap(); + let store = store.lock().unwrap(); + match evt { + TouchEvent::Down { + surface, id, x, y, .. + } => { + let wid = store.find_wid(&surface); + if let Some(wid) = wid { + sink.send_event( + WindowEvent::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Started, + location: (x, y), + id: id as u64, + }), + wid, + ); + pending_ids.push(TouchPoint { + wid: wid, location: (x, y), - id: touch_id as u64 - }), - wid, - ); - idata.pending_ids.push(TouchPoint { - wid: wid, - location: (x, y), - id: touch_id - }); + id: id, + }); + } } - }, - up: |_, idata, _, _serial, _time, touch_id| { - let idx = idata.pending_ids.iter().position(|p| p.id == touch_id); - if let Some(idx) = idx { - let pt = idata.pending_ids.remove(idx); - let mut guard = idata.sink.lock().unwrap(); - guard.send_event( - Event::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - phase: TouchPhase::Ended, - location: pt.location, - id: touch_id as u64 - }), - pt.wid, - ); + TouchEvent::Up { id, .. } => { + let idx = pending_ids.iter().position(|p| p.id == id); + if let Some(idx) = idx { + let pt = pending_ids.remove(idx); + sink.send_event( + WindowEvent::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Ended, + location: pt.location, + id: id as u64, + }), + pt.wid, + ); + } } - }, - motion: |_, idata, _, _time, touch_id, x, y| { - let pt = idata.pending_ids.iter_mut().find(|p| p.id == touch_id); - if let Some(pt) = pt { - let mut guard = idata.sink.lock().unwrap(); - pt.location = (x, y); - guard.send_event( - Event::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - phase: TouchPhase::Moved, - location: (x, y), - id: touch_id as u64 - }), - pt.wid, - ); + TouchEvent::Motion { id, x, y, .. } => { + let pt = pending_ids.iter_mut().find(|p| p.id == id); + if let Some(pt) = pt { + pt.location = (x, y); + sink.send_event( + WindowEvent::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Moved, + location: (x, y), + id: id as u64, + }), + pt.wid, + ); + } } - }, - frame: |_, _, _| {}, - cancel: |_, idata, _| { - let mut guard = idata.sink.lock().unwrap(); - for pt in idata.pending_ids.drain(..) { - guard.send_event( - Event::Touch(::Touch { + TouchEvent::Frame => (), + TouchEvent::Cancel => for pt in pending_ids.drain(..) { + sink.send_event( + WindowEvent::Touch(::Touch { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), phase: TouchPhase::Cancelled, location: pt.location, - id: pt.id as u64 + id: pt.id as u64, }), pt.wid, ); - } + }, } - } -} \ No newline at end of file + }) +} diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index c8f1bd2e..4720ea17 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -1,81 +1,128 @@ use std::sync::{Arc, Mutex, Weak}; -use wayland_client::protocol::{wl_display,wl_surface}; -use wayland_client::{Proxy, StateToken}; - -use {CreationError, MouseCursor, CursorState, WindowAttributes}; +use {CreationError, CursorState, MouseCursor, WindowAttributes}; use platform::MonitorId as PlatformMonitorId; use window::MonitorId as RootMonitorId; -use super::{EventsLoop, WindowId, make_wid, MonitorId}; -use super::wayland_window::{Frame, FrameImplementation, State as FrameState}; -use super::event_loop::StateContext; +use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow}; +use sctk::reexports::client::{Display, Proxy}; +use sctk::reexports::client::protocol::{wl_seat, wl_surface}; +use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests; +use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests; + +use super::{make_wid, EventsLoop, MonitorId, WindowId}; pub struct Window { - surface: wl_surface::WlSurface, - frame: Arc>, - monitors: Arc>, + surface: Proxy, + frame: Arc>>, + monitors: Arc>>, size: Arc>, kill_switch: (Arc>, Arc>), - display: Arc, - need_frame_refresh: Arc> + display: Arc, + need_frame_refresh: Arc>, } impl Window { - pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result - { - let (width, height) = attributes.dimensions.unwrap_or((800,600)); + pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result { + let (width, height) = attributes.dimensions.unwrap_or((800, 600)); - // Create the decorated surface + // Create the window let size = Arc::new(Mutex::new((width, height))); - let store_token = evlp.store.clone(); - let (surface, mut frame) = evlp.create_window( - width, height, decorated_impl(), - |surface| FrameIData { - surface: surface.clone().unwrap(), - store_token: store_token.clone() + + // monitor tracking + let monitor_list = Arc::new(Mutex::new(Vec::new())); + + let surface = evlp.env.compositor.create_surface().unwrap().implement({ + let list = monitor_list.clone(); + let omgr = evlp.env.outputs.clone(); + move |event, _| match event { + wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId { + proxy: output, + mgr: omgr.clone(), + }), + wl_surface::Event::Leave { output } => { + list.lock().unwrap().retain(|m| !m.proxy.equals(&output)); + } } - ); + }); + + let window_store = evlp.store.clone(); + let my_surface = surface.clone(); + let mut frame = SWindow::::init( + surface.clone(), + (width, height), + &evlp.env.compositor, + &evlp.env.subcompositor, + &evlp.env.shm, + &evlp.env.shell, + move |event, ()| match event { + WEvent::Configure { new_size, .. } => { + let mut store = window_store.lock().unwrap(); + for window in &mut store.windows { + if window.surface.equals(&my_surface) { + window.newsize = new_size.map(|(w, h)| (w as i32, h as i32)); + window.need_refresh = true; + *(window.need_frame_refresh.lock().unwrap()) = true; + return; + } + } + } + WEvent::Refresh => { + let store = window_store.lock().unwrap(); + for window in &store.windows { + if window.surface.equals(&my_surface) { + *(window.need_frame_refresh.lock().unwrap()) = true; + return; + } + } + } + WEvent::Close => { + let mut store = window_store.lock().unwrap(); + for window in &mut store.windows { + if window.surface.equals(&my_surface) { + window.closed = true; + return; + } + } + } + }, + ).unwrap(); + + for &(_, ref seat) in evlp.seats.lock().unwrap().iter() { + frame.new_seat(seat); + } + // Check for fullscreen requirements - if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen { - let info = monitor_id.info.lock().unwrap(); - frame.set_state(FrameState::Fullscreen(Some(&info.output))); + if let Some(RootMonitorId { + inner: PlatformMonitorId::Wayland(ref monitor_id), + }) = attributes.fullscreen + { + frame.set_fullscreen(Some(&monitor_id.proxy)); } else if attributes.maximized { - frame.set_state(FrameState::Maximized); + frame.set_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())); - { - let mut evq = evlp.evq.borrow_mut(); - let idata = (evlp.ctxt_token.clone(), monitor_list.clone()); - evq.register(&surface, surface_impl(), idata); - } + frame.set_min_size(attributes.min_dimensions); + frame.set_max_size(attributes.max_dimensions); let kill_switch = Arc::new(Mutex::new(false)); let need_frame_refresh = Arc::new(Mutex::new(true)); let frame = Arc::new(Mutex::new(frame)); - { - let mut evq = evlp.evq.borrow_mut(); - evq.state().get_mut(&store_token).windows.push(InternalWindow { - closed: false, - newsize: None, - need_refresh: false, - need_frame_refresh: need_frame_refresh.clone(), - surface: surface.clone().unwrap(), - kill_switch: kill_switch.clone(), - frame: Arc::downgrade(&frame) - }); - evq.sync_roundtrip().unwrap(); - } + evlp.store.lock().unwrap().windows.push(InternalWindow { + closed: false, + newsize: None, + need_refresh: false, + need_frame_refresh: need_frame_refresh.clone(), + surface: surface.clone(), + kill_switch: kill_switch.clone(), + frame: Arc::downgrade(&frame), + }); + evlp.evq.borrow_mut().sync_roundtrip().unwrap(); Ok(Window { display: evlp.display.clone(), @@ -84,7 +131,7 @@ impl Window { monitors: monitor_list, size: size, kill_switch: (kill_switch, evlp.cleanup_needed.clone()), - need_frame_refresh: need_frame_refresh + need_frame_refresh: need_frame_refresh, }) } @@ -131,25 +178,25 @@ impl Window { #[inline] pub fn get_outer_size(&self) -> Option<(u32, u32)> { let (w, h) = self.size.lock().unwrap().clone(); - let (w, h) = super::wayland_window::add_borders(w as i32, h as i32); + // let (w, h) = super::wayland_window::add_borders(w as i32, h as i32); Some((w as u32, h as u32)) } #[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.frame.lock().unwrap().resize(x as i32, y as i32); + self.frame.lock().unwrap().resize(x, y); *(self.size.lock().unwrap()) = (x, y); } #[inline] pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) { - self.frame.lock().unwrap().set_min_size(dimensions.map(|(w, h)| (w as i32, h as i32))); + self.frame.lock().unwrap().set_min_size(dimensions); } #[inline] pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) { - self.frame.lock().unwrap().set_max_size(dimensions.map(|(w, h)| (w as i32, h as i32))); + self.frame.lock().unwrap().set_max_size(dimensions); } #[inline] @@ -159,22 +206,22 @@ impl Window { #[inline] pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> { - use CursorState::{Grab, Normal, Hide}; + use CursorState::{Grab, Hide, Normal}; // TODO : not yet possible on wayland to grab cursor match state { Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()), Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()), - Normal => Ok(()) + Normal => Ok(()), } } #[inline] pub fn hidpi_factor(&self) -> f32 { - let mut factor = 1.0; + let mut factor: f32 = 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; } + for monitor_id in guard.iter() { + let hidpif = monitor_id.get_hidpi_factor(); + factor = factor.max(hidpif); } factor } @@ -186,18 +233,23 @@ impl Window { pub fn set_maximized(&self, maximized: bool) { if maximized { - self.frame.lock().unwrap().set_state(FrameState::Maximized); + self.frame.lock().unwrap().set_maximized(); } else { - self.frame.lock().unwrap().set_state(FrameState::Regular); + self.frame.lock().unwrap().unset_maximized(); } } pub fn set_fullscreen(&self, monitor: Option) { - if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = monitor { - let info = monitor_id.info.lock().unwrap(); - self.frame.lock().unwrap().set_state(FrameState::Fullscreen(Some(&info.output))); + if let Some(RootMonitorId { + inner: PlatformMonitorId::Wayland(ref monitor_id), + }) = monitor + { + self.frame + .lock() + .unwrap() + .set_fullscreen(Some(&monitor_id.proxy)); } else { - self.frame.lock().unwrap().set_state(FrameState::Regular); + self.frame.lock().unwrap().unset_fullscreen(); } } @@ -207,11 +259,11 @@ impl Window { Err(()) } - pub fn get_display(&self) -> &wl_display::WlDisplay { + pub fn get_display(&self) -> &Display { &*self.display } - pub fn get_surface(&self) -> &wl_surface::WlSurface { + pub fn get_surface(&self) -> &Proxy { &self.surface } @@ -219,7 +271,7 @@ impl Window { // 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() + guard.last().unwrap().clone() } } @@ -235,25 +287,27 @@ impl Drop for Window { */ struct InternalWindow { - surface: wl_surface::WlSurface, + surface: Proxy, newsize: Option<(i32, i32)>, need_refresh: bool, need_frame_refresh: Arc>, closed: bool, kill_switch: Arc>, - frame: Weak> + frame: Weak>>, } pub struct WindowStore { - windows: Vec + windows: Vec, } impl WindowStore { pub fn new() -> WindowStore { - WindowStore { windows: Vec::new() } + WindowStore { + windows: Vec::new(), + } } - pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option { + pub fn find_wid(&self, surface: &Proxy) -> Option { for window in &self.windows { if surface.equals(&window.surface) { return Some(make_wid(surface)); @@ -277,8 +331,17 @@ impl WindowStore { pruned } + pub fn new_seat(&self, seat: &Proxy) { + for window in &self.windows { + if let Some(w) = window.frame.upgrade() { + w.lock().unwrap().new_seat(seat); + } + } + } + pub fn for_each(&mut self, mut f: F) - where F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut Frame>) + where + F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow>), { for window in &mut self.windows { let opt_arc = window.frame.upgrade(); @@ -289,7 +352,7 @@ impl WindowStore { ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), window.closed, make_wid(&window.surface), - opt_mutex_lock.as_mut().map(|m| &mut **m) + opt_mutex_lock.as_mut().map(|m| &mut **m), ); window.need_refresh = false; // avoid re-spamming the event @@ -297,68 +360,3 @@ impl WindowStore { } } } - -/* - * Protocol implementation - */ - -struct FrameIData { - store_token: StateToken, - surface: wl_surface::WlSurface -} - -fn decorated_impl() -> FrameImplementation { - FrameImplementation { - configure: |evqh, idata, _, newsize| { - 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.lock().unwrap()) = 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; - } - } - }, - 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.lock().unwrap()) = 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)); - } - } -}