From 2bf229575f986176314a9605457f9d244238e33a Mon Sep 17 00:00:00 2001 From: Billy Messenger Date: Thu, 15 Oct 2020 13:17:03 -0500 Subject: [PATCH] add UI scaling support --- examples/open_window.rs | 3 +- src/event.rs | 11 ++---- src/lib.rs | 24 +++++++++++-- src/window_info.rs | 76 +++++++++++++++++++++++++++++++++++++++++ src/x11/window.rs | 73 +++++++++++++++++---------------------- 5 files changed, 135 insertions(+), 52 deletions(-) create mode 100644 src/window_info.rs diff --git a/examples/open_window.rs b/examples/open_window.rs index 911a039..0f67440 100644 --- a/examples/open_window.rs +++ b/examples/open_window.rs @@ -27,9 +27,10 @@ fn main() { title: "baseview".into(), width: 512, height: 512, + scale: 1.0, parent: baseview::Parent::None, }; - let handle = Window::open::(window_open_options); + let (handle, _window_info) = Window::open::(window_open_options).unwrap(); handle.app_run_blocking(); } diff --git a/src/event.rs b/src/event.rs index ef4f4c7..87c4889 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,3 +1,5 @@ +use crate::WindowInfo; + #[derive(Debug, Clone, Copy, PartialEq)] pub enum KeyboardEvent { KeyPressed(u32), @@ -72,14 +74,7 @@ pub enum MouseEvent { CursorLeft, } -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct WindowInfo { - pub width: u32, - pub height: u32, - pub scale: f64, -} - -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum WindowEvent { Resized(WindowInfo), Focused, diff --git a/src/lib.rs b/src/lib.rs index fb84d94..f0ed8b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,11 @@ pub use macos::*; mod event; mod keyboard; mod mouse_cursor; +mod window_info; pub use event::*; pub use keyboard::*; pub use mouse_cursor::MouseCursor; +pub use window_info::WindowInfo; pub enum Parent { None, @@ -33,12 +35,30 @@ unsafe impl Send for Parent {} pub struct WindowOpenOptions { pub title: String, - pub width: usize, - pub height: usize, + /// The logical width of the window + pub width: u32, + /// The logical height of the window + pub height: u32, + + /// The dpi scale factor. This will used in conjunction with the dpi scale + /// factor of the system. + pub scale: f64, pub parent: Parent, } +pub struct WindowHandle { + thread: std::thread::JoinHandle<()>, +} + +impl WindowHandle { + pub fn app_run_blocking(self) { + let _ = self.thread.join(); + } +} + +type WindowOpenResult = Result; + pub trait WindowHandler { type Message; diff --git a/src/window_info.rs b/src/window_info.rs new file mode 100644 index 0000000..1e46be1 --- /dev/null +++ b/src/window_info.rs @@ -0,0 +1,76 @@ +/// The info about the window +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct WindowInfo { + /// The physical the width of the window + physical_width: u32, + /// The physical height of the window + physical_height: u32, + /// The logical width of the window + logical_width: u32, + /// The logical height of the window + logical_height: u32, + /// The scale factor + scale: f64, + scale_recip: f64, +} + +impl WindowInfo { + pub fn from_logical_size(logical_width: u32, logical_height: u32, scale: f64) -> Self { + let scale_recip = 1.0 / scale; + + Self { + physical_width: (logical_width as f64 * scale).round() as u32, + physical_height: (logical_height as f64 * scale).round() as u32, + logical_width, + logical_height, + scale, + scale_recip, + } + } + + pub fn from_physical_size(physical_width: u32, physical_height: u32, scale: f64) -> Self { + let scale_recip = 1.0 / scale; + + Self { + physical_width, + physical_height, + logical_width: (physical_width as f64 * scale_recip).round() as u32, + logical_height: (physical_height as f64 * scale_recip).round() as u32, + scale, + scale_recip, + } + } + + /// The physical width of the window + pub fn physical_width(&self) -> u32 { + self.physical_width + } + + /// The physical height of the window + pub fn physical_height(&self) -> u32 { + self.physical_height + } + + /// The logical width of the window + pub fn logical_width(&self) -> u32 { + self.logical_width + } + + /// The logical height of the window + pub fn logical_height(&self) -> u32 { + self.logical_height + } + + /// The scale factor of the window + pub fn scale(&self) -> f64 { + self.scale + } + + /// Convert physical mouse coordinates to logical coordinates + pub fn physical_to_logical(&self, x: f64, y: f64) -> (f64, f64) { + ( + x * self.scale_recip, + y * self.scale_recip + ) + } +} \ No newline at end of file diff --git a/src/x11/window.rs b/src/x11/window.rs index c7cd911..507c67d 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -12,7 +12,7 @@ use raw_window_handle::{ use super::XcbConnection; use crate::{ Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent, - WindowHandler,WindowInfo, WindowOpenOptions, + WindowHandler,WindowInfo, WindowOpenOptions, WindowOpenResult, WindowHandle, }; pub struct Window { @@ -24,24 +24,11 @@ pub struct Window { frame_interval: Duration, event_loop_running: bool, - new_size: Option<(u32, u32)> + new_physical_size: Option<(u32, u32)> } -// FIXME: move to outer crate context -pub struct WindowHandle { - thread: std::thread::JoinHandle<()>, -} - -impl WindowHandle { - pub fn app_run_blocking(self) { - let _ = self.thread.join(); - } -} - -type WindowOpenResult = Result<(), ()>; - impl Window { - pub fn open(options: WindowOpenOptions) -> WindowHandle { + pub fn open(options: WindowOpenOptions) -> Result<(WindowHandle, WindowInfo), ()> { let (tx, rx) = mpsc::sync_channel::(1); let thread = thread::spawn(move || { @@ -51,9 +38,9 @@ impl Window { }); // FIXME: placeholder types for returning errors in the future - let _ = rx.recv(); + let window_info = rx.recv().unwrap().unwrap(); - WindowHandle { thread } + Ok((WindowHandle { thread }, window_info)) } fn window_thread( @@ -92,6 +79,10 @@ impl Window { ], ); + let scaling = xcb_connection.get_scaling().unwrap_or(1.0) * options.scale; + + let window_info = WindowInfo::from_logical_size(options.width, options.height, scaling); + let window_id = xcb_connection.conn.generate_id(); xcb::create_window( &xcb_connection.conn, @@ -100,8 +91,8 @@ impl Window { parent_id, 0, // x coordinate of the new window 0, // y coordinate of the new window - options.width as u16, // window width - options.height as u16, // window height + window_info.physical_width() as u16, // window width + window_info.physical_height() as u16, // window height 0, // window border xcb::WINDOW_CLASS_INPUT_OUTPUT as u16, screen.root_visual(), @@ -144,14 +135,6 @@ impl Window { xcb_connection.conn.flush(); - let scaling = xcb_connection.get_scaling().unwrap_or(1.0); - - let window_info = WindowInfo { - width: options.width as u32, - height: options.height as u32, - scale: scaling, - }; - let mut window = Self { xcb_connection, window_id, @@ -161,15 +144,15 @@ impl Window { frame_interval: Duration::from_millis(15), event_loop_running: false, - new_size: None + new_physical_size: None, }; let mut handler = H::build(&mut window); - let _ = tx.send(Ok(())); + let _ = tx.send(Ok(window_info)); window.run_event_loop(&mut handler); - Ok(()) + Ok(window_info) } pub fn window_info(&self) -> &WindowInfo { @@ -201,15 +184,18 @@ impl Window { // the X server has a tendency to send spurious/extraneous configure notify events when a // window is resized, and we need to batch those together and just send one resize event // when they've all been coalesced. - self.new_size = None; + self.new_physical_size = None; while let Some(event) = self.xcb_connection.conn.poll_for_event() { self.handle_xcb_event(handler, event); } - if let Some((width, height)) = self.new_size.take() { - self.window_info.width = width; - self.window_info.height = height; + if let Some((width, height)) = self.new_physical_size.take() { + self.window_info = WindowInfo::from_physical_size( + width, + height, + self.window_info.scale() + ); handler.on_event(self, Event::Window( WindowEvent::Resized(self.window_info) @@ -312,11 +298,11 @@ impl Window { xcb::CONFIGURE_NOTIFY => { let event = unsafe { xcb::cast_event::(&event) }; - let new_size = (event.width() as u32, event.height() as u32); - let cur_size = (self.window_info.width, self.window_info.height); + let new_physical_size = (event.width() as u32, event.height() as u32); + let cur_physical_size = (self.window_info.physical_width(), self.window_info.physical_height()); - if self.new_size.is_some() || new_size != cur_size { - self.new_size = Some(new_size); + if self.new_physical_size.is_some() || new_physical_size != cur_physical_size { + self.new_physical_size = Some(new_physical_size); } } @@ -328,11 +314,16 @@ impl Window { let detail = event.detail(); if detail != 4 && detail != 5 { + let (logical_x, logical_y) = self.window_info.physical_to_logical( + event.event_x() as f64, + event.event_y() as f64 + ); + handler.on_event( self, Event::Mouse(MouseEvent::CursorMoved { - x: event.event_x() as i32, - y: event.event_y() as i32, + x: logical_x.round() as i32, + y: logical_y.round() as i32, }), ); }