diff --git a/examples/open_window.rs b/examples/open_window.rs index a764453..aa3b59c 100644 --- a/examples/open_window.rs +++ b/examples/open_window.rs @@ -1,4 +1,4 @@ -use baseview::{Event, Window, WindowHandler}; +use baseview::{Event, Window, WindowHandler, WindowSize, WindowScalePolicy}; struct MyProgram {} @@ -25,9 +25,13 @@ impl WindowHandler for MyProgram { fn main() { let window_open_options = baseview::WindowOpenOptions { title: "baseview".into(), - logical_size: (512, 512), - resize: baseview::WindowResize::None, - scale: 1.0, + size: WindowSize::MinMaxLogical { + initial_size: (512, 512), + min_size: (200, 200), + max_size: (1024, 1024), + keep_aspect: false, + }, + scale: WindowScalePolicy::TrySystemScaleFactor, parent: baseview::Parent::None, }; diff --git a/src/event.rs b/src/event.rs index abaa010..5ada391 100644 --- a/src/event.rs +++ b/src/event.rs @@ -41,18 +41,28 @@ pub enum ScrollDelta { pub struct MouseClick { pub button: MouseButton, pub click_count: usize, - pub x: i32, - pub y: i32, + /// The logical X coordinate of the mouse position + logical_x: i32, + /// The logical Y coordinate of the mouse position + logical_y: i32, + /// The physical X coordinate of the mouse position + physical_x: i32, + /// The physical Y coordinate of the mouse position + physical_y: i32, } #[derive(Debug, Clone, Copy, PartialEq)] pub enum MouseEvent { /// The mouse cursor was moved CursorMoved { - /// The X coordinate of the mouse position - x: i32, - /// The Y coordinate of the mouse position - y: i32, + /// The logical X coordinate of the mouse position + logical_x: i32, + /// The logical Y coordinate of the mouse position + logical_y: i32, + /// The physical X coordinate of the mouse position + physical_x: i32, + /// The physical Y coordinate of the mouse position + physical_y: i32, }, /// A mouse button was pressed. @@ -74,7 +84,7 @@ pub enum MouseEvent { CursorLeft, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug)] pub enum WindowEvent { Resized(WindowInfo), Focused, diff --git a/src/lib.rs b/src/lib.rs index c0531ff..65fdc14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,11 +19,14 @@ mod event; mod keyboard; mod mouse_cursor; mod window_info; +mod window_open_options; pub use event::*; pub use keyboard::*; pub use mouse_cursor::MouseCursor; pub use window_info::WindowInfo; +pub use window_open_options::*; +#[derive(Debug)] pub enum Parent { None, AsIfParented, @@ -32,30 +35,6 @@ pub enum Parent { unsafe impl Send for Parent {} -pub enum WindowResize { - None, - MinMax { - min_logical_size: (u32, u32), - max_logical_size: (u32, u32), - keep_aspect: bool, - }, -} - -pub struct WindowOpenOptions { - pub title: String, - - /// The logical width and height of the window - pub logical_size: (u32, u32), - - pub resize: WindowResize, - - /// 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<()>, } diff --git a/src/window_info.rs b/src/window_info.rs index 1e46be1..373936e 100644 --- a/src/window_info.rs +++ b/src/window_info.rs @@ -1,5 +1,5 @@ /// The info about the window -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone)] pub struct WindowInfo { /// The physical the width of the window physical_width: u32, @@ -66,11 +66,19 @@ impl WindowInfo { self.scale } - /// Convert physical mouse coordinates to logical coordinates + /// Convert physical coordinates to logical coordinates pub fn physical_to_logical(&self, x: f64, y: f64) -> (f64, f64) { ( x * self.scale_recip, y * self.scale_recip ) } + + /// Convert logicalcoordinates to physical coordinates + pub fn logical_to_physical(&self, x: f64, y: f64) -> (f64, f64) { + ( + x * self.scale, + y * self.scale + ) + } } \ No newline at end of file diff --git a/src/window_open_options.rs b/src/window_open_options.rs new file mode 100644 index 0000000..1ebafa1 --- /dev/null +++ b/src/window_open_options.rs @@ -0,0 +1,74 @@ +use crate::{WindowInfo, Parent}; + +/// The size of the window +#[derive(Debug)] +pub enum WindowSize { + /// Use logical width and height + Logical(u32, u32), + /// Use physical width and height + Physical(u32, u32), + /// Use minimum and maximum logical width and height + MinMaxLogical { + /// The initial logical width and height + initial_size: (u32, u32), + /// The minimum logical width and height + min_size: (u32, u32), + /// The maximum logical width and height + max_size: (u32, u32), + /// Whether to keep the aspect ratio when resizing (true), or not (false) + keep_aspect: bool, + }, + /// Use minimum and maximum physical width and height + MinMaxPhysical { + /// The initial physical width and height + initial_size: (u32, u32), + /// The minimum physical width and height + min_size: (u32, u32), + /// The maximum physical width and height + max_size: (u32, u32), + /// Whether to keep the aspect ratio when resizing (true), or not (false) + keep_aspect: bool, + }, +} + +/// The dpi scaling policy of the window +#[derive(Debug)] +pub enum WindowScalePolicy { + /// Try using the system scale factor + TrySystemScaleFactor, + /// Try using the system scale factor in addition to the given scale factor + TrySystemScaleFactorTimes(f64), + /// Use the given scale factor + UseScaleFactor(f64), + /// No scaling + NoScaling, +} + +/// The options for opening a new window +#[derive(Debug)] +pub struct WindowOpenOptions { + pub title: String, + + /// The size information about the window + pub size: WindowSize, + + /// The scaling of the window + pub scale: WindowScalePolicy, + + pub parent: Parent, +} + +impl WindowOpenOptions { + pub(crate) fn window_info_from_scale(&self, scale: f64) -> WindowInfo { + match self.size { + WindowSize::Logical(w, h) => WindowInfo::from_logical_size(w, h, scale), + WindowSize::Physical(w, h) => WindowInfo::from_physical_size(w, h, scale), + WindowSize::MinMaxLogical { initial_size, .. } => { + WindowInfo::from_logical_size(initial_size.0, initial_size.1, scale) + }, + WindowSize::MinMaxPhysical { initial_size, .. } => { + WindowInfo::from_logical_size(initial_size.0, initial_size.1, scale) + } + } + } +} \ No newline at end of file diff --git a/src/x11/window.rs b/src/x11/window.rs index e8baf7a..0c97c46 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -12,7 +12,8 @@ use raw_window_handle::{ use super::XcbConnection; use crate::{ Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent, - WindowHandle, WindowHandler, WindowInfo, WindowOpenOptions, WindowOpenResult, WindowResize, + WindowHandle, WindowHandler, WindowInfo, WindowOpenOptions, WindowOpenResult, + WindowScalePolicy, }; pub struct Window { @@ -79,14 +80,16 @@ impl Window { ], ); - let scaling = xcb_connection.get_scaling().unwrap_or(1.0) * options.scale; + let scaling = match options.scale { + WindowScalePolicy::TrySystemScaleFactor => + xcb_connection.get_scaling().unwrap_or(1.0), + WindowScalePolicy::TrySystemScaleFactorTimes(user_scale) => + xcb_connection.get_scaling().unwrap_or(1.0) * user_scale, + WindowScalePolicy::UseScaleFactor(user_scale) => user_scale, + WindowScalePolicy::NoScaling => 1.0, + }; - let (logical_width, logical_height) = options.logical_size; - let window_info = WindowInfo::from_logical_size( - logical_width, - logical_height, - scaling - ); + let window_info = options.window_info_from_scale(scaling); let window_id = xcb_connection.conn.generate_id(); xcb::create_window( @@ -127,42 +130,6 @@ impl Window { title.as_bytes(), ); - match options.resize { - WindowResize::MinMax { min_logical_size, max_logical_size, keep_aspect } => { - let min_physical_width = (min_logical_size.0 as f64 * scaling).round() as i32; - let min_physical_height = (min_logical_size.1 as f64 * scaling).round() as i32; - let max_physical_width = (max_logical_size.0 as f64 * scaling).round() as i32; - let max_physical_height = (max_logical_size.1 as f64 * scaling).round() as i32; - - let size_hints = if keep_aspect { - xcb_util::icccm::SizeHints::empty() - .min_size(min_physical_width, min_physical_height) - .max_size(max_physical_width, max_physical_height) - .aspect( - (min_physical_width, min_physical_height), - (max_physical_width, max_physical_height), - ) - .build() - } else { - xcb_util::icccm::SizeHints::empty() - .min_size(min_physical_width, min_physical_height) - .max_size(max_physical_width, max_physical_height) - .build() - }; - - xcb_connection.atoms.wm_normal_hints - .map(|wm_normal_hints| { - xcb_util::icccm::set_wm_size_hints( - &xcb_connection.conn, - window_id, - wm_normal_hints, - &size_hints, - ); - }); - } - _ => {} - } - xcb_connection.atoms.wm_protocols .zip(xcb_connection.atoms.wm_delete_window) .map(|(wm_protocols, wm_delete_window)| { @@ -173,6 +140,8 @@ impl Window { &[wm_delete_window], ); }); + + xcb_connection.set_resize_policy(window_id, &options.size, scaling); xcb_connection.conn.flush(); @@ -363,8 +332,10 @@ impl Window { handler.on_event( self, Event::Mouse(MouseEvent::CursorMoved { - x: logical_x.round() as i32, - y: logical_y.round() as i32, + logical_x: logical_x.round() as i32, + logical_y: logical_y.round() as i32, + physical_x: event.event_x() as i32, + physical_y: event.event_y() as i32, }), ); } diff --git a/src/x11/xcb_connection.rs b/src/x11/xcb_connection.rs index db2113a..d8a32ac 100644 --- a/src/x11/xcb_connection.rs +++ b/src/x11/xcb_connection.rs @@ -5,7 +5,7 @@ use std::ffi::{CStr, CString}; use std::collections::HashMap; -use crate::MouseCursor; +use crate::{MouseCursor, WindowSize}; use super::cursor; @@ -154,4 +154,69 @@ impl XcbConnection { .entry(cursor) .or_insert_with(|| cursor::get_xcursor(dpy, cursor)) } + + pub fn set_resize_policy(&self, window_id: u32, size: &WindowSize, scale: f64) { + match size { + WindowSize::MinMaxLogical { min_size, max_size, keep_aspect, .. } => { + let min_physical_width = (min_size.0 as f64 * scale).round() as i32; + let min_physical_height = (min_size.1 as f64 * scale).round() as i32; + let max_physical_width = (max_size.0 as f64 * scale).round() as i32; + let max_physical_height = (max_size.1 as f64 * scale).round() as i32; + + let size_hints = if *keep_aspect { + xcb_util::icccm::SizeHints::empty() + .min_size(min_physical_width, min_physical_height) + .max_size(max_physical_width, max_physical_height) + .aspect( + (min_physical_width, min_physical_height), + (max_physical_width, max_physical_height), + ) + .build() + } else { + xcb_util::icccm::SizeHints::empty() + .min_size(min_physical_width, min_physical_height) + .max_size(max_physical_width, max_physical_height) + .build() + }; + + self.atoms.wm_normal_hints + .map(|wm_normal_hints| { + xcb_util::icccm::set_wm_size_hints( + &self.conn, + window_id, + wm_normal_hints, + &size_hints, + ); + }); + } + WindowSize::MinMaxPhysical { min_size, max_size, keep_aspect, .. } => { + let size_hints = if *keep_aspect { + xcb_util::icccm::SizeHints::empty() + .min_size(min_size.0 as i32, min_size.1 as i32) + .max_size(max_size.0 as i32, max_size.1 as i32) + .aspect( + (min_size.0 as i32, min_size.1 as i32), + (max_size.0 as i32, max_size.1 as i32), + ) + .build() + } else { + xcb_util::icccm::SizeHints::empty() + .min_size(min_size.0 as i32, min_size.1 as i32) + .max_size(max_size.0 as i32, max_size.1 as i32) + .build() + }; + + self.atoms.wm_normal_hints + .map(|wm_normal_hints| { + xcb_util::icccm::set_wm_size_hints( + &self.conn, + window_id, + wm_normal_hints, + &size_hints, + ); + }); + } + _ => {} + } + } }