diff --git a/Cargo.toml b/Cargo.toml index 1869b1c8..f46f61a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,8 @@ features = [ [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] } -calloop = "0.4.2" +mio = "0.6" +mio-extras = "2.0" smithay-client-toolkit = "0.6" x11-dl = "2.18.3" percent-encoding = "2.0" diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 616c02fe..350e05f0 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -7,11 +7,9 @@ use raw_window_handle::RawWindowHandle; use smithay_client_toolkit::reexports::client::ConnectError; pub use self::x11::XNotSupported; -use self::x11::{ - ffi::XVisualInfo, get_xtarget, util::WindowType as XWindowType, XConnection, XError, -}; +use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError}; use crate::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::Event, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, @@ -248,7 +246,7 @@ impl Window { } #[inline] - pub fn outer_position(&self) -> Result { + pub fn outer_position(&self) -> Result { match self { &Window::X(ref w) => w.outer_position(), &Window::Wayland(ref w) => w.outer_position(), @@ -256,7 +254,7 @@ impl Window { } #[inline] - pub fn inner_position(&self) -> Result { + pub fn inner_position(&self) -> Result { match self { &Window::X(ref m) => m.inner_position(), &Window::Wayland(ref m) => m.inner_position(), @@ -264,7 +262,7 @@ impl Window { } #[inline] - pub fn set_outer_position(&self, position: LogicalPosition) { + pub fn set_outer_position(&self, position: Position) { match self { &Window::X(ref w) => w.set_outer_position(position), &Window::Wayland(ref w) => w.set_outer_position(position), @@ -272,7 +270,7 @@ impl Window { } #[inline] - pub fn inner_size(&self) -> LogicalSize { + pub fn inner_size(&self) -> PhysicalSize { match self { &Window::X(ref w) => w.inner_size(), &Window::Wayland(ref w) => w.inner_size(), @@ -280,7 +278,7 @@ impl Window { } #[inline] - pub fn outer_size(&self) -> LogicalSize { + pub fn outer_size(&self) -> PhysicalSize { match self { &Window::X(ref w) => w.outer_size(), &Window::Wayland(ref w) => w.outer_size(), @@ -288,7 +286,7 @@ impl Window { } #[inline] - pub fn set_inner_size(&self, size: LogicalSize) { + pub fn set_inner_size(&self, size: Size) { match self { &Window::X(ref w) => w.set_inner_size(size), &Window::Wayland(ref w) => w.set_inner_size(size), @@ -296,7 +294,7 @@ impl Window { } #[inline] - pub fn set_min_inner_size(&self, dimensions: Option) { + pub fn set_min_inner_size(&self, dimensions: Option) { match self { &Window::X(ref w) => w.set_min_inner_size(dimensions), &Window::Wayland(ref w) => w.set_min_inner_size(dimensions), @@ -304,7 +302,7 @@ impl Window { } #[inline] - pub fn set_max_inner_size(&self, dimensions: Option) { + pub fn set_max_inner_size(&self, dimensions: Option) { match self { &Window::X(ref w) => w.set_max_inner_size(dimensions), &Window::Wayland(ref w) => w.set_max_inner_size(dimensions), @@ -352,7 +350,7 @@ impl Window { } #[inline] - pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { match self { &Window::X(ref w) => w.set_cursor_position(position), &Window::Wayland(ref w) => w.set_cursor_position(position), @@ -416,7 +414,7 @@ impl Window { } #[inline] - pub fn set_ime_position(&self, position: LogicalPosition) { + pub fn set_ime_position(&self, position: Position) { match self { &Window::X(ref w) => w.set_ime_position(position), &Window::Wayland(_) => (), @@ -603,7 +601,7 @@ impl EventLoop { .into_iter() .map(MonitorHandle::Wayland) .collect(), - EventLoop::X(ref evlp) => get_xtarget(&evlp.target) + EventLoop::X(ref evlp) => evlp .x_connection() .available_monitors() .into_iter() @@ -616,9 +614,7 @@ impl EventLoop { pub fn primary_monitor(&self) -> MonitorHandle { match *self { EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.primary_monitor()), - EventLoop::X(ref evlp) => { - MonitorHandle::X(get_xtarget(&evlp.target).x_connection().primary_monitor()) - } + EventLoop::X(ref evlp) => MonitorHandle::X(evlp.x_connection().primary_monitor()), } } @@ -631,7 +627,7 @@ impl EventLoop { pub fn run_return(&mut self, callback: F) where - F: FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: FnMut(crate::event::Event<'_, T>, &RootELW, &mut ControlFlow), { match *self { EventLoop::Wayland(ref mut evlp) => evlp.run_return(callback), @@ -641,7 +637,7 @@ impl EventLoop { pub fn run(self, callback: F) -> ! where - F: 'static + FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: 'static + FnMut(crate::event::Event<'_, T>, &RootELW, &mut ControlFlow), { match self { EventLoop::Wayland(evlp) => evlp.run(callback), @@ -682,12 +678,12 @@ impl EventLoopWindowTarget { } fn sticky_exit_callback( - evt: Event, + evt: Event<'_, T>, target: &RootELW, control_flow: &mut ControlFlow, callback: &mut F, ) where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { // make ControlFlow::Exit sticky by providing a dummy // control flow reference if it is already Exit. diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index 7ba2c59a..c538e77f 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -2,11 +2,16 @@ use std::{ cell::RefCell, collections::VecDeque, fmt, + io::ErrorKind, rc::Rc, sync::{Arc, Mutex}, - time::Instant, + time::{Duration, Instant}, }; +use mio::{Events, Poll, PollOpt, Ready, Token}; + +use mio_extras::channel::{channel, Receiver, SendError, Sender}; + use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{ zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, }; @@ -21,15 +26,17 @@ use smithay_client_toolkit::reexports::client::protocol::{ }; use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - event::ModifiersState, + dpi::{LogicalSize, PhysicalPosition, PhysicalSize}, + event::{ + DeviceEvent, DeviceId as RootDeviceId, Event, ModifiersState, StartCause, WindowEvent, + }, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::platform::{ - sticky_exit_callback, MonitorHandle as PlatformMonitorHandle, - VideoMode as PlatformVideoMode, + sticky_exit_callback, DeviceId as PlatformDeviceId, MonitorHandle as PlatformMonitorHandle, + VideoMode as PlatformVideoMode, WindowId as PlatformWindowId, }, - window::CursorIcon, + window::{CursorIcon, WindowId as RootWindowId}, }; use super::{window::WindowStore, DeviceId, WindowId}; @@ -43,43 +50,37 @@ use smithay_client_toolkit::{ Environment, }; -pub struct WindowEventsSink { - buffer: VecDeque>, +const KBD_TOKEN: Token = Token(0); +const USER_TOKEN: Token = Token(1); +const EVQ_TOKEN: Token = Token(2); + +#[derive(Clone)] +pub struct EventsSink { + sender: Sender>, } -impl WindowEventsSink { - pub fn new() -> WindowEventsSink { - WindowEventsSink { - buffer: VecDeque::new(), - } +impl EventsSink { + pub fn new(sender: Sender>) -> EventsSink { + EventsSink { sender } } - pub fn send_event(&mut self, evt: crate::event::Event) { - self.buffer.push_back(evt); + pub fn send_event(&self, event: Event<'static, ()>) { + self.sender.send(event).unwrap() } - pub fn send_window_event(&mut self, evt: crate::event::WindowEvent, wid: WindowId) { - self.buffer.push_back(crate::event::Event::WindowEvent { - event: evt, - window_id: crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid)), + pub fn send_device_event(&self, event: DeviceEvent, device_id: DeviceId) { + self.send_event(Event::DeviceEvent { + event, + device_id: RootDeviceId(PlatformDeviceId::Wayland(device_id)), }); } - pub fn send_device_event(&mut self, evt: crate::event::DeviceEvent, dev_id: DeviceId) { - self.buffer.push_back(crate::event::Event::DeviceEvent { - event: evt, - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(dev_id)), + pub fn send_window_event(&self, event: WindowEvent<'static>, window_id: WindowId) { + self.send_event(Event::WindowEvent { + event, + window_id: RootWindowId(PlatformWindowId::Wayland(window_id)), }); } - - fn empty_with(&mut self, mut callback: F) - where - F: FnMut(crate::event::Event), - { - for evt in self.buffer.drain(..) { - callback(evt) - } - } } pub struct CursorManager { @@ -230,21 +231,17 @@ impl CursorManager { } pub struct EventLoop { - // The loop - inner_loop: ::calloop::EventLoop<()>, + // Poll instance + poll: Poll, // The wayland display pub display: Arc, // The output manager pub outputs: OutputMgr, - // Our sink, shared with some handlers, buffering the events - sink: Arc>>, - pending_user_events: Rc>>, // The cursor manager cursor_manager: Arc>, - // Utility for grabbing the cursor and changing visibility - _user_source: ::calloop::Source<::calloop::channel::Channel>, - user_sender: ::calloop::channel::Sender, - _kbd_source: ::calloop::Source<::calloop::channel::Channel>>, + kbd_channel: Receiver>, + user_channel: Receiver, + user_sender: Sender, window_target: RootELW, } @@ -252,12 +249,12 @@ pub struct EventLoop { // // We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. pub struct EventLoopProxy { - user_sender: calloop::channel::Sender, + user_sender: Sender, } pub struct EventLoopWindowTarget { - // The event queue - pub evq: RefCell<::calloop::Source>, + // the event queue + pub evq: RefCell, // The window store pub store: Arc>, // The cursor manager @@ -284,7 +281,7 @@ impl Clone for EventLoopProxy { impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { self.user_sender.send(event).map_err(|e| { - EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e { + EventLoopClosed(if let SendError::Disconnected(x) = e { x } else { unreachable!() @@ -298,33 +295,26 @@ impl EventLoop { let (display, mut event_queue) = Display::connect_to_env()?; let display = Arc::new(display); - let sink = Arc::new(Mutex::new(WindowEventsSink::new())); let store = Arc::new(Mutex::new(WindowStore::new())); let seats = Arc::new(Mutex::new(Vec::new())); - let inner_loop = ::calloop::EventLoop::new().unwrap(); + let poll = Poll::new().unwrap(); - let (kbd_sender, kbd_channel) = ::calloop::channel::channel::>(); - let kbd_sink = sink.clone(); - let kbd_source = inner_loop - .handle() - .insert_source(kbd_channel, move |evt, &mut ()| { - if let ::calloop::channel::Event::Msg(evt) = evt { - let evt = evt.map_nonuser_event().ok().unwrap(); - kbd_sink.lock().unwrap().send_event(evt); - } - }) + let (kbd_sender, kbd_channel) = channel(); + + let sink = EventsSink::new(kbd_sender); + + poll.register(&kbd_channel, KBD_TOKEN, Ready::readable(), PollOpt::level()) .unwrap(); let pointer_constraints_proxy = Arc::new(Mutex::new(None)); let mut seat_manager = SeatManager { - sink: sink.clone(), - relative_pointer_manager_proxy: Rc::new(RefCell::new(None)), - pointer_constraints_proxy: pointer_constraints_proxy.clone(), + sink, store: store.clone(), seats: seats.clone(), - kbd_sender, + relative_pointer_manager_proxy: Rc::new(RefCell::new(None)), + pointer_constraints_proxy: pointer_constraints_proxy.clone(), cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))), }; @@ -404,39 +394,31 @@ impl EventLoop { ) .unwrap(); - let source = inner_loop - .handle() - .insert_source(event_queue, |(), &mut ()| {}) + poll.register(&event_queue, EVQ_TOKEN, Ready::readable(), PollOpt::level()) .unwrap(); - let pending_user_events = Rc::new(RefCell::new(VecDeque::new())); - let pending_user_events2 = pending_user_events.clone(); + let (user_sender, user_channel) = channel(); - let (user_sender, user_channel) = ::calloop::channel::channel(); - - let user_source = inner_loop - .handle() - .insert_source(user_channel, move |evt, &mut ()| { - if let ::calloop::channel::Event::Msg(msg) = evt { - pending_user_events2.borrow_mut().push_back(msg); - } - }) - .unwrap(); + poll.register( + &user_channel, + USER_TOKEN, + Ready::readable(), + PollOpt::level(), + ) + .unwrap(); let cursor_manager_clone = cursor_manager.clone(); Ok(EventLoop { - inner_loop, - sink, - pending_user_events, + poll, display: display.clone(), outputs: env.outputs.clone(), - _user_source: user_source, user_sender, + user_channel, + kbd_channel, cursor_manager, - _kbd_source: kbd_source, window_target: RootELW { p: crate::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget { - evq: RefCell::new(source), + evq: RefCell::new(event_queue), store, env, cursor_manager: cursor_manager_clone, @@ -458,7 +440,7 @@ impl EventLoop { pub fn run(mut self, callback: F) -> ! where - F: 'static + FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: 'static + FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { self.run_return(callback); std::process::exit(0); @@ -466,53 +448,60 @@ impl EventLoop { pub fn run_return(&mut self, mut callback: F) where - F: FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { // send pending events to the server self.display.flush().expect("Wayland connection lost."); let mut control_flow = ControlFlow::default(); - - let sink = self.sink.clone(); - let user_events = self.pending_user_events.clone(); + let mut events = Events::with_capacity(8); callback( - crate::event::Event::NewEvents(crate::event::StartCause::Init), + Event::NewEvents(StartCause::Init), &self.window_target, &mut control_flow, ); loop { - self.post_dispatch_triggers(); + // Read events from the event queue + { + let mut evq = get_target(&self.window_target).evq.borrow_mut(); - // empty buffer of events - { - let mut guard = sink.lock().unwrap(); - guard.empty_with(|evt| { - sticky_exit_callback( - evt, - &self.window_target, - &mut control_flow, - &mut callback, - ); - }); - } - // empty user events - { - let mut guard = user_events.borrow_mut(); - for evt in guard.drain(..) { - sticky_exit_callback( - crate::event::Event::UserEvent(evt), - &self.window_target, - &mut control_flow, - &mut callback, - ); + evq.dispatch_pending() + .expect("failed to dispatch wayland events"); + + if let Some(read) = evq.prepare_read() { + if let Err(e) = read.read_events() { + if e.kind() != ErrorKind::WouldBlock { + panic!("failed to read wayland events: {}", e); + } + } + + evq.dispatch_pending() + .expect("failed to dispatch wayland events"); } } + + self.post_dispatch_triggers(&mut callback, &mut control_flow); + + while let Ok(event) = self.kbd_channel.try_recv() { + let event = event.map_nonuser_event().unwrap(); + sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback); + } + + while let Ok(event) = self.user_channel.try_recv() { + sticky_exit_callback( + Event::UserEvent(event), + &self.window_target, + &mut control_flow, + &mut callback, + ); + } + // send Events cleared { sticky_exit_callback( - crate::event::Event::MainEventsCleared, + Event::MainEventsCleared, &self.window_target, &mut control_flow, &mut callback, @@ -523,7 +512,7 @@ impl EventLoop { { self.redraw_triggers(|wid, window_target| { sticky_exit_callback( - crate::event::Event::RedrawRequested(crate::window::WindowId( + Event::RedrawRequested(crate::window::WindowId( crate::platform_impl::WindowId::Wayland(wid), )), window_target, @@ -536,7 +525,7 @@ impl EventLoop { // send RedrawEventsCleared { sticky_exit_callback( - crate::event::Event::RedrawEventsCleared, + Event::RedrawEventsCleared, &self.window_target, &mut control_flow, &mut callback, @@ -569,24 +558,25 @@ impl EventLoop { ControlFlow::Exit => break, ControlFlow::Poll => { // non-blocking dispatch - self.inner_loop - .dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()) + self.poll + .poll(&mut events, Some(Duration::from_millis(0))) .unwrap(); + events.clear(); + callback( - crate::event::Event::NewEvents(crate::event::StartCause::Poll), + Event::NewEvents(StartCause::Poll), &self.window_target, &mut control_flow, ); } ControlFlow::Wait => { - let timeout = if instant_wakeup { - Some(::std::time::Duration::from_millis(0)) - } else { - None - }; - self.inner_loop.dispatch(timeout, &mut ()).unwrap(); + if !instant_wakeup { + self.poll.poll(&mut events, None).unwrap(); + events.clear(); + } + callback( - crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled { + Event::NewEvents(StartCause::WaitCancelled { start: Instant::now(), requested_resume: None, }), @@ -600,29 +590,27 @@ impl EventLoop { let duration = if deadline > start && !instant_wakeup { deadline - start } else { - ::std::time::Duration::from_millis(0) + Duration::from_millis(0) }; - self.inner_loop.dispatch(Some(duration), &mut ()).unwrap(); + self.poll.poll(&mut events, Some(duration)).unwrap(); + events.clear(); + let now = Instant::now(); if now < deadline { callback( - crate::event::Event::NewEvents( - crate::event::StartCause::WaitCancelled { - start, - requested_resume: Some(deadline), - }, - ), + Event::NewEvents(StartCause::WaitCancelled { + start, + requested_resume: Some(deadline), + }), &self.window_target, &mut control_flow, ); } else { callback( - crate::event::Event::NewEvents( - crate::event::StartCause::ResumeTimeReached { - start, - requested_resume: deadline, - }, - ), + Event::NewEvents(StartCause::ResumeTimeReached { + start, + requested_resume: deadline, + }), &self.window_target, &mut control_flow, ); @@ -631,11 +619,7 @@ impl EventLoop { } } - callback( - crate::event::Event::LoopDestroyed, - &self.window_target, - &mut control_flow, - ); + callback(Event::LoopDestroyed, &self.window_target, &mut control_flow); } pub fn primary_monitor(&self) -> MonitorHandle { @@ -687,12 +671,19 @@ impl EventLoop { ) } - fn post_dispatch_triggers(&mut self) { - let mut sink = self.sink.lock().unwrap(); + fn post_dispatch_triggers(&mut self, mut callback: F, control_flow: &mut ControlFlow) + where + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), + { let window_target = match self.window_target.p { crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt, _ => unreachable!(), }; + + let mut callback = |event: Event<'_, T>| { + sticky_exit_callback(event, &self.window_target, control_flow, &mut callback); + }; + // prune possible dead windows { let mut cleanup_needed = window_target.cleanup_needed.lock().unwrap(); @@ -700,41 +691,67 @@ impl EventLoop { let pruned = window_target.store.lock().unwrap().cleanup(); *cleanup_needed = false; for wid in pruned { - sink.send_window_event(crate::event::WindowEvent::Destroyed, wid); + callback(Event::WindowEvent { + window_id: crate::window::WindowId( + crate::platform_impl::WindowId::Wayland(wid), + ), + event: WindowEvent::Destroyed, + }); } } } // process pending resize/refresh window_target.store.lock().unwrap().for_each(|window| { + let window_id = + crate::window::WindowId(crate::platform_impl::WindowId::Wayland(window.wid)); if let Some(frame) = window.frame { - if let Some(newsize) = window.newsize { - let (w, h) = newsize; + if let Some((w, h)) = window.newsize { // mutter (GNOME Wayland) relies on `set_geometry` to reposition window in case // it overlaps mutter's `bounding box`, so we can't avoid this resize call, // which calls `set_geometry` under the hood, for now. frame.resize(w, h); frame.refresh(); + // Don't send resize event downstream if the new size is identical to the // current one. - if newsize != *window.size { + if (w, h) != *window.size { let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64); - sink.send_window_event( - crate::event::WindowEvent::Resized(logical_size), - window.wid, - ); + let physical_size = logical_size + .to_physical(window.new_dpi.unwrap_or(window.prev_dpi) as f64); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::Resized(physical_size), + }); + *window.size = (w, h); + } + } + + if let Some(dpi) = window.new_dpi { + let dpi = dpi as f64; + let logical_size = LogicalSize::from(*window.size); + let mut new_inner_size = Some(logical_size.to_physical(dpi)); + + callback(Event::WindowEvent { + window_id, + event: WindowEvent::HiDpiFactorChanged { + hidpi_factor: dpi, + new_inner_size: &mut new_inner_size, + }, + }); + + if let Some(new_size) = new_inner_size { + let (w, h) = new_size.to_logical(dpi).into(); + frame.resize(w, h); *window.size = (w, h); } } } - if let Some(dpi) = window.new_dpi { - sink.send_window_event( - crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64), - window.wid, - ); - } if window.closed { - sink.send_window_event(crate::event::WindowEvent::CloseRequested, window.wid); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::CloseRequested, + }); } if let Some(grab_cursor) = window.grab_cursor { @@ -749,21 +766,27 @@ impl EventLoop { } } +fn get_target(target: &RootELW) -> &EventLoopWindowTarget { + match target.p { + crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt, + _ => unreachable!(), + } +} + /* * Wayland protocol implementations */ -struct SeatManager { - sink: Arc>>, +struct SeatManager { + sink: EventsSink, store: Arc>, seats: Arc>>, - kbd_sender: ::calloop::channel::Sender>, relative_pointer_manager_proxy: Rc>>, pointer_constraints_proxy: Arc>>, cursor_manager: Arc>, } -impl SeatManager { +impl SeatManager { fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) { use std::cmp::min; @@ -775,7 +798,6 @@ impl SeatManager { relative_pointer_manager_proxy: self.relative_pointer_manager_proxy.clone(), keyboard: None, touch: None, - kbd_sender: self.kbd_sender.clone(), modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), cursor_manager: self.cursor_manager.clone(), }; @@ -799,10 +821,9 @@ impl SeatManager { } } -struct SeatData { - sink: Arc>>, +struct SeatData { + sink: EventsSink, store: Arc>, - kbd_sender: ::calloop::channel::Sender>, pointer: Option, relative_pointer: Option, relative_pointer_manager_proxy: Rc>>, @@ -812,7 +833,7 @@ struct SeatData { cursor_manager: Arc>, } -impl SeatData { +impl SeatData { fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) { match evt { wl_seat::Event::Name { .. } => (), @@ -858,7 +879,7 @@ impl SeatData { if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() { self.keyboard = Some(super::keyboard::init_keyboard( &seat, - self.kbd_sender.clone(), + self.sink.clone(), self.modifiers_tracker.clone(), )) } @@ -892,7 +913,7 @@ impl SeatData { } } -impl Drop for SeatData { +impl Drop for SeatData { fn drop(&mut self) { if let Some(pointer) = self.pointer.take() { if pointer.as_ref().version() >= 3 { diff --git a/src/platform_impl/linux/wayland/keyboard.rs b/src/platform_impl/linux/wayland/keyboard.rs index 7e0d2e3e..ff61b9e3 100644 --- a/src/platform_impl/linux/wayland/keyboard.rs +++ b/src/platform_impl/linux/wayland/keyboard.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use super::{make_wid, DeviceId}; +use super::{event_loop::EventsSink, make_wid, DeviceId}; use smithay_client_toolkit::{ keyboard::{ self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, @@ -9,12 +9,12 @@ use smithay_client_toolkit::{ }; use crate::event::{ - DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent, + DeviceEvent, ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent, }; pub fn init_keyboard( seat: &wl_seat::WlSeat, - sink: ::calloop::channel::Sender>, + sink: EventsSink, modifiers_tracker: Arc>, ) -> wl_keyboard::WlKeyboard { // { variables to be captured by the closures @@ -31,22 +31,12 @@ pub fn init_keyboard( match evt { KbEvent::Enter { surface, .. } => { let wid = make_wid(&surface); - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::Focused(true), - }) - .unwrap(); + my_sink.send_window_event(WindowEvent::Focused(true), wid); *target.lock().unwrap() = Some(wid); } KbEvent::Leave { surface, .. } => { let wid = make_wid(&surface); - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::Focused(false), - }) - .unwrap(); + my_sink.send_window_event(WindowEvent::Focused(false), wid); *target.lock().unwrap() = None; } KbEvent::Key { @@ -63,33 +53,28 @@ pub fn init_keyboard( _ => unreachable!(), }; let vkcode = key_to_vkey(rawkey, keysym); - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::KeyboardInput { - device_id: device_id(), - input: KeyboardInput { - state, - scancode: rawkey, - virtual_keycode: vkcode, - modifiers: modifiers_tracker.lock().unwrap().clone(), - }, - is_synthetic: false, + my_sink.send_window_event( + WindowEvent::KeyboardInput { + device_id: crate::event::DeviceId( + crate::platform_impl::DeviceId::Wayland(DeviceId), + ), + input: KeyboardInput { + state, + scancode: rawkey, + virtual_keycode: vkcode, + modifiers: modifiers_tracker.lock().unwrap().clone(), }, - }) - .unwrap(); + is_synthetic: false, + }, + 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() { - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::ReceivedCharacter(chr), - }) - .unwrap(); + my_sink.send_window_event(WindowEvent::ReceivedCharacter(chr), wid); } } } @@ -102,12 +87,7 @@ pub fn init_keyboard( *modifiers_tracker.lock().unwrap() = modifiers; - my_sink - .send(Event::DeviceEvent { - device_id: device_id(), - event: DeviceEvent::ModifiersChanged(modifiers), - }) - .unwrap(); + my_sink.send_device_event(DeviceEvent::ModifiersChanged(modifiers), DeviceId); } } }, @@ -115,29 +95,24 @@ pub fn init_keyboard( if let Some(wid) = *repeat_target.lock().unwrap() { let state = ElementState::Pressed; let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym); - repeat_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::KeyboardInput { - device_id: device_id(), - input: KeyboardInput { - state, - scancode: repeat_event.rawkey, - virtual_keycode: vkcode, - modifiers: my_modifiers.lock().unwrap().clone(), - }, - is_synthetic: false, + repeat_sink.send_window_event( + WindowEvent::KeyboardInput { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + input: KeyboardInput { + state, + scancode: repeat_event.rawkey, + virtual_keycode: vkcode, + modifiers: my_modifiers.lock().unwrap().clone(), }, - }) - .unwrap(); + is_synthetic: false, + }, + wid, + ); if let Some(txt) = repeat_event.utf8 { for chr in txt.chars() { - repeat_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::ReceivedCharacter(chr), - }) - .unwrap(); + repeat_sink.send_window_event(WindowEvent::ReceivedCharacter(chr), wid); } } } @@ -164,22 +139,12 @@ pub fn init_keyboard( move |evt, _| match evt { wl_keyboard::Event::Enter { surface, .. } => { let wid = make_wid(&surface); - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::Focused(true), - }) - .unwrap(); + my_sink.send_window_event(WindowEvent::Focused(true), wid); target = Some(wid); } wl_keyboard::Event::Leave { surface, .. } => { let wid = make_wid(&surface); - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::Focused(false), - }) - .unwrap(); + my_sink.send_window_event(WindowEvent::Focused(false), wid); target = None; } wl_keyboard::Event::Key { key, state, .. } => { @@ -189,21 +154,21 @@ pub fn init_keyboard( wl_keyboard::KeyState::Released => ElementState::Released, _ => unreachable!(), }; - my_sink - .send(Event::WindowEvent { - window_id: mk_root_wid(wid), - event: WindowEvent::KeyboardInput { - device_id: device_id(), - input: KeyboardInput { - state, - scancode: key, - virtual_keycode: None, - modifiers: ModifiersState::default(), - }, - is_synthetic: false, + my_sink.send_window_event( + WindowEvent::KeyboardInput { + device_id: crate::event::DeviceId( + crate::platform_impl::DeviceId::Wayland(DeviceId), + ), + input: KeyboardInput { + state, + scancode: key, + virtual_keycode: None, + modifiers: ModifiersState::default(), }, - }) - .unwrap(); + is_synthetic: false, + }, + wid, + ); } } _ => (), @@ -412,11 +377,3 @@ impl ModifiersState { m } } - -fn device_id() -> crate::event::DeviceId { - crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId)) -} - -fn mk_root_wid(wid: crate::platform_impl::wayland::WindowId) -> crate::window::WindowId { - crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid)) -} diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 09cd66d1..7fdb3fd1 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -2,10 +2,7 @@ target_os = "netbsd", target_os = "openbsd"))] pub use self::{ - event_loop::{ - EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, VideoMode, - WindowEventsSink, - }, + event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, VideoMode}, window::Window, }; diff --git a/src/platform_impl/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs index d2785bfe..f84264c3 100644 --- a/src/platform_impl/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -6,7 +6,7 @@ use crate::event::{ }; use super::{ - event_loop::{CursorManager, WindowEventsSink}, + event_loop::{CursorManager, EventsSink}, window::WindowStore, DeviceId, }; @@ -28,9 +28,9 @@ use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints: use smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface; -pub fn implement_pointer( +pub fn implement_pointer( seat: &wl_seat::WlSeat, - sink: Arc>>, + sink: EventsSink, store: Arc>, modifiers_tracker: Arc>, cursor_manager: Arc>, @@ -43,7 +43,6 @@ pub fn implement_pointer( pointer.implement_closure( move |evt, pointer| { - let mut sink = sink.lock().unwrap(); let store = store.lock().unwrap(); let mut cursor_manager = cursor_manager.lock().unwrap(); match evt { @@ -242,20 +241,18 @@ pub fn implement_pointer( .unwrap() } -pub fn implement_relative_pointer( - sink: Arc>>, +pub fn implement_relative_pointer( + sink: EventsSink, pointer: &WlPointer, manager: &ZwpRelativePointerManagerV1, ) -> Result { manager.get_relative_pointer(pointer, |rel_pointer| { rel_pointer.implement_closure( - move |evt, _rel_pointer| { - let mut sink = sink.lock().unwrap(); - match evt { - Event::RelativeMotion { dx, dy, .. } => sink - .send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId), - _ => unreachable!(), + move |evt, _rel_pointer| match evt { + Event::RelativeMotion { dx, dy, .. } => { + sink.send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId) } + _ => unreachable!(), }, (), ) diff --git a/src/platform_impl/linux/wayland/touch.rs b/src/platform_impl/linux/wayland/touch.rs index 33644a86..803ea624 100644 --- a/src/platform_impl/linux/wayland/touch.rs +++ b/src/platform_impl/linux/wayland/touch.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex}; use crate::event::{TouchPhase, WindowEvent}; -use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId, WindowId}; +use super::{event_loop::EventsSink, window::WindowStore, DeviceId, WindowId}; use smithay_client_toolkit::reexports::client::protocol::{ wl_seat, @@ -15,16 +15,15 @@ struct TouchPoint { id: i32, } -pub(crate) fn implement_touch( +pub(crate) fn implement_touch( seat: &wl_seat::WlSeat, - sink: Arc>>, + sink: EventsSink, store: Arc>, ) -> WlTouch { let mut pending_ids = Vec::new(); seat.get_touch(|touch| { touch.implement_closure( move |evt, _| { - let mut sink = sink.lock().unwrap(); let store = store.lock().unwrap(); match evt { TouchEvent::Down { diff --git a/src/platform_impl/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs index f6ed3964..2c509bbf 100644 --- a/src/platform_impl/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - dpi::{LogicalPosition, LogicalSize}, + dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, monitor::MonitorHandle as RootMonitorHandle, platform_impl::{ @@ -49,11 +49,7 @@ impl Window { attributes: WindowAttributes, pl_attribs: PlAttributes, ) -> Result { - let (width, height) = attributes.inner_size.map(Into::into).unwrap_or((800, 600)); - // Create the window - let size = Arc::new(Mutex::new((width, height))); - let fullscreen = Arc::new(Mutex::new(false)); - + // Create the surface first to get initial DPI let window_store = evlp.store.clone(); let cursor_manager = evlp.cursor_manager.clone(); let surface = evlp.env.create_surface(move |dpi, surface| { @@ -61,7 +57,18 @@ impl Window { surface.set_buffer_scale(dpi); }); + let dpi = get_dpi_factor(&surface) as f64; + let (width, height) = attributes + .inner_size + .map(|size| size.to_logical(dpi).into()) + .unwrap_or((800, 600)); + + // Create the window + let size = Arc::new(Mutex::new((width, height))); + let fullscreen = Arc::new(Mutex::new(false)); + let window_store = evlp.store.clone(); + let my_surface = surface.clone(); let mut frame = SWindow::::init_from_env( &evlp.env, @@ -137,8 +144,16 @@ impl Window { frame.set_title(attributes.title); // min-max dimensions - frame.set_min_size(attributes.min_inner_size.map(Into::into)); - frame.set_max_size(attributes.max_inner_size.map(Into::into)); + frame.set_min_size( + attributes + .min_inner_size + .map(|size| size.to_logical(dpi).into()), + ); + frame.set_max_size( + attributes + .max_inner_size + .map(|size| size.to_logical(dpi).into()), + ); let kill_switch = Arc::new(Mutex::new(false)); let need_frame_refresh = Arc::new(Mutex::new(true)); @@ -191,22 +206,24 @@ impl Window { } #[inline] - pub fn outer_position(&self) -> Result { + pub fn outer_position(&self) -> Result { Err(NotSupportedError::new()) } #[inline] - pub fn inner_position(&self) -> Result { + pub fn inner_position(&self) -> Result { Err(NotSupportedError::new()) } #[inline] - pub fn set_outer_position(&self, _pos: LogicalPosition) { + pub fn set_outer_position(&self, _pos: Position) { // Not possible with wayland } - pub fn inner_size(&self) -> LogicalSize { - self.size.lock().unwrap().clone().into() + pub fn inner_size(&self) -> PhysicalSize { + let dpi = self.hidpi_factor() as f64; + let size = LogicalSize::from(*self.size.lock().unwrap()); + size.to_physical(dpi) } pub fn request_redraw(&self) { @@ -214,34 +231,39 @@ impl Window { } #[inline] - pub fn outer_size(&self) -> LogicalSize { + pub fn outer_size(&self) -> PhysicalSize { + let dpi = self.hidpi_factor() as f64; let (w, h) = self.size.lock().unwrap().clone(); // let (w, h) = super::wayland_window::add_borders(w as i32, h as i32); - (w, h).into() + let size = LogicalSize::from((w, h)); + size.to_physical(dpi) } #[inline] // NOTE: This will only resize the borders, the contents must be updated by the user - pub fn set_inner_size(&self, size: LogicalSize) { - let (w, h) = size.into(); + pub fn set_inner_size(&self, size: Size) { + let dpi = self.hidpi_factor() as f64; + let (w, h) = size.to_logical(dpi).into(); self.frame.lock().unwrap().resize(w, h); *(self.size.lock().unwrap()) = (w, h); } #[inline] - pub fn set_min_inner_size(&self, dimensions: Option) { + pub fn set_min_inner_size(&self, dimensions: Option) { + let dpi = self.hidpi_factor() as f64; self.frame .lock() .unwrap() - .set_min_size(dimensions.map(Into::into)); + .set_min_size(dimensions.map(|dim| dim.to_logical(dpi).into())); } #[inline] - pub fn set_max_inner_size(&self, dimensions: Option) { + pub fn set_max_inner_size(&self, dimensions: Option) { + let dpi = self.hidpi_factor() as f64; self.frame .lock() .unwrap() - .set_max_size(dimensions.map(Into::into)); + .set_max_size(dimensions.map(|dim| dim.to_logical(dpi).into())); } #[inline] @@ -325,7 +347,7 @@ impl Window { } #[inline] - pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), ExternalError> { + pub fn set_cursor_position(&self, _pos: Position) -> Result<(), ExternalError> { Err(ExternalError::NotSupported(NotSupportedError::new())) } @@ -375,6 +397,7 @@ impl Drop for Window { struct InternalWindow { surface: wl_surface::WlSurface, + // TODO: CONVERT TO LogicalSizes newsize: Option<(u32, u32)>, size: Arc>, need_refresh: Arc>, @@ -395,6 +418,7 @@ pub struct WindowStore { pub struct WindowStoreForEach<'a> { pub newsize: Option<(u32, u32)>, pub size: &'a mut (u32, u32), + pub prev_dpi: i32, pub new_dpi: Option, pub closed: bool, pub grab_cursor: Option, @@ -460,6 +484,7 @@ impl WindowStore { f(WindowStoreForEach { newsize: window.newsize.take(), size: &mut *(window.size.lock().unwrap()), + prev_dpi: window.current_dpi, new_dpi: window.new_dpi, closed: window.closed, grab_cursor: window.cursor_grab_changed.lock().unwrap().take(), diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 162b09df..884a7ea1 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc, slice}; +use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; use libc::{c_char, c_int, c_long, c_uint, c_ulong}; @@ -11,7 +11,7 @@ use super::{ use util::modifiers::{ModifierKeyState, ModifierKeymap}; use crate::{ - dpi::{LogicalPosition, LogicalSize}, + dpi::{PhysicalPosition, PhysicalSize}, event::{ DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent, }, @@ -45,7 +45,7 @@ impl EventProcessor { fn with_window(&self, window_id: ffi::Window, callback: F) -> Option where - F: Fn(&UnownedWindow) -> Ret, + F: Fn(&Arc) -> Ret, { let mut deleted = false; let window_id = WindowId(window_id); @@ -59,7 +59,7 @@ impl EventProcessor { deleted = arc.is_none(); arc }) - .map(|window| callback(&*window)); + .map(|window| callback(&window)); if deleted { // Garbage collection wt.windows.borrow_mut().remove(&window_id); @@ -107,7 +107,7 @@ impl EventProcessor { pub(super) fn process_event(&mut self, xev: &mut ffi::XEvent, mut callback: F) where - F: FnMut(Event), + F: FnMut(Event<'_, T>), { let wt = get_xtarget(&self.target); // XFilterEvent tells us when an event has been discarded by the input method. @@ -321,16 +321,11 @@ impl EventProcessor { } ffi::ConfigureNotify => { - #[derive(Debug, Default)] - struct Events { - resized: Option, - moved: Option, - dpi_changed: Option, - } - let xev: &ffi::XConfigureEvent = xev.as_ref(); let xwindow = xev.window; - let events = self.with_window(xwindow, |window| { + let window_id = mkwid(xwindow); + + if let Some(window) = self.with_window(xwindow, Arc::clone) { // So apparently... // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent @@ -344,7 +339,6 @@ impl EventProcessor { let new_inner_size = (xev.width as u32, xev.height as u32); let new_inner_position = (xev.x as i32, xev.y as i32); - let mut monitor = window.current_monitor(); // This must be done *before* locking! let mut shared_state_lock = window.shared_state.lock(); let (mut resized, moved) = { @@ -374,8 +368,6 @@ impl EventProcessor { (resized, moved) }; - let mut events = Events::default(); - let new_outer_position = if moved || shared_state_lock.position.is_none() { // We need to convert client area position to window position. let frame_extents = shared_state_lock @@ -392,9 +384,10 @@ impl EventProcessor { .inner_pos_to_outer(new_inner_position.0, new_inner_position.1); shared_state_lock.position = Some(outer); if moved { - let logical_position = - LogicalPosition::from_physical(outer, monitor.hidpi_factor); - events.moved = Some(WindowEvent::Moved(logical_position)); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::Moved(outer.into()), + }); } outer } else { @@ -406,36 +399,46 @@ impl EventProcessor { // resizing by dragging across monitors *without* dropping the window. let (width, height) = shared_state_lock .dpi_adjusted - .unwrap_or_else(|| (xev.width as f64, xev.height as f64)); + .unwrap_or_else(|| (xev.width as u32, xev.height as u32)); let last_hidpi_factor = shared_state_lock.last_monitor.hidpi_factor; let new_hidpi_factor = { let window_rect = util::AaRect::new(new_outer_position, new_inner_size); - let new_monitor = wt.xconn.get_monitor_for_window(Some(window_rect)); + let monitor = wt.xconn.get_monitor_for_window(Some(window_rect)); - if new_monitor.is_dummy() { + if monitor.is_dummy() { // Avoid updating monitor using a dummy monitor handle last_hidpi_factor } else { - monitor = new_monitor; shared_state_lock.last_monitor = monitor.clone(); monitor.hidpi_factor } }; if last_hidpi_factor != new_hidpi_factor { - events.dpi_changed = - Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor)); - let (new_width, new_height, flusher) = window.adjust_for_dpi( + let (new_width, new_height) = window.adjust_for_dpi( last_hidpi_factor, new_hidpi_factor, width, height, ); - flusher.queue(); - shared_state_lock.dpi_adjusted = Some((new_width, new_height)); - // if the DPI factor changed, force a resize event to ensure the logical - // size is computed with the right DPI factor - resized = true; + + let mut new_inner_size = Some(PhysicalSize::new(new_width, new_height)); + + callback(Event::WindowEvent { + window_id, + event: WindowEvent::HiDpiFactorChanged { + hidpi_factor: new_hidpi_factor, + new_inner_size: &mut new_inner_size, + }, + }); + + if let Some(new_size) = new_inner_size { + window.set_inner_size_physical(new_size.width, new_size.height); + shared_state_lock.dpi_adjusted = Some(new_size.into()); + // if the DPI factor changed, force a resize event to ensure the logical + // size is computed with the right DPI factor + resized = true; + } } } @@ -444,44 +447,19 @@ impl EventProcessor { // WMs constrain the window size, making the resize fail. This would cause an endless stream of // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU. if let Some(adjusted_size) = shared_state_lock.dpi_adjusted { - let rounded_size = ( - adjusted_size.0.round() as u32, - adjusted_size.1.round() as u32, - ); - if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) { + if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) { // When this finally happens, the event will not be synthetic. shared_state_lock.dpi_adjusted = None; } else { - unsafe { - (wt.xconn.xlib.XResizeWindow)( - wt.xconn.display, - xwindow, - rounded_size.0 as c_uint, - rounded_size.1 as c_uint, - ); - } + window.set_inner_size_physical(adjusted_size.0, adjusted_size.1); } } if resized { - let logical_size = - LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor); - events.resized = Some(WindowEvent::Resized(logical_size)); - } - - events - }); - - if let Some(events) = events { - let window_id = mkwid(xwindow); - if let Some(event) = events.dpi_changed { - callback(Event::WindowEvent { window_id, event }); - } - if let Some(event) = events.resized { - callback(Event::WindowEvent { window_id, event }); - } - if let Some(event) = events.moved { - callback(Event::WindowEvent { window_id, event }); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::Resized(new_inner_size.into()), + }); } } } @@ -728,24 +706,17 @@ impl EventProcessor { util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) }); if cursor_moved == Some(true) { - let dpi_factor = - self.with_window(xev.event, |window| window.hidpi_factor()); - if let Some(dpi_factor) = dpi_factor { - let position = LogicalPosition::from_physical( - (xev.event_x as f64, xev.event_y as f64), - dpi_factor, - ); - callback(Event::WindowEvent { - window_id, - event: CursorMoved { - device_id, - position, - modifiers, - }, - }); - } else { - return; - } + let position = + PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); + + callback(Event::WindowEvent { + window_id, + event: CursorMoved { + device_id, + position, + modifiers, + }, + }); } else if cursor_moved.is_none() { return; } @@ -836,18 +807,14 @@ impl EventProcessor { } } - if let Some(dpi_factor) = - self.with_window(xev.event, |window| window.hidpi_factor()) - { + if self.window_exists(xev.event) { callback(Event::WindowEvent { window_id, event: CursorEntered { device_id }, }); - let position = LogicalPosition::from_physical( - (xev.event_x as f64, xev.event_y as f64), - dpi_factor, - ); + let position = + PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); // The mods field on this event isn't actually populated, so query the // pointer device. In the future, we can likely remove this round-trip by @@ -890,11 +857,6 @@ impl EventProcessor { ffi::XI_FocusIn => { let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; - let dpi_factor = - match self.with_window(xev.event, |window| window.hidpi_factor()) { - Some(dpi_factor) => dpi_factor, - None => return, - }; let window_id = mkwid(xev.event); wt.ime @@ -920,10 +882,9 @@ impl EventProcessor { .map(|device| device.attachment) .unwrap_or(2); - let position = LogicalPosition::from_physical( - (xev.event_x as f64, xev.event_y as f64), - dpi_factor, - ); + let position = + PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); + callback(Event::WindowEvent { window_id, event: CursorMoved { @@ -966,15 +927,11 @@ impl EventProcessor { ffi::XI_TouchEnd => TouchPhase::Ended, _ => unreachable!(), }; - let dpi_factor = - self.with_window(xev.event, |window| window.hidpi_factor()); - if let Some(dpi_factor) = dpi_factor { + if self.window_exists(xev.event) { let id = xev.detail as u64; let modifiers = self.device_mod_state.modifiers(); - let location = LogicalPosition::from_physical( - (xev.event_x as f64, xev.event_y as f64), - dpi_factor, - ); + let location = + PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); // Mouse cursor position changes when touch events are received. // Only the first concurrently active touch ID moves the mouse cursor. @@ -1163,21 +1120,40 @@ impl EventProcessor { // Check if the window is on this monitor let monitor = window.current_monitor(); if monitor.name == new_monitor.name { - callback(Event::WindowEvent { - window_id: mkwid(window_id.0), - event: WindowEvent::HiDpiFactorChanged( - new_monitor.hidpi_factor, - ), - }); let (width, height) = window.inner_size_physical(); - let (_, _, flusher) = window.adjust_for_dpi( - prev_monitor.hidpi_factor, - new_monitor.hidpi_factor, - width as f64, - height as f64, + let (new_width, new_height) = window + .adjust_for_dpi( + prev_monitor.hidpi_factor, + new_monitor.hidpi_factor, + width, + height, + ); + + let window_id = crate::window::WindowId( + crate::platform_impl::platform::WindowId::X( + *window_id, + ), ); - flusher.queue(); + let mut new_inner_size = Some( + PhysicalSize::new(new_width, new_height), + ); + + callback(Event::WindowEvent { + window_id, + event: WindowEvent::HiDpiFactorChanged { + hidpi_factor: new_monitor.hidpi_factor, + new_inner_size: &mut new_inner_size, + }, + }); + + if let Some(new_size) = new_inner_size { + let (new_width, new_height) = + new_size.into(); + window.set_inner_size_physical( + new_width, new_height, + ); + } } } } @@ -1203,7 +1179,7 @@ impl EventProcessor { state: ElementState, callback: &mut F, ) where - F: FnMut(Event), + F: FnMut(Event<'_, T>), { let wt = get_xtarget(&self.target); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index a3bdea9c..f0275bf9 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -24,7 +24,7 @@ pub use self::{ use std::{ cell::RefCell, - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet}, ffi::CStr, mem::{self, MaybeUninit}, ops::Deref, @@ -37,6 +37,10 @@ use std::{ use libc::{self, setlocale, LC_CTYPE}; +use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token}; + +use mio_extras::channel::{channel, Receiver, SendError, Sender}; + use self::{ dnd::{Dnd, DndState}, event_processor::EventProcessor, @@ -51,6 +55,9 @@ use crate::{ window::WindowAttributes, }; +const X_TOKEN: Token = Token(0); +const USER_TOKEN: Token = Token(1); + pub struct EventLoopWindowTarget { xconn: Arc, wm_delete_window: ffi::Atom, @@ -64,18 +71,15 @@ pub struct EventLoopWindowTarget { } pub struct EventLoop { - inner_loop: ::calloop::EventLoop<()>, - _x11_source: ::calloop::Source<::calloop::generic::Generic<::calloop::generic::EventedRawFd>>, - _user_source: ::calloop::Source<::calloop::channel::Channel>, - pending_user_events: Rc>>, - event_processor: Rc>>, - user_sender: ::calloop::channel::Sender, - pending_events: Rc>>>, - pub(crate) target: Rc>, + poll: Poll, + event_processor: EventProcessor, + user_channel: Receiver, + user_sender: Sender, + target: Rc>, } pub struct EventLoopProxy { - user_sender: ::calloop::channel::Sender, + user_sender: Sender, } impl Clone for EventLoopProxy { @@ -171,28 +175,27 @@ impl EventLoop { _marker: ::std::marker::PhantomData, }); - // A calloop event loop to drive us - let inner_loop = ::calloop::EventLoop::new().unwrap(); + let poll = Poll::new().unwrap(); - // Handle user events - let pending_user_events = Rc::new(RefCell::new(VecDeque::new())); - let pending_user_events2 = pending_user_events.clone(); + let (user_sender, user_channel) = channel(); - let (user_sender, user_channel) = ::calloop::channel::channel(); + poll.register( + &EventedFd(&get_xtarget(&target).xconn.x11_fd), + X_TOKEN, + Ready::readable(), + PollOpt::level(), + ) + .unwrap(); - let _user_source = inner_loop - .handle() - .insert_source(user_channel, move |evt, &mut ()| { - if let ::calloop::channel::Event::Msg(msg) = evt { - pending_user_events2.borrow_mut().push_back(msg); - } - }) - .unwrap(); + poll.register( + &user_channel, + USER_TOKEN, + Ready::readable(), + PollOpt::level(), + ) + .unwrap(); - // Handle X11 events - let pending_events: Rc>> = Default::default(); - - let processor = EventProcessor { + let event_processor = EventProcessor { target: target.clone(), dnd, devices: Default::default(), @@ -212,38 +215,12 @@ impl EventLoop { .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) .queue(); - processor.init_device(ffi::XIAllDevices); - - let processor = Rc::new(RefCell::new(processor)); - let event_processor = processor.clone(); - - // Setup the X11 event source - let mut x11_events = - ::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd); - x11_events.set_interest(::calloop::mio::Ready::readable()); - let _x11_source = inner_loop - .handle() - .insert_source(x11_events, { - let pending_events = pending_events.clone(); - move |evt, &mut ()| { - if evt.readiness.is_readable() { - let mut processor = processor.borrow_mut(); - let mut pending_events = pending_events.borrow_mut(); - let mut pending_redraws = pending_redraws.lock().unwrap(); - - drain_events(&mut processor, &mut pending_events, &mut pending_redraws); - } - } - }) - .unwrap(); + event_processor.init_device(ffi::XIAllDevices); let result = EventLoop { - inner_loop, - pending_events, - _x11_source, - _user_source, + poll, + user_channel, user_sender, - pending_user_events, event_processor, target, }; @@ -261,12 +238,16 @@ impl EventLoop { &self.target } + pub(crate) fn x_connection(&self) -> &Arc { + get_xtarget(&self.target).x_connection() + } + pub fn run_return(&mut self, mut callback: F) where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { let mut control_flow = ControlFlow::default(); - let wt = get_xtarget(&self.target); + let mut events = Events::with_capacity(8); callback( crate::event::Event::NewEvents(crate::event::StartCause::Init), @@ -275,22 +256,16 @@ impl EventLoop { ); loop { - self.drain_events(); + // Process all pending events + self.drain_events(&mut callback, &mut control_flow); - // Empty the event buffer - { - let mut guard = self.pending_events.borrow_mut(); - for evt in guard.drain(..) { - sticky_exit_callback(evt, &self.target, &mut control_flow, &mut callback); - } - } + let wt = get_xtarget(&self.target); // Empty the user event buffer { - let mut guard = self.pending_user_events.borrow_mut(); - for evt in guard.drain(..) { + while let Ok(event) = self.user_channel.try_recv() { sticky_exit_callback( - crate::event::Event::UserEvent(evt), + crate::event::Event::UserEvent(event), &self.target, &mut control_flow, &mut callback, @@ -331,7 +306,7 @@ impl EventLoop { } let start = Instant::now(); - let (mut cause, deadline, mut timeout); + let (mut cause, deadline, timeout); match control_flow { ControlFlow::Exit => break, @@ -362,26 +337,39 @@ impl EventLoop { } } - if self.events_waiting() { - timeout = Some(Duration::from_millis(0)); - } + if self.event_processor.poll() { + // If the XConnection already contains buffered events, we don't + // need to wait for data on the socket. + // However, we still need to check for user events. + self.poll + .poll(&mut events, Some(Duration::from_millis(0))) + .unwrap(); + events.clear(); - self.inner_loop.dispatch(timeout, &mut ()).unwrap(); + callback( + crate::event::Event::NewEvents(cause), + &self.target, + &mut control_flow, + ); + } else { + self.poll.poll(&mut events, timeout).unwrap(); + events.clear(); - if let Some(deadline) = deadline { - if deadline > Instant::now() { + let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline); + + if wait_cancelled { cause = StartCause::WaitCancelled { start, - requested_resume: Some(deadline), + requested_resume: deadline, }; } - } - callback( - crate::event::Event::NewEvents(cause), - &self.target, - &mut control_flow, - ); + callback( + crate::event::Event::NewEvents(cause), + &self.target, + &mut control_flow, + ); + } } callback( @@ -393,44 +381,42 @@ impl EventLoop { pub fn run(mut self, callback: F) -> ! where - F: 'static + FnMut(Event, &RootELW, &mut ControlFlow), + F: 'static + FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { self.run_return(callback); ::std::process::exit(0); } - fn drain_events(&self) { - let mut processor = self.event_processor.borrow_mut(); - let mut pending_events = self.pending_events.borrow_mut(); + fn drain_events(&mut self, callback: &mut F, control_flow: &mut ControlFlow) + where + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), + { + let target = &self.target; + let mut xev = MaybeUninit::uninit(); + let wt = get_xtarget(&self.target); let mut pending_redraws = wt.pending_redraws.lock().unwrap(); - drain_events(&mut processor, &mut pending_events, &mut pending_redraws); - } - - fn events_waiting(&self) -> bool { - !self.pending_events.borrow().is_empty() || self.event_processor.borrow().poll() - } -} - -fn drain_events( - processor: &mut EventProcessor, - pending_events: &mut VecDeque>, - pending_redraws: &mut HashSet, -) { - let mut callback = |event| { - if let Event::RedrawRequested(crate::window::WindowId(super::WindowId::X(wid))) = event { - pending_redraws.insert(wid); - } else { - pending_events.push_back(event); + while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { + let mut xev = unsafe { xev.assume_init() }; + self.event_processor.process_event(&mut xev, |event| { + sticky_exit_callback( + event, + target, + control_flow, + &mut |event, window_target, control_flow| { + if let Event::RedrawRequested(crate::window::WindowId( + super::WindowId::X(wid), + )) = event + { + pending_redraws.insert(wid); + } else { + callback(event, window_target, control_flow); + } + }, + ); + }); } - }; - - // process all pending events - let mut xev = MaybeUninit::uninit(); - while unsafe { processor.poll_one_event(xev.as_mut_ptr()) } { - let mut xev = unsafe { xev.assume_init() }; - processor.process_event(&mut xev, &mut callback); } } @@ -452,7 +438,7 @@ impl EventLoopWindowTarget { impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { self.user_sender.send(event).map_err(|e| { - EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e { + EventLoopClosed(if let SendError::Disconnected(x) = e { x } else { unreachable!() diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index fc12ba0a..0a72875b 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -15,7 +15,7 @@ use libc; use parking_lot::Mutex; use crate::{ - dpi::{LogicalPosition, LogicalSize}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::{ @@ -36,7 +36,7 @@ pub struct SharedState { pub inner_position: Option<(i32, i32)>, pub inner_position_rel_parent: Option<(i32, i32)>, pub last_monitor: X11MonitorHandle, - pub dpi_adjusted: Option<(f64, f64)>, + pub dpi_adjusted: Option<(u32, u32)>, pub fullscreen: Option, // Set when application calls `set_fullscreen` when window is not visible pub desired_fullscreen: Option>, @@ -45,8 +45,8 @@ pub struct SharedState { // Used to restore video mode after exiting fullscreen pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>, pub frame_extents: Option, - pub min_inner_size: Option, - pub max_inner_size: Option, + pub min_inner_size: Option, + pub max_inner_size: Option, pub visibility: Visibility, } @@ -148,8 +148,8 @@ impl UnownedWindow { // by the user, so we have to manually apply the initial constraints let mut dimensions: (u32, u32) = window_attrs .inner_size - .or_else(|| Some((800, 600).into())) .map(|size| size.to_physical(dpi_factor)) + .or_else(|| Some((800, 600).into())) .map(Into::into) .unwrap(); if let Some(max) = max_inner_size { @@ -440,16 +440,6 @@ impl UnownedWindow { .map_err(|x_err| os_error!(OsError::XError(x_err))) } - fn logicalize_coords(&self, (x, y): (i32, i32)) -> LogicalPosition { - let dpi = self.hidpi_factor(); - LogicalPosition::from_physical((x, y), dpi) - } - - fn logicalize_size(&self, (width, height): (u32, u32)) -> LogicalSize { - let dpi = self.hidpi_factor(); - LogicalSize::from_physical((width, height), dpi) - } - fn set_pid(&self) -> Option> { let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") }; let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") }; @@ -951,11 +941,11 @@ impl UnownedWindow { } #[inline] - pub fn outer_position(&self) -> Result { + pub fn outer_position(&self) -> Result { let extents = (*self.shared_state.lock()).frame_extents.clone(); if let Some(extents) = extents { - let logical = self.inner_position().unwrap(); - Ok(extents.inner_pos_to_outer_logical(logical, self.hidpi_factor())) + let (x, y) = self.inner_position_physical(); + Ok(extents.inner_pos_to_outer(x, y).into()) } else { self.update_cached_frame_extents(); self.outer_position() @@ -972,8 +962,8 @@ impl UnownedWindow { } #[inline] - pub fn inner_position(&self) -> Result { - Ok(self.logicalize_coords(self.inner_position_physical())) + pub fn inner_position(&self) -> Result { + Ok(self.inner_position_physical().into()) } pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> { @@ -1002,8 +992,8 @@ impl UnownedWindow { } #[inline] - pub fn set_outer_position(&self, logical_position: LogicalPosition) { - let (x, y) = logical_position.to_physical(self.hidpi_factor()).into(); + pub fn set_outer_position(&self, position: Position) { + let (x, y) = position.to_physical(self.hidpi_factor()).into(); self.set_position_physical(x, y); } @@ -1017,16 +1007,16 @@ impl UnownedWindow { } #[inline] - pub fn inner_size(&self) -> LogicalSize { - self.logicalize_size(self.inner_size_physical()) + pub fn inner_size(&self) -> PhysicalSize { + self.inner_size_physical().into() } #[inline] - pub fn outer_size(&self) -> LogicalSize { + pub fn outer_size(&self) -> PhysicalSize { let extents = self.shared_state.lock().frame_extents.clone(); if let Some(extents) = extents { - let logical = self.inner_size(); - extents.inner_size_to_outer_logical(logical, self.hidpi_factor()) + let (width, height) = self.inner_size_physical(); + extents.inner_size_to_outer(width, height).into() } else { self.update_cached_frame_extents(); self.outer_size() @@ -1047,9 +1037,9 @@ impl UnownedWindow { } #[inline] - pub fn set_inner_size(&self, logical_size: LogicalSize) { + pub fn set_inner_size(&self, size: Size) { let dpi_factor = self.hidpi_factor(); - let (width, height) = logical_size.to_physical(dpi_factor).into(); + let (width, height) = size.to_physical(dpi_factor).into(); self.set_inner_size_physical(width, height); } @@ -1070,10 +1060,10 @@ impl UnownedWindow { } #[inline] - pub fn set_min_inner_size(&self, logical_dimensions: Option) { - self.shared_state.lock().min_inner_size = logical_dimensions; - let physical_dimensions = logical_dimensions - .map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into()); + pub fn set_min_inner_size(&self, dimensions: Option) { + self.shared_state.lock().min_inner_size = dimensions; + let physical_dimensions = + dimensions.map(|dimensions| dimensions.to_physical(self.hidpi_factor()).into()); self.set_min_inner_size_physical(physical_dimensions); } @@ -1083,10 +1073,10 @@ impl UnownedWindow { } #[inline] - pub fn set_max_inner_size(&self, logical_dimensions: Option) { - self.shared_state.lock().max_inner_size = logical_dimensions; - let physical_dimensions = logical_dimensions - .map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into()); + pub fn set_max_inner_size(&self, dimensions: Option) { + self.shared_state.lock().max_inner_size = dimensions; + let physical_dimensions = + dimensions.map(|dimensions| dimensions.to_physical(self.hidpi_factor()).into()); self.set_max_inner_size_physical(physical_dimensions); } @@ -1094,12 +1084,10 @@ impl UnownedWindow { &self, old_dpi_factor: f64, new_dpi_factor: f64, - width: f64, - height: f64, - ) -> (f64, f64, util::Flusher<'_>) { + width: u32, + height: u32, + ) -> (u32, u32) { let scale_factor = new_dpi_factor / old_dpi_factor; - let new_width = width * scale_factor; - let new_height = height * scale_factor; self.update_normal_hints(|normal_hints| { let dpi_adjuster = |(width, height): (u32, u32)| -> (u32, u32) { let new_width = width as f64 * scale_factor; @@ -1116,15 +1104,11 @@ impl UnownedWindow { normal_hints.set_base_size(base_size); }) .expect("Failed to update normal hints"); - unsafe { - (self.xconn.xlib.XResizeWindow)( - self.xconn.display, - self.xwindow, - new_width.round() as c_uint, - new_height.round() as c_uint, - ); - } - (new_width, new_height, util::Flusher::new(&self.xconn)) + + let new_width = (width as f64 * scale_factor).round() as u32; + let new_height = (height as f64 * scale_factor).round() as u32; + + (new_width, new_height) } pub fn set_resizable(&self, resizable: bool) { @@ -1136,25 +1120,25 @@ impl UnownedWindow { return; } - let (logical_min, logical_max) = if resizable { + let (min_size, max_size) = if resizable { let shared_state_lock = self.shared_state.lock(); ( shared_state_lock.min_inner_size, shared_state_lock.max_inner_size, ) } else { - let window_size = Some(self.inner_size()); + let window_size = Some(Size::from(self.inner_size())); (window_size.clone(), window_size) }; self.set_maximizable_inner(resizable).queue(); let dpi_factor = self.hidpi_factor(); - let min_inner_size = logical_min - .map(|logical_size| logical_size.to_physical(dpi_factor)) + let min_inner_size = min_size + .map(|size| size.to_physical(dpi_factor)) .map(Into::into); - let max_inner_size = logical_max - .map(|logical_size| logical_size.to_physical(dpi_factor)) + let max_inner_size = max_size + .map(|size| size.to_physical(dpi_factor)) .map(Into::into); self.update_normal_hints(|normal_hints| { normal_hints.set_min_size(min_inner_size); @@ -1289,11 +1273,8 @@ impl UnownedWindow { } #[inline] - pub fn set_cursor_position( - &self, - logical_position: LogicalPosition, - ) -> Result<(), ExternalError> { - let (x, y) = logical_position.to_physical(self.hidpi_factor()).into(); + pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { + let (x, y) = position.to_physical(self.hidpi_factor()).into(); self.set_cursor_position_physical(x, y) } @@ -1305,8 +1286,8 @@ impl UnownedWindow { } #[inline] - pub fn set_ime_position(&self, logical_spot: LogicalPosition) { - let (x, y) = logical_spot.to_physical(self.hidpi_factor()).into(); + pub fn set_ime_position(&self, spot: Position) { + let (x, y) = spot.to_physical(self.hidpi_factor()).into(); self.set_ime_position_physical(x, y); } diff --git a/src/window.rs b/src/window.rs index b0ab30a3..b97b9c6a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -459,7 +459,7 @@ impl Window { self.window.set_outer_position(position.into()) } - /// Returns the logical size of the window's client area. + /// Returns the physical size of the window's client area. /// /// The client area is the content of the window, excluding the title bar and borders. /// @@ -488,7 +488,7 @@ impl Window { self.window.set_inner_size(size.into()) } - /// Returns the logical size of the entire window. + /// Returns the physical size of the entire window. /// /// These dimensions include the title bar and borders. If you don't want that (and you usually don't), /// use `inner_size` instead.