From 559510833810d0a23d4054f4d2b1ae4ce4091157 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 19 Jun 2026 07:22:10 +0200 Subject: [PATCH 1/9] wip --- Cargo.toml | 1 + examples/render_femtovg/src/main.rs | 39 ++++---- src/context.rs | 15 ++- src/event.rs | 16 ++-- src/lib.rs | 3 +- src/platform/x11/drag_n_drop.rs | 37 +++---- src/platform/x11/event_loop.rs | 33 +++---- src/platform/x11/window.rs | 29 +++--- src/platform/x11/window_shared.rs | 28 ++++-- src/platform/x11/xcb_connection.rs | 26 +---- src/window_info.rs | 144 ---------------------------- src/window_open_options.rs | 13 +-- 12 files changed, 112 insertions(+), 272 deletions(-) delete mode 100644 src/window_info.rs diff --git a/Cargo.toml b/Cargo.toml index 60b3a6fb..0a35da15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ uuid = [] # Deprecated; this feature was provided by the now unneeded uuid depen [dependencies] keyboard-types = { version = "0.6.1", default-features = false } raw-window-handle = "0.6.2" +dpi = "0.1.2" [target.'cfg(target_os="linux")'.dependencies] x11rb = { version = "0.13.2", features = ["cursor", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false } diff --git a/examples/render_femtovg/src/main.rs b/examples/render_femtovg/src/main.rs index 7ab894d5..6ecc9299 100644 --- a/examples/render_femtovg/src/main.rs +++ b/examples/render_femtovg/src/main.rs @@ -1,7 +1,8 @@ +use baseview::dpi::{LogicalSize, PhysicalPosition}; use baseview::gl::{GlConfig, GlContext}; use baseview::{ - Event, EventStatus, MouseEvent, PhyPoint, Size, Window, WindowContext, WindowEvent, - WindowHandler, WindowInfo, WindowOpenOptions, + Event, EventStatus, MouseEvent, Window, WindowContext, WindowEvent, WindowHandler, + WindowOpenOptions, }; use femtovg::renderer::OpenGl; use femtovg::{Canvas, Color}; @@ -11,8 +12,7 @@ struct FemtovgExample { window_context: WindowContext, gl_context: GlContext, canvas: RefCell>, - current_size: Cell, - current_mouse_position: Cell, + current_mouse_position: Cell>, damaged: Cell, } @@ -25,18 +25,17 @@ impl FemtovgExample { unsafe { OpenGl::new_from_function(|s| gl_context.get_proc_address(s)) }.unwrap(); let mut canvas = Canvas::new(renderer).unwrap(); - // TODO: get actual window width - canvas.set_size(512, 512, 1.0); + let size = window_context.size(); + + canvas.set_size(size.width, size.height, window_context.scale_factor() as f32); unsafe { gl_context.make_not_current() }; Self { gl_context, window_context, canvas: canvas.into(), - current_size: WindowInfo::from_logical_size(Size { width: 512.0, height: 512.0 }, 1.0) - .into(), - current_mouse_position: PhyPoint { x: 256, y: 256 }.into(), damaged: true.into(), + current_mouse_position: Cell::new(PhysicalPosition::default()), } } } @@ -67,10 +66,12 @@ impl WindowHandler for FemtovgExample { Color::rgbf(0., 0.3, 0.9), ); + let mouse_position = self.current_mouse_position.get().cast::(); + // Make smol orange rectangle canvas.clear_rect( - (self.current_mouse_position.get().x - 15).clamp(0, screen_width as i32 - 30) as u32, - (self.current_mouse_position.get().y - 15).clamp(0, screen_height as i32 - 30) as u32, + (mouse_position.x - 15).clamp(0, screen_width as i32 - 30) as u32, + (mouse_position.y - 15).clamp(0, screen_height as i32 - 30) as u32, 30, 30, Color::rgbf(0.9, 0.3, 0.), @@ -85,14 +86,8 @@ impl WindowHandler for FemtovgExample { fn on_event(&self, event: Event) -> EventStatus { match event { - Event::Window(WindowEvent::Resized(size)) => { - let phy_size = size.physical_size(); - self.current_size.set(size); - self.canvas.borrow_mut().set_size( - phy_size.width, - phy_size.height, - size.scale() as f32, - ); + Event::Window(WindowEvent::Resized { size, scale_factor }) => { + self.canvas.borrow_mut().set_size(size.width, size.height, scale_factor as f32); self.damaged.set(true); } Event::Mouse( @@ -101,8 +96,8 @@ impl WindowHandler for FemtovgExample { | MouseEvent::DragMoved { position, .. } | MouseEvent::DragDropped { position, .. }, ) => { - self.current_mouse_position.set(position.to_physical(&self.current_size.get())); - if self.current_mouse_position.get().y > 400 && !self.window_context.has_focus() { + self.current_mouse_position.set(position); + if position.y > 400. && !self.window_context.has_focus() { self.window_context.focus() } self.damaged.set(true); @@ -117,7 +112,7 @@ impl WindowHandler for FemtovgExample { fn main() { let window_open_options = WindowOpenOptions::new() .with_title("Femtovg on Baseview") - .with_size(512.0, 512.0) + .with_size(LogicalSize::new(512, 512)) .with_gl_config(GlConfig { alpha_bits: 8, ..GlConfig::default() }); Window::open_blocking(window_open_options, FemtovgExample::new); diff --git a/src/context.rs b/src/context.rs index 3cd18569..0b5bb23e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,5 @@ -use crate::{platform, MouseCursor, Size}; +use crate::{platform, MouseCursor}; +use dpi::{PhysicalPosition, PhysicalSize, Pixel, Size}; use raw_window_handle::{ DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle, }; @@ -29,8 +30,16 @@ impl WindowContext { self.inner.focus(); } - pub fn resize(&self, size: Size) { - self.inner.resize(size); + pub fn resize(&self, size: impl Into) { + self.inner.resize(size.into()); + } + + pub fn scale_factor(&self) -> f64 { + self.inner.scale_factor() + } + + pub fn size(&self) -> PhysicalSize { + self.inner.size() } #[cfg(feature = "opengl")] diff --git a/src/event.rs b/src/event.rs index 64431a09..262f25de 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,8 +1,6 @@ -use std::path::PathBuf; - +use dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use keyboard_types::{KeyboardEvent, Modifiers}; - -use crate::{Point, WindowInfo}; +use std::path::PathBuf; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum MouseButton { @@ -39,7 +37,7 @@ pub enum MouseEvent { /// The mouse cursor was moved CursorMoved { /// The logical coordinates of the mouse position - position: Point, + position: PhysicalPosition, /// The modifiers that were held down just before the event. modifiers: Modifiers, }, @@ -80,7 +78,7 @@ pub enum MouseEvent { DragEntered { /// The logical coordinates of the mouse position - position: Point, + position: PhysicalPosition, /// The modifiers that were held down just before the event. modifiers: Modifiers, /// Data being dragged @@ -89,7 +87,7 @@ pub enum MouseEvent { DragMoved { /// The logical coordinates of the mouse position - position: Point, + position: PhysicalPosition, /// The modifiers that were held down just before the event. modifiers: Modifiers, /// Data being dragged @@ -100,7 +98,7 @@ pub enum MouseEvent { DragDropped { /// The logical coordinates of the mouse position - position: Point, + position: PhysicalPosition, /// The modifiers that were held down just before the event. modifiers: Modifiers, /// Data being dragged @@ -110,7 +108,7 @@ pub enum MouseEvent { #[derive(Debug, Clone)] pub enum WindowEvent { - Resized(WindowInfo), + Resized { size: PhysicalSize, scale_factor: f64 }, Focused, Unfocused, WillClose, diff --git a/src/lib.rs b/src/lib.rs index 15190678..f1f53fab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,6 @@ mod handler; mod keyboard; mod mouse_cursor; mod window; -mod window_info; mod window_open_options; pub(crate) mod platform; @@ -15,11 +14,11 @@ pub mod gl; pub use clipboard::*; pub use context::WindowContext; +pub use dpi; pub use event::*; pub use handler::WindowHandler; pub use mouse_cursor::MouseCursor; pub use window::*; -pub use window_info::*; pub use window_open_options::*; pub(crate) mod wrappers; diff --git a/src/platform/x11/drag_n_drop.rs b/src/platform/x11/drag_n_drop.rs index 6cf2b950..84405467 100644 --- a/src/platform/x11/drag_n_drop.rs +++ b/src/platform/x11/drag_n_drop.rs @@ -1,4 +1,6 @@ +use dpi::PhysicalPosition; use keyboard_types::Modifiers; +use percent_encoding::percent_decode; use std::error::Error; use std::fmt::{Display, Formatter}; use std::{ @@ -6,8 +8,6 @@ use std::{ path::{Path, PathBuf}, str::Utf8Error, }; - -use percent_encoding::percent_decode; use x11rb::connection::Connection; use x11rb::errors::ReplyError; use x11rb::protocol::xproto::{Atom, ClientMessageEvent, SelectionNotifyEvent, Timestamp}; @@ -20,7 +20,7 @@ use x11rb::{ use super::xcb_connection::{Atoms, GetPropertyError}; use super::*; use crate::handler::WindowHandler; -use crate::{DropData, Event, MouseEvent, PhyPoint}; +use crate::{DropData, Event, MouseEvent}; use DragNDropState::*; /// The Drag-N-Drop session state of a `baseview` X11 window, for which it is the target. @@ -55,7 +55,7 @@ pub(crate) enum DragNDropState { /// The source window the current drag session originates from. source_window: xproto::Window, /// The current position of the pointer, from the last received position event. - position: PhyPoint, + position: PhysicalPosition, /// The timestamp of the event we made the selection request from. /// /// This is either from the first position event, or from the drop event if it arrived first. @@ -82,7 +82,7 @@ pub(crate) enum DragNDropState { protocol_version: u8, /// The source window the current drag session originates from. source_window: xproto::Window, - position: PhyPoint, + position: PhysicalPosition, data: DropData, requested_action: Option, }, @@ -187,7 +187,7 @@ impl DragNDropState { protocol_version: *protocol_version, requested_at: timestamp, source_window: event_source_window, - position: PhyPoint::new(0, 0), + position: PhysicalPosition::new(0, 0), requested_action, dropped: false, }; @@ -224,7 +224,7 @@ impl DragNDropState { }; handler.on_event(Event::Mouse(MouseEvent::DragMoved { - position: position.to_logical(&window.window_info.get()), + position: position.cast(), data: data.clone(), // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), @@ -330,7 +330,7 @@ impl DragNDropState { source_window: event_source_window, // We don't have usable position data. Maybe we'll receive a position later, // but otherwise this will have to do. - position: PhyPoint::new(0, 0), + position: PhysicalPosition::new(0, 0), requested_action: Some(DndAction::Private), dropped: true, }; @@ -372,7 +372,7 @@ impl DragNDropState { send_finished_event(event_source_window, window, requested_action); handler.on_event(Event::Mouse(MouseEvent::DragDropped { - position: position.to_logical(&window.window_info.get()), + position: position.cast(), data, // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), @@ -426,8 +426,6 @@ impl DragNDropState { // TODO: Log warning } Ok(data) => { - let logical_position = position.to_logical(&window.window_info.get()); - // Inform the source that we are (still) accepting the drop. // Handle the case where the user already dropped, but we only received the data later. @@ -438,14 +436,14 @@ impl DragNDropState { // Now that we have actual drop data, we can inform the handler about the drag AND drop events. handler.on_event(Event::Mouse(MouseEvent::DragEntered { - position: logical_position, + position: position.cast(), data: data.clone(), // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), })); handler.on_event(Event::Mouse(MouseEvent::DragDropped { - position: logical_position, + position: position.cast(), data: data.clone(), // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), @@ -466,7 +464,7 @@ impl DragNDropState { // Now that we have actual drop data, we can inform the handler about the drag event. handler.on_event(Event::Mouse(MouseEvent::DragEntered { - position: logical_position, + position: position.cast(), data, // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), @@ -582,19 +580,22 @@ fn decode_xy(data: u32) -> (u16, u16) { fn translate_root_coordinates( window: &WindowInner, x: u16, y: u16, -) -> Result { +) -> Result, ReplyError> { let root_id = window.connection.screen().root; + let x = x.try_into().unwrap_or(i16::MAX); + let y = y.try_into().unwrap_or(i16::MAX); + if root_id == window.window_id.get() { - return Ok(PhyPoint::new(x as i32, y as i32)); + return Ok(PhysicalPosition::new(x, y)); } let reply = window .connection .conn - .translate_coordinates(root_id, window.window_id.get(), x as i16, y as i16)? + .translate_coordinates(root_id, window.window_id.get(), x, y)? .reply()?; - Ok(PhyPoint::new(reply.dst_x as i32, reply.dst_y as i32)) + Ok(PhysicalPosition::new(reply.dst_x, reply.dst_y)) } fn fetch_dnd_data(window: &WindowInner) -> Result> { diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index 4d14d7fd..3a7748bb 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -4,10 +4,8 @@ use super::*; use crate::wrappers::connection_poller::{ConnectionPoller, PollStatus}; use crate::wrappers::xkbcommon::XkbcommonState; -use crate::{ - Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler, - WindowInfo, -}; +use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler}; +use dpi::{PhysicalPosition, PhysicalSize}; use std::error::Error; use std::rc::Rc; use std::time::{Duration, Instant}; @@ -19,7 +17,7 @@ pub(crate) struct EventLoop { window: Rc, parent_handle: Option, - new_physical_size: Option, + new_physical_size: Option>, frame_interval: Duration, event_loop_running: bool, @@ -57,13 +55,14 @@ impl EventLoop { } if let Some(size) = self.new_physical_size.take() { - self.window - .window_info - .set(WindowInfo::from_physical_size(size, self.window.window_info.get().scale())); + self.window.window_size.set(size); - let window_info = self.window.window_info.get(); + let scale_factor = self.window.scaling_factor.get(); - self.handle_event(Event::Window(WindowEvent::Resized(window_info))); + self.handle_event(Event::Window(WindowEvent::Resized { + size: size.cast(), + scale_factor, + })); } Ok(()) @@ -198,10 +197,10 @@ impl EventLoop { } XEvent::ConfigureNotify(event) => { - let new_physical_size = PhySize::new(event.width as u32, event.height as u32); + let new_physical_size = PhysicalSize::new(event.width, event.height); if self.new_physical_size.is_some() - || new_physical_size != self.window.window_info.get().physical_size() + || new_physical_size != self.window.window_size.get() { self.new_physical_size = Some(new_physical_size); } @@ -211,11 +210,10 @@ impl EventLoop { // mouse //// XEvent::MotionNotify(event) => { - let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32); - let logical_pos = physical_pos.to_logical(&self.window.window_info.get()); + let physical_pos = PhysicalPosition::new(event.event_x, event.event_y); self.handle_event(Event::Mouse(MouseEvent::CursorMoved { - position: logical_pos, + position: physical_pos.cast(), modifiers: key_mods(event.state), })); } @@ -224,10 +222,9 @@ impl EventLoop { self.handle_event(Event::Mouse(MouseEvent::CursorEntered)); // since no `MOTION_NOTIFY` event is generated when `ENTER_NOTIFY` is generated, // we generate a CursorMoved as well, so the mouse position from here isn't lost - let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32); - let logical_pos = physical_pos.to_logical(&self.window.window_info.get()); + let physical_pos = PhysicalPosition::new(event.event_x, event.event_y); self.handle_event(Event::Mouse(MouseEvent::CursorMoved { - position: logical_pos, + position: physical_pos.cast(), modifiers: key_mods(event.state), })); } diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 832fa65d..96842f8d 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,3 +1,5 @@ +use dpi::Pixel; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use std::cell::Cell; use std::error::Error; use std::num::NonZero; @@ -7,8 +9,6 @@ use std::sync::mpsc; use std::sync::Arc; use std::thread::{self, JoinHandle}; -use raw_window_handle::{HasWindowHandle, RawWindowHandle}; - use x11rb::connection::Connection; use x11rb::protocol::xproto::{ AtomEnum, ConnectionExt, CreateGCAux, CreateWindowAux, EventMask, PropMode, WindowClass, @@ -19,7 +19,7 @@ use super::X11Connection; use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; use crate::context::WindowContext; use crate::platform::x11::window_shared::WindowInner; -use crate::{Event, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy}; +use crate::{Event, WindowEvent, WindowHandler, WindowOpenOptions, WindowScalePolicy}; pub struct WindowHandle { window_id: Option>, @@ -144,7 +144,7 @@ impl Window { WindowScalePolicy::ScaleFactor(scale) => scale, }; - let window_info = WindowInfo::from_logical_size(options.size, scaling); + let physical_size = options.size.to_physical(scaling); #[cfg(feature = "opengl")] let visual_info = @@ -161,11 +161,11 @@ impl Window { visual_info.visual_depth, window_id.get(), parent_id, - 0, // x coordinate of the new window - 0, // y coordinate of the new window - window_info.physical_size().width as u16, // window width - window_info.physical_size().height as u16, // window height - 0, // window border + 0, // x coordinate of the new window + 0, // y coordinate of the new window + physical_size.width, // window width + physical_size.height, // window height + 0, // window border WindowClass::INPUT_OUTPUT, visual_info.visual_id, &CreateWindowAux::new() @@ -218,9 +218,6 @@ impl Window { xcb_connection.conn.flush()?; let xcb_connection = Rc::new(xcb_connection); - // TODO: These APIs could use a couple tweaks now that everything is internal and there is - // no error handling anymore at this point. Everything is more or less unchanged - // compared to when raw-gl-context was a separate crate. #[cfg(feature = "opengl")] let gl_context = visual_info.fb_config.map(|fb_config| { use std::ffi::c_ulong; @@ -238,7 +235,8 @@ impl Window { let inner = Rc::new(WindowInner::new( xcb_connection, window_id, - window_info, + physical_size, + scaling, #[cfg(feature = "opengl")] gl_context, )); @@ -247,7 +245,10 @@ impl Window { // Send an initial window resized event so the user is alerted of // the correct dpi scaling. - handler.on_event(Event::Window(WindowEvent::Resized(window_info))); + handler.on_event(Event::Window(WindowEvent::Resized { + size: physical_size.cast(), + scale_factor: scaling, + })); let _ = tx.send(Ok(window_id)); diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index 2ba0bcb1..c24818d5 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -1,5 +1,6 @@ use crate::platform::X11Connection; -use crate::{MouseCursor, Size, WindowInfo}; +use crate::MouseCursor; +use dpi::{PhysicalPosition, PhysicalSize, Size}; use raw_window_handle::{DisplayHandle, XcbWindowHandle}; use std::cell::Cell; use std::num::NonZero; @@ -17,7 +18,8 @@ pub(crate) struct WindowInner { pub(crate) connection: Rc, pub(crate) window_id: NonZero, - pub(crate) window_info: Cell, + pub(crate) scaling_factor: Cell, + pub(crate) window_size: Cell>, mouse_cursor: Cell, pub(crate) close_requested: Cell, @@ -26,13 +28,14 @@ pub(crate) struct WindowInner { impl WindowInner { pub(crate) fn new( - connection: Rc, window_id: NonZero, window_info: WindowInfo, - #[cfg(feature = "opengl")] gl_context: Option, + connection: Rc, window_id: NonZero, window_size: PhysicalSize, + scale_factor: f64, #[cfg(feature = "opengl")] gl_context: Option, ) -> Self { Self { connection, window_id, - window_info: window_info.into(), + window_size: window_size.into(), + scaling_factor: scale_factor.into(), mouse_cursor: MouseCursor::default().into(), close_requested: false.into(), @@ -79,14 +82,13 @@ impl WindowInner { } pub fn resize(&self, size: Size) { - let scaling = self.window_info.get().scale(); - let new_window_info = WindowInfo::from_logical_size(size, scaling); + let new_physical_size = size.to_physical::(self.scaling_factor.get()); let _ = self.connection.conn.configure_window( self.window_id.get(), &ConfigureWindowAux::new() - .width(new_window_info.physical_size().width) - .height(new_window_info.physical_size().height), + .width(new_physical_size.width) + .height(new_physical_size.height), ); let _ = self.connection.conn.flush(); @@ -107,4 +109,12 @@ impl WindowInner { pub fn gl_context(&self) -> Option { Some(crate::gl::GlContext::new(Rc::clone(self.gl_context.as_ref()?))) } + + pub fn scale_factor(&self) -> f64 { + self.scaling_factor.get() + } + + pub fn size(&self) -> PhysicalSize { + self.window_size.get().cast() + } } diff --git a/src/platform/x11/xcb_connection.rs b/src/platform/x11/xcb_connection.rs index 08ebb48e..b0c8b805 100644 --- a/src/platform/x11/xcb_connection.rs +++ b/src/platform/x11/xcb_connection.rs @@ -81,33 +81,9 @@ impl X11Connection { } } - // Try to get the scaling with `get_scaling_xft` first. - // Only use this function as a fallback. - // If neither work, I guess just assume 96.0 and don't do any scaling. - fn get_scaling_screen_dimensions(&self) -> f64 { - // Figure out screen information - let screen = self.screen(); - - // Get the DPI from the screen struct - // - // there are 2.54 centimeters to an inch; so there are 25.4 millimeters. - // dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch)) - // = N pixels / (M inch / 25.4) - // = N * 25.4 pixels / M inch - let width_px = screen.width_in_pixels as f64; - let width_mm = screen.width_in_millimeters as f64; - let height_px = screen.height_in_pixels as f64; - let height_mm = screen.height_in_millimeters as f64; - let _xres = width_px * 25.4 / width_mm; - let yres = height_px * 25.4 / height_mm; - - // TODO: choose between `xres` and `yres`? (probably both are the same?) - yres / 96.0 - } - #[inline] pub fn get_scaling(&self) -> Result> { - Ok(self.get_scaling_xft()?.unwrap_or(self.get_scaling_screen_dimensions())) + Ok(self.get_scaling_xft()?.unwrap_or(96.)) } #[inline] diff --git a/src/window_info.rs b/src/window_info.rs deleted file mode 100644 index edb5701b..00000000 --- a/src/window_info.rs +++ /dev/null @@ -1,144 +0,0 @@ -/// The info about the window -#[derive(Debug, Copy, Clone)] -pub struct WindowInfo { - logical_size: Size, - physical_size: PhySize, - scale: f64, - scale_recip: f64, -} - -impl WindowInfo { - pub fn from_logical_size(logical_size: Size, scale: f64) -> Self { - let scale_recip = if scale == 1.0 { 1.0 } else { 1.0 / scale }; - - let physical_size = PhySize { - width: (logical_size.width * scale).round() as u32, - height: (logical_size.height * scale).round() as u32, - }; - - Self { logical_size, physical_size, scale, scale_recip } - } - - pub fn from_physical_size(physical_size: PhySize, scale: f64) -> Self { - let scale_recip = if scale == 1.0 { 1.0 } else { 1.0 / scale }; - - let logical_size = Size { - width: f64::from(physical_size.width) * scale_recip, - height: f64::from(physical_size.height) * scale_recip, - }; - - Self { logical_size, physical_size, scale, scale_recip } - } - - /// The logical size of the window - pub fn logical_size(&self) -> Size { - self.logical_size - } - - /// The physical size of the window - pub fn physical_size(&self) -> PhySize { - self.physical_size - } - - /// The scale factor of the window - pub fn scale(&self) -> f64 { - self.scale - } - - /// The reciprocal of the scale factor of the window - pub fn scale_recip(&self) -> f64 { - self.scale_recip - } -} - -/// A point in logical coordinates -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Point { - pub x: f64, - pub y: f64, -} - -impl Point { - /// Create a new point in logical coordinates - pub fn new(x: f64, y: f64) -> Self { - Self { x, y } - } - - /// Convert to actual physical coordinates - #[inline] - pub fn to_physical(&self, window_info: &WindowInfo) -> PhyPoint { - PhyPoint { - x: (self.x * window_info.scale()).round() as i32, - y: (self.y * window_info.scale()).round() as i32, - } - } -} - -/// A point in actual physical coordinates -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct PhyPoint { - pub x: i32, - pub y: i32, -} - -impl PhyPoint { - /// Create a new point in actual physical coordinates - pub fn new(x: i32, y: i32) -> Self { - Self { x, y } - } - - /// Convert to logical coordinates - #[inline] - pub fn to_logical(&self, window_info: &WindowInfo) -> Point { - Point { - x: f64::from(self.x) * window_info.scale_recip(), - y: f64::from(self.y) * window_info.scale_recip(), - } - } -} - -/// A size in logical coordinates -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Size { - pub width: f64, - pub height: f64, -} - -impl Size { - /// Create a new size in logical coordinates - pub fn new(width: f64, height: f64) -> Self { - Self { width, height } - } - - /// Convert to actual physical size - #[inline] - pub fn to_physical(&self, window_info: &WindowInfo) -> PhySize { - PhySize { - width: (self.width * window_info.scale()).round() as u32, - height: (self.height * window_info.scale()).round() as u32, - } - } -} - -/// An actual size in physical coordinates -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct PhySize { - pub width: u32, - pub height: u32, -} - -impl PhySize { - /// Create a new size in actual physical coordinates - pub fn new(width: u32, height: u32) -> Self { - Self { width, height } - } - - /// Convert to logical size - #[inline] - pub fn to_logical(&self, window_info: &WindowInfo) -> Size { - Size { - width: f64::from(self.width) * window_info.scale_recip(), - height: f64::from(self.height) * window_info.scale_recip(), - } - } -} diff --git a/src/window_open_options.rs b/src/window_open_options.rs index 7c903513..e8c37a0e 100644 --- a/src/window_open_options.rs +++ b/src/window_open_options.rs @@ -1,4 +1,4 @@ -use crate::Size; +use dpi::{LogicalSize, Size}; #[cfg(feature = "opengl")] use crate::gl::GlConfig; @@ -18,10 +18,7 @@ pub enum WindowScalePolicy { pub struct WindowOpenOptions { pub title: String, - /// The logical size of the window - /// - /// These dimensions will be scaled by the scaling policy specified in `scale`. Mouse - /// position will be passed back as logical coordinates. + /// The size of the window, either in physical or logical coordinates pub size: Size, /// The dpi scaling policy @@ -48,8 +45,8 @@ impl WindowOpenOptions { } #[inline] - pub fn with_size(mut self, width: f64, height: f64) -> Self { - self.size = Size::new(width, height); + pub fn with_size(mut self, size: impl Into) -> Self { + self.size = size.into(); self } @@ -71,7 +68,7 @@ impl Default for WindowOpenOptions { fn default() -> Self { Self { title: String::from("baseview window"), - size: Size { width: 500.0, height: 400.0 }, + size: LogicalSize { width: 500.0, height: 400.0 }.into(), scale: WindowScalePolicy::default(), #[cfg(feature = "opengl")] gl_config: None, From c429b746ce4868e13088534529f21585acd4ecda Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 19 Jun 2026 09:33:08 +0200 Subject: [PATCH 2/9] wip --- examples/open_parented/src/main.rs | 57 +++++++++++++----------------- examples/open_window/src/main.rs | 44 +++++++++++------------ 2 files changed, 45 insertions(+), 56 deletions(-) diff --git a/examples/open_parented/src/main.rs b/examples/open_parented/src/main.rs index 7f12deab..4eaeb0db 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -1,5 +1,6 @@ +use baseview::dpi::LogicalSize; use baseview::{ - Event, EventStatus, PhySize, Window, WindowContext, WindowEvent, WindowHandle, WindowHandler, + Event, EventStatus, Window, WindowContext, WindowEvent, WindowHandle, WindowHandler, WindowOpenOptions, }; use std::cell::{Cell, RefCell}; @@ -7,7 +8,6 @@ use std::num::NonZeroU32; struct ParentWindowHandler { surface: RefCell>, - current_size: Cell, damaged: Cell, _child_window: Option, @@ -17,21 +17,19 @@ impl ParentWindowHandler { pub fn new(window: WindowContext) -> Self { let ctx = softbuffer::Context::new(window.clone()).unwrap(); let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); - surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); + let size = window.size(); + surface + .resize(NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap()) + .unwrap(); - let window_open_options = - WindowOpenOptions::new().with_size(256.0, 256.0).with_title("baseview child"); + let window_open_options = WindowOpenOptions::new() + .with_size(LogicalSize::new(256, 256)) + .with_title("baseview child"); let child_window = Window::open_parented(&window, window_open_options, ChildWindowHandler::new); - // TODO: no way to query physical size initially? - Self { - surface: surface.into(), - current_size: PhySize::new(512, 512).into(), - damaged: true.into(), - _child_window: Some(child_window), - } + Self { surface: surface.into(), damaged: true.into(), _child_window: Some(child_window) } } } @@ -48,13 +46,11 @@ impl WindowHandler for ParentWindowHandler { fn on_event(&self, event: Event) -> EventStatus { match event { - Event::Window(WindowEvent::Resized(info)) => { - println!("Parent Resized: {:?}", info); - let new_size = info.physical_size(); - self.current_size.set(new_size); + Event::Window(WindowEvent::Resized { size, scale_factor }) => { + println!("Parent Resized: {size:?}, scale: {scale_factor}"); if let (Some(width), Some(height)) = - (NonZeroU32::new(new_size.width), NonZeroU32::new(new_size.height)) + (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) { self.surface.borrow_mut().resize(width, height).unwrap(); self.damaged.set(true); @@ -71,22 +67,19 @@ impl WindowHandler for ParentWindowHandler { struct ChildWindowHandler { surface: RefCell>, - current_size: Cell, damaged: Cell, } impl ChildWindowHandler { pub fn new(window: WindowContext) -> Self { let ctx = softbuffer::Context::new(window.clone()).unwrap(); - let mut surface = softbuffer::Surface::new(&ctx, window).unwrap(); - surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); - - // TODO: no way to query physical size initially? - Self { - surface: surface.into(), - current_size: PhySize::new(256, 256).into(), - damaged: true.into(), - } + let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); + let size = window.size(); + surface + .resize(NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap()) + .unwrap(); + + Self { surface: surface.into(), damaged: true.into() } } } @@ -103,13 +96,11 @@ impl WindowHandler for ChildWindowHandler { fn on_event(&self, event: Event) -> EventStatus { match event { - Event::Window(WindowEvent::Resized(info)) => { - println!("Child Resized: {:?}", info); - let new_size = info.physical_size(); - self.current_size.set(new_size); + Event::Window(WindowEvent::Resized { size, scale_factor }) => { + println!("Child Resized: {size:?}, scale: {scale_factor}"); if let (Some(width), Some(height)) = - (NonZeroU32::new(new_size.width), NonZeroU32::new(new_size.height)) + (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) { self.surface.borrow_mut().resize(width, height).unwrap(); self.damaged.set(true); @@ -125,7 +116,7 @@ impl WindowHandler for ChildWindowHandler { } fn main() { - let window_open_options = WindowOpenOptions::new().with_size(512.0, 512.0); + let window_open_options = WindowOpenOptions::new().with_size(LogicalSize::new(512.0, 512.0)); Window::open_blocking(window_open_options, ParentWindowHandler::new); } diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index 4c97a1ab..33026da5 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -6,9 +6,10 @@ use rtrb::{Consumer, RingBuffer}; #[cfg(target_os = "macos")] use baseview::copy_to_clipboard; +use baseview::dpi::{LogicalSize, PhysicalPosition}; use baseview::{ - Event, EventStatus, MouseEvent, PhyPoint, PhySize, Window, WindowContext, WindowEvent, - WindowHandler, WindowInfo, WindowOpenOptions, + Event, EventStatus, MouseEvent, Window, WindowContext, WindowEvent, WindowHandler, + WindowOpenOptions, }; #[derive(Debug, Clone)] @@ -18,10 +19,10 @@ enum Message { struct OpenWindowExample { rx: RefCell>, + window_context: WindowContext, surface: RefCell>, - current_size: Cell, - mouse_pos: Cell, + mouse_pos: Cell>, is_cursor_inside: Cell, damaged: Cell, } @@ -34,8 +35,8 @@ impl WindowHandler for OpenWindowExample { let mut surface = self.surface.borrow_mut(); let mut pixels = surface.buffer_mut().unwrap(); - let size = self.current_size.get().physical_size(); - let scale_factor = self.current_size.get().scale(); + let size = self.window_context.size(); + let scale_factor = self.window_context.scale_factor(); let (width, height) = (size.width, size.height); for index in 0..(width * height) { @@ -75,11 +76,12 @@ impl WindowHandler for OpenWindowExample { if self.is_cursor_inside.get() { let rect_size = (25.0 * scale_factor) as i32; + let mouse_pos = self.mouse_pos.get().cast::(); - let rect_x_start = (self.mouse_pos.get().x - rect_size).clamp(0, width as i32) as u32; - let rect_x_end = (self.mouse_pos.get().x + rect_size).clamp(0, width as i32) as u32; - let rect_y_start = (self.mouse_pos.get().y - rect_size).clamp(0, height as i32) as u32; - let rect_y_end = (self.mouse_pos.get().y + rect_size).clamp(0, height as i32) as u32; + let rect_x_start = (mouse_pos.x - rect_size).clamp(0, width as i32) as u32; + let rect_x_end = (mouse_pos.x + rect_size).clamp(0, width as i32) as u32; + let rect_y_start = (mouse_pos.y - rect_size).clamp(0, height as i32) as u32; + let rect_y_end = (mouse_pos.y + rect_size).clamp(0, height as i32) as u32; for x in rect_x_start..rect_x_end { for y in rect_y_start..rect_y_end { @@ -98,12 +100,11 @@ impl WindowHandler for OpenWindowExample { } fn on_event(&self, event: Event) -> EventStatus { - match &event { + match event { #[cfg(target_os = "macos")] Event::Mouse(MouseEvent::ButtonPressed { .. }) => copy_to_clipboard("This is a test!"), Event::Mouse(MouseEvent::CursorMoved { position, .. }) => { - let phy_pos = position.to_physical(&self.current_size.get()); - self.mouse_pos.set(phy_pos); + self.mouse_pos.set(position); self.damaged.set(true); } Event::Mouse(MouseEvent::CursorEntered) => { @@ -114,14 +115,11 @@ impl WindowHandler for OpenWindowExample { self.is_cursor_inside.set(false); self.damaged.set(true); } - Event::Window(WindowEvent::Resized(info)) => { - println!("Resized: {:?}", info); - self.current_size.set(*info); - - let new_size = info.physical_size(); + Event::Window(WindowEvent::Resized { scale_factor, size }) => { + println!("Resized: {size:?}, scale: {scale_factor}"); if let (Some(width), Some(height)) = - (NonZeroU32::new(new_size.width), NonZeroU32::new(new_size.height)) + (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) { self.surface.borrow_mut().resize(width, height).unwrap(); self.damaged.set(true); @@ -137,7 +135,7 @@ impl WindowHandler for OpenWindowExample { } fn main() { - let window_open_options = WindowOpenOptions::new().with_size(512.0, 512.0); + let window_open_options = WindowOpenOptions::new().with_size(LogicalSize::new(512.0, 512.0)); let (mut tx, rx) = RingBuffer::new(128); @@ -151,14 +149,14 @@ fn main() { Window::open_blocking(window_open_options, |window| { let ctx = softbuffer::Context::new(window.clone()).unwrap(); - let mut surface = softbuffer::Surface::new(&ctx, window).unwrap(); + let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); OpenWindowExample { + window_context: window, surface: surface.into(), rx: rx.into(), - current_size: WindowInfo::from_physical_size(PhySize::new(512, 512), 1.0).into(), - mouse_pos: PhyPoint::new(0, 0).into(), + mouse_pos: PhysicalPosition::new(0., 0.).into(), is_cursor_inside: false.into(), damaged: true.into(), } From 8f15cd02cdf7631d01b92324f2954307278b88ad Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 20 Jun 2026 08:35:59 +0200 Subject: [PATCH 3/9] win32 impl --- src/platform/win/drop_target.rs | 17 ++++++------ src/platform/win/window.rs | 41 +++++++++++++---------------- src/platform/win/window_state.rs | 25 +++++++++--------- src/wrappers/win32/rect.rs | 10 +++---- src/wrappers/win32/window.rs | 4 +-- src/wrappers/win32/window/handle.rs | 6 ++--- 6 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/platform/win/drop_target.rs b/src/platform/win/drop_target.rs index 7b41da47..269aa407 100644 --- a/src/platform/win/drop_target.rs +++ b/src/platform/win/drop_target.rs @@ -1,3 +1,4 @@ +use dpi::PhysicalPosition; use std::cell::{Cell, RefCell}; use std::ffi::OsString; use std::os::windows::prelude::OsStringExt; @@ -14,7 +15,7 @@ use windows_sys::Win32::{ }; use super::window_state::WindowState; -use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent, PhyPoint, Point}; +use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent}; #[implement(IDropTarget)] pub(crate) struct DropTarget { @@ -22,7 +23,7 @@ pub(crate) struct DropTarget { // These are cached since DragOver and DragLeave callbacks don't provide them, // and handling drag move events gets awkward on the client end otherwise - drag_position: Cell, + drag_position: Cell>, drop_data: RefCell, } @@ -30,7 +31,7 @@ impl DropTarget { pub(crate) fn new(window_state: Weak) -> Self { Self { window_state, - drag_position: Cell::new(Point::new(0.0, 0.0)), + drag_position: Cell::new(PhysicalPosition::new(0, 0)), drop_data: RefCell::new(DropData::None), } } @@ -63,8 +64,8 @@ impl DropTarget { }; let mut pt = POINT { x: pt.x, y: pt.y }; unsafe { ScreenToClient(window_state.hwnd, &mut pt as *mut POINT) }; - let phy_point = PhyPoint::new(pt.x, pt.y); - self.drag_position.set(phy_point.to_logical(&window_state.window_info())); + let phy_point = PhysicalPosition::new(pt.x, pt.y); + self.drag_position.set(phy_point); } fn parse_drop_data(&self, data_object: &IDataObject) { @@ -124,7 +125,7 @@ impl IDropTarget_Impl for DropTarget_Impl { self.parse_drop_data(pdataobj.unwrap()); let event = MouseEvent::DragEntered { - position: self.drag_position.get(), + position: self.drag_position.get().cast(), modifiers, data: self.drop_data.borrow().clone(), }; @@ -146,7 +147,7 @@ impl IDropTarget_Impl for DropTarget_Impl { self.parse_coordinates(*pt); let event = MouseEvent::DragMoved { - position: self.drag_position.get(), + position: self.drag_position.get().cast(), modifiers, data: self.drop_data.borrow().clone(), }; @@ -175,7 +176,7 @@ impl IDropTarget_Impl for DropTarget_Impl { self.parse_drop_data(pdataobj.unwrap()); let event = MouseEvent::DragDropped { - position: self.drag_position.get(), + position: self.drag_position.get().cast(), modifiers, data: self.drop_data.borrow().clone(), }; diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index b82b7381..acd6620a 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -13,12 +13,12 @@ use windows_sys::Win32::{ }, }; +use dpi::{PhysicalPosition, PhysicalSize, Size}; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use std::cell::Cell; use std::num::NonZeroUsize; use std::ptr::null_mut; -use raw_window_handle::{HasWindowHandle, RawWindowHandle}; - pub(crate) const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1; use super::drop_target::DropTarget; @@ -26,8 +26,8 @@ use super::*; use crate::platform::win::window_state::WindowState; use crate::wrappers::win32::cursor::SystemCursor; use crate::{ - Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, - WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, + Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowOpenOptions, + WindowScalePolicy, }; use crate::wrappers::win32::window::*; @@ -118,8 +118,7 @@ impl WindowImpl for BaseviewWindow { // have no way to know where the window will end up. // So, at window creation, we assume a DPI=96, and if it ends up wrong, we resize the window // to the actual logical size the user desired. - let new_size = WindowInfo::from_logical_size(self.initial_size, dpi.scale_factor()) - .physical_size(); + let new_size = self.initial_size.to_physical(dpi.scale_factor()); // Preemptively update so a synchronous WM_SIZE from SetWindowPos below // doesn't also emit Resized. @@ -191,10 +190,8 @@ unsafe fn wnd_proc_inner( let x = (lparam & 0xFFFF) as i16 as i32; let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; - let physical_pos = PhyPoint { x, y }; - let logical_pos = physical_pos.to_logical(&window_state.window_info()); let move_event = Event::Mouse(MouseEvent::CursorMoved { - position: logical_pos, + position: PhysicalPosition { x, y }.cast(), modifiers: window_state .keyboard_state .borrow() @@ -340,21 +337,20 @@ unsafe fn wnd_proc_inner( let width = (lparam & 0xFFFF) as u16 as u32; let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; - let new_window_info = { - let new_size = PhySize { width, height }; - let current_size = window_state.current_size.get(); - - // Only send the event if anything changed - if current_size == new_size { - return None; - } + let new_size = PhysicalSize { width, height }; + let current_size = window_state.current_size.get(); - window_state.current_size.set(new_size); + // Only send the event if anything changed + if current_size == new_size { + return None; + } - WindowInfo::from_physical_size(new_size, window_state.current_scale_factor()) - }; + window_state.current_size.set(new_size); - window_state.handle_event(Event::Window(WindowEvent::Resized(new_window_info))); + window_state.handle_event(Event::Window(WindowEvent::Resized { + scale_factor: window_state.current_dpi.get().scale_factor(), + size: new_size, + })); None } @@ -447,8 +443,7 @@ impl Window { WindowScalePolicy::ScaleFactor(scale) => scale, }; - let window_size = - WindowInfo::from_logical_size(options.size, scaling_factor).physical_size(); + let window_size = options.size.to_physical(scaling_factor); let style = if parented { WindowStyle::parented() } else { WindowStyle::embedded() }; let dpi_ctx = DpiAwarenessContext::new(&extended_user_32).unwrap(); diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index f6afd46d..b3d3167b 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -2,10 +2,8 @@ use crate::platform::win::keyboard::KeyboardState; use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::window::HWnd; use crate::wrappers::win32::{Dpi, ExtendedUser32}; -use crate::{ - Event, EventStatus, MouseCursor, PhySize, Size, WindowEvent, WindowHandler, WindowInfo, - WindowScalePolicy, -}; +use crate::{Event, EventStatus, MouseCursor, WindowEvent, WindowHandler, WindowScalePolicy}; +use dpi::{PhysicalSize, Size}; use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::cell::{Cell, OnceCell, Ref, RefCell}; use std::num::NonZeroIsize; @@ -16,7 +14,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::PostMessageW; pub(crate) struct WindowState { /// The HWND belonging to this window. pub hwnd: HWND, - pub current_size: Cell, + pub current_size: Cell>, pub current_dpi: Cell, // None if in non-system scale policy pub keyboard_state: RefCell, pub mouse_button_counter: Cell, @@ -34,7 +32,8 @@ pub(crate) struct WindowState { impl WindowState { pub fn new( - hwnd: HWND, current_size: PhySize, scale_policy: WindowScalePolicy, user32: ExtendedUser32, + hwnd: HWND, current_size: PhysicalSize, scale_policy: WindowScalePolicy, + user32: ExtendedUser32, ) -> Self { Self { hwnd, @@ -67,11 +66,11 @@ impl WindowState { handler.on_event(event) } - pub(crate) fn window_info(&self) -> WindowInfo { - WindowInfo::from_physical_size(self.current_size.get(), self.current_scale_factor()) + pub fn size(&self) -> PhysicalSize { + self.current_size.get() } - pub fn current_scale_factor(&self) -> f64 { + pub fn scale_factor(&self) -> f64 { match self.scale_policy { WindowScalePolicy::ScaleFactor(scale) => scale, WindowScalePolicy::SystemScaleFactor => self.current_dpi.get().scale_factor(), @@ -83,7 +82,10 @@ impl WindowState { } pub fn send_resized(&self) { - self.handle_event(Event::Window(WindowEvent::Resized(self.window_info()))); + self.handle_event(Event::Window(WindowEvent::Resized { + size: self.current_size.get(), + scale_factor: self.current_dpi.get().scale_factor(), + })); } pub fn close(&self) { @@ -109,8 +111,7 @@ impl WindowState { // `self.window_info` will be modified in response to the `WM_SIZE` event that // follows the `SetWindowPos()` call let dpi = self.current_dpi.get(); - let window_info = WindowInfo::from_logical_size(size, dpi.scale_factor()); - let new_size = window_info.physical_size(); + let new_size = size.to_physical(dpi.scale_factor()); self.hwnd().resize_and_activate(new_size, dpi, &self.user32).unwrap(); } diff --git a/src/wrappers/win32/rect.rs b/src/wrappers/win32/rect.rs index 8f7c29be..73485c94 100644 --- a/src/wrappers/win32/rect.rs +++ b/src/wrappers/win32/rect.rs @@ -1,4 +1,4 @@ -use crate::PhySize; +use dpi::PhysicalSize; use windows_sys::Win32::Foundation::RECT; #[derive(Copy, Clone)] @@ -7,16 +7,16 @@ pub struct Rect(pub RECT); impl Rect { pub const EMPTY: Self = Self(RECT { left: 0, top: 0, right: 0, bottom: 0 }); - pub fn size(&self) -> PhySize { - PhySize { + pub fn size(&self) -> PhysicalSize { + PhysicalSize { width: self.0.right.abs_diff(self.0.left), height: self.0.top.abs_diff(self.0.bottom), } } } -impl From for Rect { - fn from(size: PhySize) -> Self { +impl From> for Rect { + fn from(size: PhysicalSize) -> Self { Self(RECT { left: 0, top: 0, diff --git a/src/wrappers/win32/window.rs b/src/wrappers/win32/window.rs index a3b9af6c..f1786786 100644 --- a/src/wrappers/win32/window.rs +++ b/src/wrappers/win32/window.rs @@ -4,6 +4,7 @@ mod proc; mod window_class; use data::WindowData; +use dpi::PhysicalSize; pub use handle::HWnd; pub use proc::wnd_proc; use std::ptr::null_mut; @@ -14,7 +15,6 @@ use windows_core::{Error, Result, HSTRING}; use crate::wrappers::win32::h_instance::HInstance; use crate::wrappers::win32::style::WindowStyle; use crate::wrappers::win32::DpiAwarenessContext; -use crate::PhySize; use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM}; use windows_sys::Win32::UI::WindowsAndMessaging::CreateWindowExW; @@ -51,7 +51,7 @@ pub trait WindowImpl: 'static { /// For any non-trivial operations (e.g. window resizing, GL context creation, etc.), put them in /// [`WindowImpl::after_create`] instead. pub fn create_window( - title: &HSTRING, style: WindowStyle, nc_size: PhySize, parent: HWND, + title: &HSTRING, style: WindowStyle, nc_size: PhysicalSize, parent: HWND, _dpi_ctx: &DpiAwarenessContext, initializer: impl FnOnce(HWnd) -> W + 'static, ) -> Result { let instance = HInstance::get(); diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 68f2a756..a1a7d6fe 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -1,7 +1,7 @@ use crate::wrappers::win32::style::WindowStyle; use crate::wrappers::win32::user32::ExtendedUser32; use crate::wrappers::win32::{Dpi, DpiAwarenessContext, Rect}; -use crate::PhySize; +use dpi::PhysicalSize; use std::num::NonZeroUsize; use std::ptr::{null_mut, NonNull}; use windows::Win32::System::Ole::IDropTarget; @@ -117,7 +117,7 @@ impl HWnd { } } - pub fn resize_nc_and_activate(&self, size: PhySize) -> Result<()> { + pub fn resize_nc_and_activate(&self, size: PhysicalSize) -> Result<()> { let result = unsafe { SetWindowPos( self.0, @@ -138,7 +138,7 @@ impl HWnd { } pub fn resize_and_activate( - &self, client_size: PhySize, window_dpi: Dpi, user32: &ExtendedUser32, + &self, client_size: PhysicalSize, window_dpi: Dpi, user32: &ExtendedUser32, ) -> Result<()> { let dpi_ctx = DpiAwarenessContext::new(user32)?; let style = self.get_style()?; From 710646c2241e5064683f5202414fd94647fff187 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:44:47 +0200 Subject: [PATCH 4/9] macOS impl --- examples/open_parented/src/main.rs | 4 +- examples/open_window/src/main.rs | 2 +- examples/render_femtovg/src/main.rs | 2 +- src/context.rs | 4 +- src/platform/macos/context.rs | 17 ++++++-- src/platform/macos/view.rs | 66 ++++++++++++++--------------- src/platform/macos/window.rs | 37 +++++++++------- src/window.rs | 30 +++++++++++++ src/wrappers/appkit/view.rs | 11 +++++ src/wrappers/appkit/window.rs | 4 +- 10 files changed, 116 insertions(+), 61 deletions(-) diff --git a/examples/open_parented/src/main.rs b/examples/open_parented/src/main.rs index 4eaeb0db..b4155ba8 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -17,7 +17,7 @@ impl ParentWindowHandler { pub fn new(window: WindowContext) -> Self { let ctx = softbuffer::Context::new(window.clone()).unwrap(); let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); - let size = window.size(); + let size = window.size().physical; surface .resize(NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap()) .unwrap(); @@ -74,7 +74,7 @@ impl ChildWindowHandler { pub fn new(window: WindowContext) -> Self { let ctx = softbuffer::Context::new(window.clone()).unwrap(); let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); - let size = window.size(); + let size = window.size().physical; surface .resize(NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap()) .unwrap(); diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index 33026da5..f720ec5e 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -37,7 +37,7 @@ impl WindowHandler for OpenWindowExample { let mut pixels = surface.buffer_mut().unwrap(); let size = self.window_context.size(); let scale_factor = self.window_context.scale_factor(); - let (width, height) = (size.width, size.height); + let (width, height) = (size.physical.width, size.physical.height); for index in 0..(width * height) { let x = index % width; diff --git a/examples/render_femtovg/src/main.rs b/examples/render_femtovg/src/main.rs index 6ecc9299..4d23f1c8 100644 --- a/examples/render_femtovg/src/main.rs +++ b/examples/render_femtovg/src/main.rs @@ -27,7 +27,7 @@ impl FemtovgExample { let mut canvas = Canvas::new(renderer).unwrap(); let size = window_context.size(); - canvas.set_size(size.width, size.height, window_context.scale_factor() as f32); + canvas.set_size(size.physical.width, size.physical.height, size.scale_factor as f32); unsafe { gl_context.make_not_current() }; Self { diff --git a/src/context.rs b/src/context.rs index 0b5bb23e..5084aa67 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,4 @@ -use crate::{platform, MouseCursor}; +use crate::{platform, MouseCursor, WindowSize}; use dpi::{PhysicalPosition, PhysicalSize, Pixel, Size}; use raw_window_handle::{ DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle, @@ -38,7 +38,7 @@ impl WindowContext { self.inner.scale_factor() } - pub fn size(&self) -> PhysicalSize { + pub fn size(&self) -> WindowSize { self.inner.size() } diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index 432f8c1a..bbd772aa 100644 --- a/src/platform/macos/context.rs +++ b/src/platform/macos/context.rs @@ -1,21 +1,24 @@ use crate::platform::macos::cursor::Cursor; use crate::platform::macos::view::BaseviewView; +use crate::platform::WindowSharedState; use crate::wrappers::appkit::{View, ViewRef}; -use crate::{MouseCursor, Size}; +use crate::{MouseCursor, WindowSize}; +use dpi::Size; use objc2::rc::Weak; use objc2::runtime::NSObjectProtocol; use objc2::Message; use raw_window_handle::DisplayHandle; +use std::rc::Rc; #[derive(Clone)] pub struct WindowContext { view: Weak>, + state: Rc, } impl WindowContext { pub(crate) fn new(view: ViewRef<'_, BaseviewView>) -> Self { - view.view.retain(); - Self { view: Weak::from_retained(&view.view.retain()) } + Self { view: Weak::from_retained(&view.view.retain()), state: Rc::clone(&view.state) } } pub fn close(&self) { @@ -63,6 +66,14 @@ impl WindowContext { view.addCursorRect_cursor(view.bounds(), &native_cursor.load()); } + pub fn size(&self) -> WindowSize { + WindowSize::from_logical(self.state.size.get(), self.state.scale_factor.get()) + } + + pub fn scale_factor(&self) -> f64 { + self.state.scale_factor.get() + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option { Some(crate::gl::GlContext::new(self.view.load()?.inner().gl_context.get()?.clone())) diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index fdf1d8c4..e871ba2e 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -6,9 +6,10 @@ use crate::platform::macos::context::WindowContext; use crate::wrappers::appkit::*; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::{ - DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, - WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, + DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, ScrollDelta, WindowEvent, + WindowHandler, WindowOpenOptions, }; +use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, Size}; use objc2::__framework_prelude::Retained; use objc2::rc::Weak; use objc2::runtime::{NSObjectProtocol, ProtocolObject}; @@ -45,12 +46,12 @@ impl BaseviewView { pub fn new( options: WindowOpenOptions, builder: impl FnOnce(crate::WindowContext) -> H + Send + 'static, - parenting: ViewParentingType, + parenting: ViewParentingType, final_size: LogicalSize, ) -> (Retained>, Rc) { let view_rect = - NSRect::new(NSPoint::ZERO, NSSize::new(options.size.width, options.size.height)); + NSRect::new(NSPoint::ZERO, NSSize::new(final_size.width, final_size.height)); - let state = Rc::new(WindowSharedState::new(&options)); + let state = Rc::new(WindowSharedState::new(final_size, 1.0)); let inner = BaseviewView { state: state.clone(), @@ -79,6 +80,9 @@ impl BaseviewView { } } + view.state.scale_factor.set(view.view.backing_scale_factor()); + view.state.size.set(view.view.size()); + #[cfg(feature = "opengl")] if let Some(gl_config) = options.gl_config { let gl_context = super::gl::GlContext::create(view.view, gl_config).unwrap(); @@ -111,12 +115,6 @@ impl BaseviewView { } }); view.notification_center_observer.set(Some(observer)); - - // Send an initial Resized event so users get the correct scale factor and physical size. - Self::trigger_event( - view, - Event::Window(WindowEvent::Resized(Self::fetch_view_size(view.view))), - ); }); (view, state) @@ -140,6 +138,7 @@ impl BaseviewView { } pub fn resize(this: ViewRef, size: Size) { + let size = size.to_logical::(this.view.backing_scale_factor()); // NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even // though the size is in fractional pixels. let size = NSSize::new(size.width.round(), size.height.round()); @@ -178,19 +177,6 @@ impl BaseviewView { handler.on_frame(); } - - fn fetch_view_size(view: &NSView) -> WindowInfo { - let ns_window = view.window(); - - let scale_factor: f64 = ns_window.map(|w| w.backingScaleFactor()).unwrap_or(1.0); - - let bounds = view.bounds(); - - WindowInfo::from_logical_size( - Size::new(bounds.size.width, bounds.size.height), - scale_factor, - ) - } } impl Drop for BaseviewView { @@ -225,14 +211,24 @@ impl ViewImpl for BaseviewView { } fn view_did_change_backing_properties(this: ViewRef) { - let new_window_info = Self::fetch_view_size(this.view); - let window_info = this.state.window_info.get(); + let current_size = this.view.size(); + let current_scale_factor = this.view.backing_scale_factor(); // Only send the event when the window's size has actually changed to be in line with the // other platform implementations - if new_window_info.physical_size() != window_info.physical_size() { - this.state.window_info.set(new_window_info); - Self::trigger_event(this, Event::Window(WindowEvent::Resized(new_window_info))); + if this.state.scale_factor.get() != current_scale_factor + || this.state.size.get() != current_size + { + this.state.size.set(current_size); + this.state.scale_factor.set(current_scale_factor); + + Self::trigger_event( + this, + Event::Window(WindowEvent::Resized { + size: current_size.to_physical(current_scale_factor), + scale_factor: current_scale_factor, + }), + ); } } @@ -322,7 +318,7 @@ impl ViewImpl for BaseviewView { fn mouse_moved(this: ViewRef, event: &NSEvent) { let point = this.view.convertPoint_fromView(event.locationInWindow(), None); - let position = Point { x: point.x, y: point.y }; + let position = PhysicalPosition { x: point.x, y: point.y }; Self::trigger_event( this, @@ -359,7 +355,7 @@ impl ViewImpl for BaseviewView { let drop_data = get_drop_data(sender); let event = MouseEvent::DragEntered { - position: get_drag_position(sender), + position: get_drag_position(sender).to_physical(this.view.backing_scale_factor()), modifiers: make_modifiers(modifiers), data: drop_data, }; @@ -374,7 +370,7 @@ impl ViewImpl for BaseviewView { let drop_data = get_drop_data(sender); let event = MouseEvent::DragMoved { - position: get_drag_position(sender), + position: get_drag_position(sender).to_physical(this.view.backing_scale_factor()), modifiers: make_modifiers(modifiers), data: drop_data, }; @@ -398,7 +394,7 @@ impl ViewImpl for BaseviewView { let drop_data = get_drop_data(sender); let event = MouseEvent::DragDropped { - position: get_drag_position(sender), + position: get_drag_position(sender).to_physical(this.view.backing_scale_factor()), modifiers: make_modifiers(modifiers), data: drop_data, }; @@ -579,13 +575,13 @@ fn new_tracking_area(this: &NSView) -> Retained { } } -fn get_drag_position(sender: Option<&ProtocolObject>) -> Point { +fn get_drag_position(sender: Option<&ProtocolObject>) -> LogicalPosition { let point = match sender { Some(sender) => sender.draggingLocation(), None => NSPoint::ZERO, }; - Point::new(point.x, point.y) + LogicalPosition::new(point.x, point.y) } fn get_drop_data(sender: Option<&ProtocolObject>) -> DropData { diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index aab2d7b8..8961e14f 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -1,17 +1,17 @@ -use std::cell::{Cell, RefCell}; -use std::rc::Rc; - +use dpi::LogicalSize; use objc2::rc::{autoreleasepool, Weak}; use objc2::MainThreadMarker; use objc2_app_kit::{ NSApplication, NSApplicationActivationPolicy, NSPasteboard, NSPasteboardTypeString, }; -use objc2_foundation::NSString; +use objc2_foundation::{NSSize, NSString}; use raw_window_handle::HasWindowHandle; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; use crate::platform::macos::view::{BaseviewView, ViewParentingType}; use crate::wrappers::appkit::{create_window, extract_raw_window_handle, View}; -use crate::{WindowContext, WindowHandler, WindowInfo, WindowOpenOptions}; +use crate::{WindowContext, WindowHandler, WindowOpenOptions}; pub struct WindowHandle { view: RefCell>>>, @@ -48,7 +48,11 @@ impl Window { let parenting = ViewParentingType::Parented { parent_view: Weak::from_retained(&parent_view) }; - let (ns_view, state) = BaseviewView::new(options, build, parenting); + let backing_scale_factor = + parent_view.window().map(|w| w.backingScaleFactor()).unwrap_or(1.0); + let final_size = options.size.to_logical(backing_scale_factor); + + let (ns_view, state) = BaseviewView::new(options, build, parenting, final_size); WindowHandle { view: Some(Weak::from_retained(&ns_view)).into(), state } }) @@ -67,9 +71,15 @@ impl Window { let _ = app.setActivationPolicy(NSApplicationActivationPolicy::Regular); - let window = create_window(options.size, mtm); + let initial_size = options.size.to_logical(1.0); + let window = create_window(initial_size, mtm); window.center(); + let final_size = options.size.to_logical(window.backingScaleFactor()); + if final_size != initial_size { + window.setContentSize(NSSize::new(final_size.width, final_size.height)); + } + let title = NSString::from_str(&options.title); window.setTitle(&title); window.makeKeyAndOrderFront(None); @@ -79,7 +89,7 @@ impl Window { owned_window: Weak::from_retained(&window), }; - let _ = BaseviewView::new(options, build, parenting); + let _ = BaseviewView::new(options, build, parenting, final_size); app.run(); }) @@ -87,17 +97,14 @@ impl Window { } pub(crate) struct WindowSharedState { - /// The last known window info for this window. - pub window_info: Cell, pub closed: Cell, + pub size: Cell>, + pub scale_factor: Cell, } impl WindowSharedState { - pub fn new(options: &WindowOpenOptions) -> Self { - Self { - window_info: WindowInfo::from_logical_size(options.size, 1.0).into(), - closed: false.into(), - } + pub fn new(size: LogicalSize, scale_factor: f64) -> Self { + Self { closed: false.into(), size: size.into(), scale_factor: scale_factor.into() } } } diff --git a/src/window.rs b/src/window.rs index 275199ca..d87e3eb6 100644 --- a/src/window.rs +++ b/src/window.rs @@ -2,6 +2,7 @@ use crate::context::WindowContext; use crate::handler::WindowHandler; use crate::platform; use crate::window_open_options::WindowOpenOptions; +use dpi::{LogicalSize, PhysicalSize, Pixel}; use raw_window_handle::HasWindowHandle; use std::marker::PhantomData; @@ -47,3 +48,32 @@ impl Window { platform::Window::open_blocking(options, build) } } + +#[derive(Debug, Copy, Clone)] +pub struct WindowSize { + pub physical: PhysicalSize, + pub logical: LogicalSize, + pub scale_factor: f64, +} + +impl WindowSize { + pub fn from_physical(physical: PhysicalSize, scale_factor: f64) -> Self { + Self { physical, logical: physical.to_logical(scale_factor), scale_factor } + } + + pub fn from_logical(logical: LogicalSize, scale_factor: f64) -> Self { + Self { physical: logical.to_physical(scale_factor), logical, scale_factor } + } +} + +impl From for PhysicalSize

{ + fn from(size: WindowSize) -> Self { + size.physical.cast() + } +} + +impl From for LogicalSize

{ + fn from(size: WindowSize) -> Self { + size.logical.cast() + } +} diff --git a/src/wrappers/appkit/view.rs b/src/wrappers/appkit/view.rs index 604cd3c7..c5a27e5b 100644 --- a/src/wrappers/appkit/view.rs +++ b/src/wrappers/appkit/view.rs @@ -1,3 +1,4 @@ +use dpi::LogicalSize; use objc2::__framework_prelude::{Allocated, AnyClass, ProtocolObject, Retained}; use objc2::rc::Weak; use objc2::runtime::AnyObject; @@ -93,6 +94,16 @@ impl View { Some(unsafe { WindowHandle::borrow_raw(handle.into()) }) } + + pub fn size(&self) -> LogicalSize { + let size = self.bounds().size; + LogicalSize::new(size.width, size.height).cast() + } + + pub fn backing_scale_factor(&self) -> f64 { + let Some(ns_window) = self.window() else { return 1.0 }; + ns_window.backingScaleFactor() + } } pub struct ViewInner { diff --git a/src/wrappers/appkit/window.rs b/src/wrappers/appkit/window.rs index 670304ea..a0b59c10 100644 --- a/src/wrappers/appkit/window.rs +++ b/src/wrappers/appkit/window.rs @@ -1,11 +1,11 @@ use crate::wrappers::appkit::{View, ViewImpl}; -use crate::Size; +use dpi::LogicalSize; use objc2::rc::Retained; use objc2::{msg_send, MainThreadMarker, MainThreadOnly}; use objc2_app_kit::{NSBackingStoreType, NSWindow, NSWindowStyleMask}; use objc2_foundation::{NSPoint, NSRect, NSSize}; -pub fn create_window(size: Size, mtm: MainThreadMarker) -> Retained { +pub fn create_window(size: LogicalSize, mtm: MainThreadMarker) -> Retained { let rect = NSRect::new(NSPoint::ZERO, NSSize { width: size.width, height: size.height }); // SAFETY: This is safe because of the setReleasedWhenClosed(false) below From b517f0747d32895c029ab8d5b6d5cde1042b9e50 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:36:10 +0200 Subject: [PATCH 5/9] fix --- src/platform/macos/view.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index e871ba2e..b549668f 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -9,7 +9,7 @@ use crate::{ DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowOpenOptions, }; -use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, Size}; +use dpi::{LogicalPosition, LogicalSize, Size}; use objc2::__framework_prelude::Retained; use objc2::rc::Weak; use objc2::runtime::{NSObjectProtocol, ProtocolObject}; @@ -318,12 +318,12 @@ impl ViewImpl for BaseviewView { fn mouse_moved(this: ViewRef, event: &NSEvent) { let point = this.view.convertPoint_fromView(event.locationInWindow(), None); - let position = PhysicalPosition { x: point.x, y: point.y }; + let position = LogicalPosition { x: point.x, y: point.y }; Self::trigger_event( this, Event::Mouse(MouseEvent::CursorMoved { - position, + position: position.to_physical(this.state.scale_factor.get()), modifiers: make_modifiers(event.modifierFlags()), }), ); From d50b04951c5965d6ecd96ecdf20fea1857d16f63 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:39:36 +0200 Subject: [PATCH 6/9] fixes --- src/platform/win/window_state.rs | 8 +++++--- src/platform/x11/window_shared.rs | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index b3d3167b..55e240d0 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -2,7 +2,9 @@ use crate::platform::win::keyboard::KeyboardState; use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::window::HWnd; use crate::wrappers::win32::{Dpi, ExtendedUser32}; -use crate::{Event, EventStatus, MouseCursor, WindowEvent, WindowHandler, WindowScalePolicy}; +use crate::{ + Event, EventStatus, MouseCursor, WindowEvent, WindowHandler, WindowScalePolicy, WindowSize, +}; use dpi::{PhysicalSize, Size}; use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::cell::{Cell, OnceCell, Ref, RefCell}; @@ -66,8 +68,8 @@ impl WindowState { handler.on_event(event) } - pub fn size(&self) -> PhysicalSize { - self.current_size.get() + pub fn size(&self) -> WindowSize { + WindowSize::from_physical(self.current_size.get(), self.scale_factor()) } pub fn scale_factor(&self) -> f64 { diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index c24818d5..e2c5b7fe 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -1,6 +1,6 @@ use crate::platform::X11Connection; -use crate::MouseCursor; -use dpi::{PhysicalPosition, PhysicalSize, Size}; +use crate::{MouseCursor, WindowSize}; +use dpi::{PhysicalSize, Size}; use raw_window_handle::{DisplayHandle, XcbWindowHandle}; use std::cell::Cell; use std::num::NonZero; @@ -114,7 +114,7 @@ impl WindowInner { self.scaling_factor.get() } - pub fn size(&self) -> PhysicalSize { - self.window_size.get().cast() + pub fn size(&self) -> WindowSize { + WindowSize::from_physical(self.window_size.get().cast(), self.scaling_factor.get()) } } From a76c872df8b5812f35d1744024353f5c37ede1fd Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:46:05 +0200 Subject: [PATCH 7/9] fixes --- src/context.rs | 2 +- src/event.rs | 2 +- src/platform/macos/view.rs | 4 ++-- src/platform/x11/window.rs | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/context.rs b/src/context.rs index 5084aa67..1687476e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,5 @@ use crate::{platform, MouseCursor, WindowSize}; -use dpi::{PhysicalPosition, PhysicalSize, Pixel, Size}; +use dpi::{Pixel, Size}; use raw_window_handle::{ DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle, }; diff --git a/src/event.rs b/src/event.rs index 262f25de..fab9d36d 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,4 @@ -use dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; +use dpi::{PhysicalPosition, PhysicalSize}; use keyboard_types::{KeyboardEvent, Modifiers}; use std::path::PathBuf; diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index b549668f..638d6ec6 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -44,7 +44,7 @@ pub(crate) struct BaseviewView { impl BaseviewView { pub fn new( - options: WindowOpenOptions, + _options: WindowOpenOptions, builder: impl FnOnce(crate::WindowContext) -> H + Send + 'static, parenting: ViewParentingType, final_size: LogicalSize, ) -> (Retained>, Rc) { @@ -84,7 +84,7 @@ impl BaseviewView { view.state.size.set(view.view.size()); #[cfg(feature = "opengl")] - if let Some(gl_config) = options.gl_config { + if let Some(gl_config) = _options.gl_config { let gl_context = super::gl::GlContext::create(view.view, gl_config).unwrap(); let Ok(()) = view.gl_context.set(gl_context) else { unreachable!() }; } diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 96842f8d..38e6eacc 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,4 +1,3 @@ -use dpi::Pixel; use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use std::cell::Cell; use std::error::Error; From 24cecb5d023e95fb5976d064e63019d12a3682b5 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:45:17 +0200 Subject: [PATCH 8/9] resized handler --- examples/open_parented/src/main.rs | 46 +++++++++++++++-------------- examples/open_window/src/main.rs | 25 ++++++++-------- examples/render_femtovg/src/main.rs | 18 ++++++----- src/event.rs | 3 +- src/handler.rs | 3 +- src/platform/macos/view.rs | 12 +++----- 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/examples/open_parented/src/main.rs b/examples/open_parented/src/main.rs index b4155ba8..580ff941 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -1,7 +1,7 @@ use baseview::dpi::LogicalSize; use baseview::{ - Event, EventStatus, Window, WindowContext, WindowEvent, WindowHandle, WindowHandler, - WindowOpenOptions, + Event, EventStatus, Window, WindowContext, WindowHandle, WindowHandler, WindowOpenOptions, + WindowSize, }; use std::cell::{Cell, RefCell}; use std::num::NonZeroU32; @@ -44,18 +44,19 @@ impl WindowHandler for ParentWindowHandler { buf.present().unwrap(); } + fn resized(&self, new_size: WindowSize) { + println!("Parent Resized: {new_size:?}"); + + if let (Some(width), Some(height)) = + (NonZeroU32::new(new_size.physical.width), NonZeroU32::new(new_size.physical.height)) + { + self.surface.borrow_mut().resize(width, height).unwrap(); + self.damaged.set(true); + } + } + fn on_event(&self, event: Event) -> EventStatus { match event { - Event::Window(WindowEvent::Resized { size, scale_factor }) => { - println!("Parent Resized: {size:?}, scale: {scale_factor}"); - - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - self.surface.borrow_mut().resize(width, height).unwrap(); - self.damaged.set(true); - } - } Event::Mouse(e) => println!("Parent Mouse event: {:?}", e), Event::Keyboard(e) => println!("Parent Keyboard event: {:?}", e), Event::Window(e) => println!("Parent Window event: {:?}", e), @@ -94,18 +95,19 @@ impl WindowHandler for ChildWindowHandler { buf.present().unwrap(); } + fn resized(&self, new_size: WindowSize) { + println!("Child Resized: {new_size:?}"); + + if let (Some(width), Some(height)) = + (NonZeroU32::new(new_size.physical.width), NonZeroU32::new(new_size.physical.height)) + { + self.surface.borrow_mut().resize(width, height).unwrap(); + self.damaged.set(true); + } + } + fn on_event(&self, event: Event) -> EventStatus { match event { - Event::Window(WindowEvent::Resized { size, scale_factor }) => { - println!("Child Resized: {size:?}, scale: {scale_factor}"); - - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - self.surface.borrow_mut().resize(width, height).unwrap(); - self.damaged.set(true); - } - } Event::Mouse(e) => println!("Child Mouse event: {:?}", e), Event::Keyboard(e) => println!("Child Keyboard event: {:?}", e), Event::Window(e) => println!("Child Window event: {:?}", e), diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index f720ec5e..f764edc4 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -8,8 +8,8 @@ use rtrb::{Consumer, RingBuffer}; use baseview::copy_to_clipboard; use baseview::dpi::{LogicalSize, PhysicalPosition}; use baseview::{ - Event, EventStatus, MouseEvent, Window, WindowContext, WindowEvent, WindowHandler, - WindowOpenOptions, + Event, EventStatus, MouseEvent, Window, WindowContext, WindowHandler, WindowOpenOptions, + WindowSize, }; #[derive(Debug, Clone)] @@ -28,6 +28,17 @@ struct OpenWindowExample { } impl WindowHandler for OpenWindowExample { + fn resized(&self, new_size: WindowSize) { + println!("Resized: {new_size:?}"); + + if let (Some(width), Some(height)) = + (NonZeroU32::new(new_size.physical.width), NonZeroU32::new(new_size.physical.height)) + { + self.surface.borrow_mut().resize(width, height).unwrap(); + self.damaged.set(true); + } + } + fn on_frame(&self) { if !self.damaged.get() { return; @@ -115,16 +126,6 @@ impl WindowHandler for OpenWindowExample { self.is_cursor_inside.set(false); self.damaged.set(true); } - Event::Window(WindowEvent::Resized { scale_factor, size }) => { - println!("Resized: {size:?}, scale: {scale_factor}"); - - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - self.surface.borrow_mut().resize(width, height).unwrap(); - self.damaged.set(true); - } - } _ => {} } diff --git a/examples/render_femtovg/src/main.rs b/examples/render_femtovg/src/main.rs index 4d23f1c8..0eb5581a 100644 --- a/examples/render_femtovg/src/main.rs +++ b/examples/render_femtovg/src/main.rs @@ -1,8 +1,8 @@ use baseview::dpi::{LogicalSize, PhysicalPosition}; use baseview::gl::{GlConfig, GlContext}; use baseview::{ - Event, EventStatus, MouseEvent, Window, WindowContext, WindowEvent, WindowHandler, - WindowOpenOptions, + Event, EventStatus, MouseEvent, Window, WindowContext, WindowHandler, WindowOpenOptions, + WindowSize, }; use femtovg::renderer::OpenGl; use femtovg::{Canvas, Color}; @@ -84,12 +84,14 @@ impl WindowHandler for FemtovgExample { self.damaged.set(false); } + fn resized(&self, new_size: WindowSize) { + let size = new_size.physical; + self.canvas.borrow_mut().set_size(size.width, size.height, new_size.scale_factor as f32); + self.damaged.set(true); + } + fn on_event(&self, event: Event) -> EventStatus { match event { - Event::Window(WindowEvent::Resized { size, scale_factor }) => { - self.canvas.borrow_mut().set_size(size.width, size.height, scale_factor as f32); - self.damaged.set(true); - } Event::Mouse( MouseEvent::CursorMoved { position, .. } | MouseEvent::DragEntered { position, .. } @@ -102,9 +104,9 @@ impl WindowHandler for FemtovgExample { } self.damaged.set(true); } - _ => {} + event => log_event(&event), }; - log_event(&event); + EventStatus::Captured } } diff --git a/src/event.rs b/src/event.rs index fab9d36d..5ca169b5 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,4 @@ -use dpi::{PhysicalPosition, PhysicalSize}; +use dpi::PhysicalPosition; use keyboard_types::{KeyboardEvent, Modifiers}; use std::path::PathBuf; @@ -108,7 +108,6 @@ pub enum MouseEvent { #[derive(Debug, Clone)] pub enum WindowEvent { - Resized { size: PhysicalSize, scale_factor: f64 }, Focused, Unfocused, WillClose, diff --git a/src/handler.rs b/src/handler.rs index 21db66e8..a3ee7087 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,6 +1,7 @@ -use crate::{Event, EventStatus}; +use crate::{Event, EventStatus, WindowSize}; pub trait WindowHandler: 'static { fn on_frame(&self); + fn resized(&self, new_size: WindowSize); fn on_event(&self, event: Event) -> EventStatus; } diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index 638d6ec6..498b2a51 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -7,7 +7,7 @@ use crate::wrappers::appkit::*; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::{ DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, ScrollDelta, WindowEvent, - WindowHandler, WindowOpenOptions, + WindowHandler, WindowOpenOptions, WindowSize, }; use dpi::{LogicalPosition, LogicalSize, Size}; use objc2::__framework_prelude::Retained; @@ -222,13 +222,9 @@ impl ViewImpl for BaseviewView { this.state.size.set(current_size); this.state.scale_factor.set(current_scale_factor); - Self::trigger_event( - this, - Event::Window(WindowEvent::Resized { - size: current_size.to_physical(current_scale_factor), - scale_factor: current_scale_factor, - }), - ); + if let Some(handler) = this.window_handler.get() { + handler.resized(WindowSize::from_logical(current_size, current_scale_factor)) + } } } From c9eaefd1b14053bcdc1210cf2916acc305850736 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 23 Jun 2026 01:12:36 +0200 Subject: [PATCH 9/9] X11 + Win32 impl resized --- src/platform/win/window.rs | 9 ++++----- src/platform/win/window_state.rs | 12 +++++------- src/platform/x11/event_loop.rs | 7 ++----- src/platform/x11/window.rs | 7 ++----- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index acd6620a..e45d2f4d 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -27,7 +27,7 @@ use crate::platform::win::window_state::WindowState; use crate::wrappers::win32::cursor::SystemCursor; use crate::{ Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowOpenOptions, - WindowScalePolicy, + WindowScalePolicy, WindowSize, }; use crate::wrappers::win32::window::*; @@ -347,10 +347,9 @@ unsafe fn wnd_proc_inner( window_state.current_size.set(new_size); - window_state.handle_event(Event::Window(WindowEvent::Resized { - scale_factor: window_state.current_dpi.get().scale_factor(), - size: new_size, - })); + if let Some(handler) = window_state.handler.get() { + handler.resized(WindowSize::from_physical(new_size, window_state.scale_factor())) + } None } diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index 55e240d0..2a230f4f 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -2,9 +2,7 @@ use crate::platform::win::keyboard::KeyboardState; use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::window::HWnd; use crate::wrappers::win32::{Dpi, ExtendedUser32}; -use crate::{ - Event, EventStatus, MouseCursor, WindowEvent, WindowHandler, WindowScalePolicy, WindowSize, -}; +use crate::{Event, EventStatus, MouseCursor, WindowHandler, WindowScalePolicy, WindowSize}; use dpi::{PhysicalSize, Size}; use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::cell::{Cell, OnceCell, Ref, RefCell}; @@ -84,10 +82,10 @@ impl WindowState { } pub fn send_resized(&self) { - self.handle_event(Event::Window(WindowEvent::Resized { - size: self.current_size.get(), - scale_factor: self.current_dpi.get().scale_factor(), - })); + if let Some(handler) = self.handler.get() { + handler + .resized(WindowSize::from_physical(self.current_size.get(), self.scale_factor())); + } } pub fn close(&self) { diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index 3a7748bb..63163f9f 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -4,7 +4,7 @@ use super::*; use crate::wrappers::connection_poller::{ConnectionPoller, PollStatus}; use crate::wrappers::xkbcommon::XkbcommonState; -use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler}; +use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowSize}; use dpi::{PhysicalPosition, PhysicalSize}; use std::error::Error; use std::rc::Rc; @@ -59,10 +59,7 @@ impl EventLoop { let scale_factor = self.window.scaling_factor.get(); - self.handle_event(Event::Window(WindowEvent::Resized { - size: size.cast(), - scale_factor, - })); + self.handler.resized(WindowSize::from_physical(size.cast(), scale_factor)); } Ok(()) diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 38e6eacc..2b3f3ef9 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -18,7 +18,7 @@ use super::X11Connection; use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; use crate::context::WindowContext; use crate::platform::x11::window_shared::WindowInner; -use crate::{Event, WindowEvent, WindowHandler, WindowOpenOptions, WindowScalePolicy}; +use crate::{WindowHandler, WindowOpenOptions, WindowScalePolicy, WindowSize}; pub struct WindowHandle { window_id: Option>, @@ -244,10 +244,7 @@ impl Window { // Send an initial window resized event so the user is alerted of // the correct dpi scaling. - handler.on_event(Event::Window(WindowEvent::Resized { - size: physical_size.cast(), - scale_factor: scaling, - })); + handler.resized(WindowSize::from_physical(physical_size.cast(), scaling)); let _ = tx.send(Ok(window_id));