From b2d382b93f21b27f7a289d8b48d0c46ea3ab3eb9 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 11 May 2018 12:12:29 +0200 Subject: [PATCH 1/3] wayland: properly update get_inner_size on resize from borders --- src/platform/linux/wayland/event_loop.rs | 7 ++++--- src/platform/linux/wayland/window.rs | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 2c02d18dac..c8e52a8b79 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -250,12 +250,13 @@ impl EventsLoop { } // process pending resize/refresh self.store.lock().unwrap().for_each( - |newsize, refresh, frame_refresh, closed, wid, frame| { + |newsize, size, refresh, frame_refresh, closed, wid, frame| { if let Some(frame) = frame { if let Some((w, h)) = newsize { - frame.resize(w as u32, h as u32); + frame.resize(w, h); frame.refresh(); - sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid); + sink.send_event(::WindowEvent::Resized(w, h), wid); + *size = (w, h); } else if frame_refresh { frame.refresh(); } diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index 216b8a4fb2..69e37191dc 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -59,7 +59,7 @@ impl Window { 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.newsize = new_size; window.need_refresh = true; *(window.need_frame_refresh.lock().unwrap()) = true; return; @@ -115,6 +115,7 @@ impl Window { evlp.store.lock().unwrap().windows.push(InternalWindow { closed: false, newsize: None, + size: size.clone(), need_refresh: false, need_frame_refresh: need_frame_refresh.clone(), surface: surface.clone(), @@ -287,7 +288,8 @@ impl Drop for Window { struct InternalWindow { surface: Proxy, - newsize: Option<(i32, i32)>, + newsize: Option<(u32, u32)>, + size: Arc>, need_refresh: bool, need_frame_refresh: Arc>, closed: bool, @@ -340,13 +342,14 @@ impl WindowStore { pub fn for_each(&mut self, mut f: F) where - F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow>), + F: FnMut(Option<(u32, u32)>, &mut (u32, u32), bool, bool, bool, WindowId, Option<&mut SWindow>), { for window in &mut self.windows { let opt_arc = window.frame.upgrade(); let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap()); f( window.newsize.take(), + &mut *(window.size.lock().unwrap()), window.need_refresh, ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), window.closed, From 15f5f28121dacb692cfed192b6e2a567dd7d5e17 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 11 May 2018 14:53:08 +0200 Subject: [PATCH 2/3] wayland: implement hidpi support for wayland --- src/platform/linux/mod.rs | 2 +- src/platform/linux/wayland/event_loop.rs | 40 +++++++- src/platform/linux/wayland/window.rs | 114 +++++++++++++++++++---- 3 files changed, 132 insertions(+), 24 deletions(-) diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 85fa550ad1..8430ea2005 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -110,7 +110,7 @@ impl MonitorId { pub fn get_hidpi_factor(&self) -> f32 { match self { &MonitorId::X(ref m) => m.get_hidpi_factor(), - &MonitorId::Wayland(ref m) => m.get_hidpi_factor(), + &MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f32, } } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index c8e52a8b79..77c6ffe2c0 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -250,17 +250,47 @@ impl EventsLoop { } // process pending resize/refresh self.store.lock().unwrap().for_each( - |newsize, size, refresh, frame_refresh, closed, wid, frame| { + |newsize, size, current_dpi, new_dpi, refresh, frame_refresh, closed, wid, frame| { + // update frame if needed if let Some(frame) = frame { if let Some((w, h)) = newsize { frame.resize(w, h); frame.refresh(); - sink.send_event(::WindowEvent::Resized(w, h), wid); *size = (w, h); } else if frame_refresh { frame.refresh(); } } + // handle dpi/size change + match (newsize, new_dpi) { + (Some((w, h)), Some(new_dpi)) => { + // both size and dpi changed, compute a compound event + let dpi_w = w * new_dpi as u32; + let dpi_h = h * new_dpi as u32; + sink.send_event(::WindowEvent::HiDPIFactorChanged(new_dpi as f32), wid); + sink.send_event(::WindowEvent::Resized(dpi_w as u32, dpi_h as u32), wid); + *size = (dpi_w as u32, dpi_h as u32); + }, + (None, Some(new_dpi)) => { + // dpi has changed, fetch previous DPI-aware size and recompute it + let (old_w, old_h) = *size; + let dpi_w = old_w as i32 * new_dpi / current_dpi; + let dpi_h = old_h as i32 * new_dpi / current_dpi; + sink.send_event(::WindowEvent::HiDPIFactorChanged(new_dpi as f32), wid); + sink.send_event(::WindowEvent::Resized(dpi_w as u32, dpi_h as u32), wid); + *size = (dpi_w as u32, dpi_h as u32); + }, + (Some((w, h)), None) => { + // new size, take dpi into account to send resized event + let dpi_w = w * current_dpi as u32; + let dpi_h = h * current_dpi as u32; + sink.send_event(::WindowEvent::Resized(dpi_w as u32, dpi_h as u32), wid); + *size = (dpi_w as u32, dpi_h as u32); + }, + (None, None) => { + // nothing to do + } + } if refresh { sink.send_event(::WindowEvent::Refresh, wid); } @@ -462,9 +492,9 @@ impl MonitorId { } #[inline] - pub fn get_hidpi_factor(&self) -> f32 { + pub fn get_hidpi_factor(&self) -> i32 { self.mgr - .with_info(&self.proxy, |_, info| info.scale_factor as f32) - .unwrap_or(1.0) + .with_info(&self.proxy, |_, info| info.scale_factor) + .unwrap_or(1) } } diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index 69e37191dc..475f20c26e 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -6,7 +6,7 @@ use window::MonitorId as RootMonitorId; 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_seat, wl_surface, wl_output}; use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests; use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests; @@ -15,7 +15,7 @@ use super::{make_wid, EventsLoop, MonitorId, WindowId}; pub struct Window { surface: Proxy, frame: Arc>>, - monitors: Arc>>, + monitors: Arc>, size: Arc>, kill_switch: (Arc>, Arc>), display: Arc, @@ -29,18 +29,35 @@ impl Window { let size = Arc::new(Mutex::new((width, height))); // monitor tracking - let monitor_list = Arc::new(Mutex::new(Vec::new())); + let monitor_list = Arc::new(Mutex::new(MonitorList::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(), - }), + let window_store = evlp.store.clone(); + move |event, surface: Proxy| match event { + wl_surface::Event::Enter { output } => { + let dpi_change = list.lock().unwrap().add_output(MonitorId { + proxy: output, + mgr: omgr.clone(), + }); + if let Some(dpi) = dpi_change { + if surface.version() >= 3 { + // without version 3 we can't be dpi aware + window_store.lock().unwrap().dpi_change(&surface, dpi); + surface.set_buffer_scale(dpi); + } + } + }, wl_surface::Event::Leave { output } => { - list.lock().unwrap().retain(|m| !m.proxy.equals(&output)); + let dpi_change = list.lock().unwrap().del_output(&output); + if let Some(dpi) = dpi_change { + if surface.version() >= 3 { + // without version 3 we can't be dpi aware + window_store.lock().unwrap().dpi_change(&surface, dpi); + surface.set_buffer_scale(dpi); + } + } } } }); @@ -121,6 +138,8 @@ impl Window { surface: surface.clone(), kill_switch: kill_switch.clone(), frame: Arc::downgrade(&frame), + current_dpi: 1, + new_dpi: None, }); evlp.evq.borrow_mut().sync_roundtrip().unwrap(); @@ -185,6 +204,9 @@ 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 dpi = self.monitors.lock().unwrap().compute_hidpi_factor(); + let x = x / dpi as u32; + let y = y / dpi as u32; self.frame.lock().unwrap().resize(x, y); *(self.size.lock().unwrap()) = (x, y); } @@ -217,13 +239,7 @@ impl Window { #[inline] pub fn hidpi_factor(&self) -> f32 { - let mut factor: f32 = 1.0; - let guard = self.monitors.lock().unwrap(); - for monitor_id in guard.iter() { - let hidpif = monitor_id.get_hidpi_factor(); - factor = factor.max(hidpif); - } - factor + self.monitors.lock().unwrap().compute_hidpi_factor() as f32 } pub fn set_decorations(&self, decorate: bool) { @@ -271,7 +287,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.last().unwrap().clone() + guard.monitors.last().unwrap().clone() } } @@ -295,6 +311,8 @@ struct InternalWindow { closed: bool, kill_switch: Arc>, frame: Weak>>, + current_dpi: i32, + new_dpi: Option } pub struct WindowStore { @@ -340,9 +358,17 @@ impl WindowStore { } } + fn dpi_change(&mut self, surface: &Proxy, new: i32) { + for window in &mut self.windows { + if surface.equals(&window.surface) { + window.new_dpi = Some(new); + } + } + } + pub fn for_each(&mut self, mut f: F) where - F: FnMut(Option<(u32, u32)>, &mut (u32, u32), bool, bool, bool, WindowId, Option<&mut SWindow>), + F: FnMut(Option<(u32, u32)>, &mut (u32, u32), i32, Option, bool, bool, bool, WindowId, Option<&mut SWindow>), { for window in &mut self.windows { let opt_arc = window.frame.upgrade(); @@ -350,15 +376,67 @@ impl WindowStore { f( window.newsize.take(), &mut *(window.size.lock().unwrap()), + window.current_dpi, + window.new_dpi, window.need_refresh, ::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), ); + if let Some(dpi) = window.new_dpi.take() { + window.current_dpi = dpi; + } window.need_refresh = false; // avoid re-spamming the event window.closed = false; } } } + +/* + * Monitor list with some covenience method to compute DPI + */ + +struct MonitorList { + monitors: Vec +} + +impl MonitorList { + fn new() -> MonitorList { + MonitorList { + monitors: Vec::new() + } + } + + fn compute_hidpi_factor(&self) -> i32 { + let mut factor = 1; + for monitor_id in &self.monitors { + let monitor_dpi = monitor_id.get_hidpi_factor(); + if monitor_dpi > factor { factor = monitor_dpi; } + } + factor + } + + fn add_output(&mut self, monitor: MonitorId) -> Option { + let old_dpi = self.compute_hidpi_factor(); + let monitor_dpi = monitor.get_hidpi_factor(); + self.monitors.push(monitor); + if monitor_dpi > old_dpi { + Some(monitor_dpi) + } else { + None + } + } + + fn del_output(&mut self, output: &Proxy) -> Option { + let old_dpi = self.compute_hidpi_factor(); + self.monitors.retain(|m| !m.proxy.equals(output)); + let new_dpi = self.compute_hidpi_factor(); + if new_dpi != old_dpi { + Some(new_dpi) + } else { + None + } + } +} \ No newline at end of file From 9eeef9543ec5d0fb12bb2ea5c678b465b4fc380c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 11 May 2018 15:17:44 +0200 Subject: [PATCH 3/3] wayland: report input taking hidpi into account --- src/platform/linux/wayland/pointer.rs | 74 +++++++++++++-------------- src/platform/linux/wayland/touch.rs | 49 +++++++++--------- src/platform/linux/wayland/window.rs | 18 +++---- 3 files changed, 69 insertions(+), 72 deletions(-) diff --git a/src/platform/linux/wayland/pointer.rs b/src/platform/linux/wayland/pointer.rs index 48f2bdac2f..0ec99182e6 100644 --- a/src/platform/linux/wayland/pointer.rs +++ b/src/platform/linux/wayland/pointer.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use {ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}; use events::ModifiersState; -use super::DeviceId; +use super::{DeviceId, make_wid}; use super::event_loop::EventsLoopSink; use super::window::WindowStore; @@ -30,57 +30,53 @@ pub fn implement_pointer( 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, - ); - } + let dpi = store.get_dpi(&surface) as f64; + sink.send_event( + WindowEvent::CursorEntered { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + }, + make_wid(&surface), + ); + sink.send_event( + WindowEvent::CursorMoved { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + position: (dpi*surface_x, dpi*surface_y), + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), + }, + make_wid(&surface), + ); + mouse_focus = Some(surface); } 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, - ); - } + sink.send_event( + WindowEvent::CursorLeft { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + }, + make_wid(&surface), + ); } PtrEvent::Motion { surface_x, surface_y, .. } => { - if let Some(wid) = mouse_focus { + if let Some(ref surface) = mouse_focus { + let dpi = store.get_dpi(&surface) as f64; sink.send_event( WindowEvent::CursorMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (surface_x, surface_y), + position: (dpi*surface_x, dpi*surface_y), // TODO: replace dummy value with actual modifier state modifiers: ModifiersState::default(), }, - wid, + make_wid(surface), ); } } PtrEvent::Button { button, state, .. } => { - if let Some(wid) = mouse_focus { + if let Some(ref surface) = mouse_focus { let state = match state { wl_pointer::ButtonState::Pressed => ElementState::Pressed, wl_pointer::ButtonState::Released => ElementState::Released, @@ -100,12 +96,12 @@ pub fn implement_pointer( // TODO: replace dummy value with actual modifier state modifiers: ModifiersState::default(), }, - wid, + make_wid(surface), ); } } PtrEvent::Axis { axis, value, .. } => { - if let Some(wid) = mouse_focus { + if let Some(ref surface) = mouse_focus { if pointer.version() < 5 { let (mut x, mut y) = (0.0, 0.0); // old seat compatibility @@ -122,7 +118,7 @@ pub fn implement_pointer( // TODO: replace dummy value with actual modifier state modifiers: ModifiersState::default(), }, - wid, + make_wid(surface), ); } else { let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0)); @@ -142,7 +138,7 @@ pub fn implement_pointer( 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(ref surface) = mouse_focus { if let Some((x, y)) = axis_discrete_buffer { sink.send_event( WindowEvent::MouseWheel { @@ -152,7 +148,7 @@ pub fn implement_pointer( // TODO: replace dummy value with actual modifier state modifiers: ModifiersState::default(), }, - wid, + make_wid(surface), ); } else if let Some((x, y)) = axis_buffer { sink.send_event( @@ -163,7 +159,7 @@ pub fn implement_pointer( // TODO: replace dummy value with actual modifier state modifiers: ModifiersState::default(), }, - wid, + make_wid(surface), ); } } diff --git a/src/platform/linux/wayland/touch.rs b/src/platform/linux/wayland/touch.rs index bd29afe2e2..8d2265f8be 100644 --- a/src/platform/linux/wayland/touch.rs +++ b/src/platform/linux/wayland/touch.rs @@ -2,15 +2,16 @@ use std::sync::{Arc, Mutex}; use {TouchPhase, WindowEvent}; -use super::{DeviceId, WindowId}; +use super::{DeviceId, make_wid}; use super::event_loop::EventsLoopSink; use super::window::WindowStore; use sctk::reexports::client::{NewProxy, Proxy}; +use sctk::reexports::client::protocol::wl_surface; use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch}; struct TouchPoint { - wid: WindowId, + surface: Proxy, location: (f64, f64), id: i32, } @@ -28,23 +29,22 @@ pub(crate) fn implement_touch( 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: id, - }); - } + let dpi = store.get_dpi(&surface) as f64; + let location = (dpi*x, dpi*y); + sink.send_event( + WindowEvent::Touch(::Touch { + device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + phase: TouchPhase::Started, + location, + id: id as u64, + }), + make_wid(&surface), + ); + pending_ids.push(TouchPoint { + surface: surface, + location, + id: id, + }); } TouchEvent::Up { id, .. } => { let idx = pending_ids.iter().position(|p| p.id == id); @@ -57,22 +57,23 @@ pub(crate) fn implement_touch( location: pt.location, id: id as u64, }), - pt.wid, + make_wid(&pt.surface), ); } } 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); + let dpi = store.get_dpi(&pt.surface) as f64; + pt.location = (dpi*x, dpi*y); sink.send_event( WindowEvent::Touch(::Touch { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), phase: TouchPhase::Moved, - location: (x, y), + location: pt.location, id: id as u64, }), - pt.wid, + make_wid(&pt.surface), ); } } @@ -85,7 +86,7 @@ pub(crate) fn implement_touch( location: pt.location, id: pt.id as u64, }), - pt.wid, + make_wid(&pt.surface), ); }, } diff --git a/src/platform/linux/wayland/window.rs b/src/platform/linux/wayland/window.rs index 475f20c26e..a9f9b0adff 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform/linux/wayland/window.rs @@ -326,15 +326,6 @@ impl WindowStore { } } - pub fn find_wid(&self, surface: &Proxy) -> Option { - for window in &self.windows { - if surface.equals(&window.surface) { - return Some(make_wid(surface)); - } - } - None - } - pub fn cleanup(&mut self) -> Vec { let mut pruned = Vec::new(); self.windows.retain(|w| { @@ -366,6 +357,15 @@ impl WindowStore { } } + pub fn get_dpi(&self, surface: &Proxy) -> i32 { + for window in &self.windows { + if surface.equals(&window.surface) { + return window.current_dpi; + } + } + return 1; + } + pub fn for_each(&mut self, mut f: F) where F: FnMut(Option<(u32, u32)>, &mut (u32, u32), i32, Option, bool, bool, bool, WindowId, Option<&mut SWindow>),