1
0
Fork 0

add UI scaling support

This commit is contained in:
Billy Messenger 2020-10-15 13:17:03 -05:00
parent d5c3ba0bb0
commit 2bf229575f
5 changed files with 135 additions and 52 deletions

View file

@ -27,9 +27,10 @@ fn main() {
title: "baseview".into(), title: "baseview".into(),
width: 512, width: 512,
height: 512, height: 512,
scale: 1.0,
parent: baseview::Parent::None, parent: baseview::Parent::None,
}; };
let handle = Window::open::<MyProgram>(window_open_options); let (handle, _window_info) = Window::open::<MyProgram>(window_open_options).unwrap();
handle.app_run_blocking(); handle.app_run_blocking();
} }

View file

@ -1,3 +1,5 @@
use crate::WindowInfo;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeyboardEvent { pub enum KeyboardEvent {
KeyPressed(u32), KeyPressed(u32),
@ -72,14 +74,7 @@ pub enum MouseEvent {
CursorLeft, CursorLeft,
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct WindowInfo {
pub width: u32,
pub height: u32,
pub scale: f64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WindowEvent { pub enum WindowEvent {
Resized(WindowInfo), Resized(WindowInfo),
Focused, Focused,

View file

@ -18,9 +18,11 @@ pub use macos::*;
mod event; mod event;
mod keyboard; mod keyboard;
mod mouse_cursor; mod mouse_cursor;
mod window_info;
pub use event::*; pub use event::*;
pub use keyboard::*; pub use keyboard::*;
pub use mouse_cursor::MouseCursor; pub use mouse_cursor::MouseCursor;
pub use window_info::WindowInfo;
pub enum Parent { pub enum Parent {
None, None,
@ -33,12 +35,30 @@ unsafe impl Send for Parent {}
pub struct WindowOpenOptions { pub struct WindowOpenOptions {
pub title: String, pub title: String,
pub width: usize, /// The logical width of the window
pub height: usize, 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 parent: Parent,
} }
pub struct WindowHandle {
thread: std::thread::JoinHandle<()>,
}
impl WindowHandle {
pub fn app_run_blocking(self) {
let _ = self.thread.join();
}
}
type WindowOpenResult = Result<WindowInfo, ()>;
pub trait WindowHandler { pub trait WindowHandler {
type Message; type Message;

76
src/window_info.rs Normal file
View file

@ -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
)
}
}

View file

@ -12,7 +12,7 @@ use raw_window_handle::{
use super::XcbConnection; use super::XcbConnection;
use crate::{ use crate::{
Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent, Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent,
WindowHandler,WindowInfo, WindowOpenOptions, WindowHandler,WindowInfo, WindowOpenOptions, WindowOpenResult, WindowHandle,
}; };
pub struct Window { pub struct Window {
@ -24,24 +24,11 @@ pub struct Window {
frame_interval: Duration, frame_interval: Duration,
event_loop_running: bool, 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 { impl Window {
pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> WindowHandle { pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> Result<(WindowHandle, WindowInfo), ()> {
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1); let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
@ -51,9 +38,9 @@ impl Window {
}); });
// FIXME: placeholder types for returning errors in the future // 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<H: WindowHandler>( fn window_thread<H: WindowHandler>(
@ -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(); let window_id = xcb_connection.conn.generate_id();
xcb::create_window( xcb::create_window(
&xcb_connection.conn, &xcb_connection.conn,
@ -100,8 +91,8 @@ impl Window {
parent_id, parent_id,
0, // x coordinate of the new window 0, // x coordinate of the new window
0, // y coordinate of the new window 0, // y coordinate of the new window
options.width as u16, // window width window_info.physical_width() as u16, // window width
options.height as u16, // window height window_info.physical_height() as u16, // window height
0, // window border 0, // window border
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(), screen.root_visual(),
@ -144,14 +135,6 @@ impl Window {
xcb_connection.conn.flush(); 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 { let mut window = Self {
xcb_connection, xcb_connection,
window_id, window_id,
@ -161,15 +144,15 @@ impl Window {
frame_interval: Duration::from_millis(15), frame_interval: Duration::from_millis(15),
event_loop_running: false, event_loop_running: false,
new_size: None new_physical_size: None,
}; };
let mut handler = H::build(&mut window); let mut handler = H::build(&mut window);
let _ = tx.send(Ok(())); let _ = tx.send(Ok(window_info));
window.run_event_loop(&mut handler); window.run_event_loop(&mut handler);
Ok(()) Ok(window_info)
} }
pub fn window_info(&self) -> &WindowInfo { 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 // 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 // window is resized, and we need to batch those together and just send one resize event
// when they've all been coalesced. // 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() { while let Some(event) = self.xcb_connection.conn.poll_for_event() {
self.handle_xcb_event(handler, event); self.handle_xcb_event(handler, event);
} }
if let Some((width, height)) = self.new_size.take() { if let Some((width, height)) = self.new_physical_size.take() {
self.window_info.width = width; self.window_info = WindowInfo::from_physical_size(
self.window_info.height = height; width,
height,
self.window_info.scale()
);
handler.on_event(self, Event::Window( handler.on_event(self, Event::Window(
WindowEvent::Resized(self.window_info) WindowEvent::Resized(self.window_info)
@ -312,11 +298,11 @@ impl Window {
xcb::CONFIGURE_NOTIFY => { xcb::CONFIGURE_NOTIFY => {
let event = unsafe { xcb::cast_event::<xcb::ConfigureNotifyEvent>(&event) }; let event = unsafe { xcb::cast_event::<xcb::ConfigureNotifyEvent>(&event) };
let new_size = (event.width() as u32, event.height() as u32); let new_physical_size = (event.width() as u32, event.height() as u32);
let cur_size = (self.window_info.width, self.window_info.height); let cur_physical_size = (self.window_info.physical_width(), self.window_info.physical_height());
if self.new_size.is_some() || new_size != cur_size { if self.new_physical_size.is_some() || new_physical_size != cur_physical_size {
self.new_size = Some(new_size); self.new_physical_size = Some(new_physical_size);
} }
} }
@ -328,11 +314,16 @@ impl Window {
let detail = event.detail(); let detail = event.detail();
if detail != 4 && detail != 5 { 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( handler.on_event(
self, self,
Event::Mouse(MouseEvent::CursorMoved { Event::Mouse(MouseEvent::CursorMoved {
x: event.event_x() as i32, x: logical_x.round() as i32,
y: event.event_y() as i32, y: logical_y.round() as i32,
}), }),
); );
} }