From 7acb37f3e39446ba39ca8a2793387dc6ca7479b1 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:36:34 +0100 Subject: [PATCH 1/9] wip --- src/platform/win/window.rs | 23 ++++++----------------- src/utils.rs | 3 +++ src/window.rs | 6 +++--- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 4a47047d..9f8b3197 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -35,7 +35,7 @@ fn lo_word(lparam: LPARAM) -> u16 { } pub struct WindowHandle { - init: Cell>, + init: Cell>>, hwnd: Cell>, state: Rc, } @@ -52,18 +52,6 @@ impl WindowHandle { self.state.is_alive.get() } - pub fn is_resizable(&self) -> bool { - self.state.sizing_strategy.is_resizable() - } - - pub fn min_size(&self) -> Option { - self.state.sizing_strategy.min_size() - } - - pub fn max_size(&self) -> Option { - self.state.sizing_strategy.max_size() - } - pub fn size(&self) -> WindowSize { self.state.size() } @@ -97,6 +85,7 @@ impl WindowHandle { } } + #[inline] pub fn sizing_strategy(&self) -> SizingStrategy { self.state.sizing_strategy } @@ -152,7 +141,7 @@ impl WindowHandle { let Some(mut init) = self.init.take() else { return Ok(()) }; init.settings.parent = Some(new_parent.into()); - let window = BaseviewWindow::create(Rc::clone(&self.state), init)?; + let window = BaseviewWindow::create(Rc::clone(&self.state), *init)?; self.hwnd.set(Some(window)); return Ok(()); @@ -179,10 +168,10 @@ impl WindowHandle { None => { let Some(init) = self.init.take() else { return Ok(()) }; - let window = BaseviewWindow::create(Rc::clone(&self.state), init)?; + let window = BaseviewWindow::create(Rc::clone(&self.state), *init)?; self.hwnd.set(Some(window)); - return Ok(()); + window } }; @@ -881,7 +870,7 @@ impl WindowHandle { return Ok(WindowHandle { hwnd: None.into(), state: shared_state, - init: Some(init).into(), + init: Some(Box::new(init)).into(), }); } diff --git a/src/utils.rs b/src/utils.rs index 934ad460..446cebe4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -23,10 +23,12 @@ impl SizingStrategy { Self::Resizable { min_size: settings.min_size, max_size: settings.max_size } } + #[inline] pub fn is_resizable(&self) -> bool { matches!(self, Self::Resizable { .. }) } + #[inline] pub fn min_size(&self) -> Option { match self { Self::Fixed => None, @@ -34,6 +36,7 @@ impl SizingStrategy { } } + #[inline] pub fn max_size(&self) -> Option { match self { Self::Fixed => None, diff --git a/src/window.rs b/src/window.rs index 2708e7c2..7da06306 100644 --- a/src/window.rs +++ b/src/window.rs @@ -157,7 +157,7 @@ impl Window { /// This is set by the [`WindowSettings::resizable`] field. #[inline] pub fn is_resizable(&self) -> bool { - self.inner.is_resizable() + self.inner.sizing_strategy().is_resizable() } /// Returns the minimum size of the window, if it has one. @@ -165,7 +165,7 @@ impl Window { /// This is set by the [`WindowSettings::min_size`] field. #[inline] pub fn min_size(&self) -> Option { - self.inner.min_size() + self.inner.sizing_strategy().min_size() } /// Returns the minimum size of the window, if it has one. @@ -173,7 +173,7 @@ impl Window { /// This is set by the [`WindowSettings::max_size`] field. #[inline] pub fn max_size(&self) -> Option { - self.inner.max_size() + self.inner.sizing_strategy().max_size() } /// Performs the work the window thread had scheduled for the main thread. From b1c8fb116064ba0e2dc5566ed845ff33591417d2 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:39:06 +0100 Subject: [PATCH 2/9] wip --- src/platform/win/window.rs | 238 ++---------------------- src/platform/win/window/sizing.rs | 217 +++++++++++++++++++++ src/wrappers/win32/window.rs | 5 +- src/wrappers/win32/window/data.rs | 10 - src/wrappers/win32/window/interfaces.rs | 67 +++++++ src/wrappers/win32/window/proc.rs | 3 +- 6 files changed, 300 insertions(+), 240 deletions(-) create mode 100644 src/platform/win/window/sizing.rs create mode 100644 src/wrappers/win32/window/interfaces.rs diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 9f8b3197..fd71efcd 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -1,14 +1,12 @@ use windows_core::{ComObject, HSTRING}; use windows_sys::Win32::{ - Foundation::{LPARAM, LRESULT, RECT, WPARAM}, + Foundation::{LPARAM, LRESULT, WPARAM}, UI::{Controls::WM_MOUSELEAVE, WindowsAndMessaging::*}, }; use crate::dpi::{PhysicalPosition, PhysicalSize, Size}; -use crate::{warn, DamageArea, EventStatus, HandlerError, WindowHandler}; +use crate::{warn, DamageArea, EventStatus, WindowHandler}; use std::cell::{Cell, OnceCell}; -use std::num::NonZeroU32; -use windows_sys::Win32::Foundation::POINT; use super::drop_target::DropTarget; use super::*; @@ -21,11 +19,13 @@ use crate::window::WindowInitializer; use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::window::*; use crate::wrappers::win32::{ - ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessGuard, LibraryModule, Rect, - TimerId, TimerType, WindowStyle, + ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessGuard, LibraryModule, TimerId, + TimerType, WindowStyle, }; use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowSize}; +mod sizing; + fn hi_word(wparam: WPARAM) -> u16 { ((wparam >> 16) & 0xffff) as u16 } @@ -63,12 +63,9 @@ impl WindowHandle { return Ok(()); } - let hwnd = match self.hwnd.get() { - Some(hwnd) => hwnd, - None => { - self.state.current_size.set(new_size); - return Ok(()); - } + let Some(hwnd) = self.hwnd.get() else { + self.state.current_size.set(new_size); + return Ok(()); }; let _guard = self.state.originate_host_resize(); @@ -292,37 +289,6 @@ impl BaseviewWindow { self.host.notify_destroyed() } - fn request_host_resize(&self, new_size: WindowSize) -> core::result::Result<(), HandlerError> { - if self.shared_state.resize_host_originated.get() { - return Ok(()); - }; - - self.host.request_resize(new_size) - } - - fn adapt_host_window_to_size( - &self, previous_size: PhysicalSize, new_size: WindowSize, - ) -> core::result::Result<(), ()> { - if let Err(e) = self.request_host_resize(new_size) { - warn!("Resize request from Host failed: {}. Reverting to previous size.", e); - - if let Some(handler) = self.handler.get() { - if let Err(e) = handler.resized(new_size) { - warn!("Window Handler failed to resize to previous window size: {}", e); - } - }; - - self.window_state.shared.current_size.set(previous_size); - if let Err(e) = self.window_state.resize(previous_size.into()) { - warn!("Failed to resize back to previous window size: {}", e); - } - - return Err(()); - } - - Ok(()) - } - pub(crate) fn notify_damage(&self, damage: DamageArea) { let Some(handler) = self.handler.get() else { return }; handler.damage(damage); @@ -399,42 +365,7 @@ impl WindowImpl for BaseviewWindow { self._keyboard_hook.set(Some(hook::init_keyboard_hook(window.as_raw()))); // Now we can get the actual dpi of the window. - let dpi = window_state - .shared - .dpi_scaling_strategy - .get() - .get_dpi_for_window(window, &self.shared_state.user32); - - if let Some(dpi) = dpi { - if dpi != window_state.shared.current_dpi.get() { - window_state.shared.current_dpi.set(dpi); - - // We cannot create a window in "logical" pixels, and we can't DPI-scale to physical pixels because we - // have no way to know where the window will end up. - // So, at window creation, we assume a DPI=96 (or parent DPI if available), and if it ends up wrong, we resize the window - // to the actual logical size the user desired. - 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. - let previous_size = window_state.shared.current_size.replace(new_size); - let guard = DpiAwarenessGuard::new( - &window_state.shared.user32, - self.shared_state.dpi_scaling_strategy.get(), - )?; - window.resize_and_activate(new_size, dpi, &guard)?; - - self.host_needs_new_size_notified_on_show.set(Some(previous_size)); - } else { - // If the host queried size before it gave us a parent, this is the one it's been using. - let previous_host_size = self.initial_size.to_physical(1.0); - let current_size = self.initial_size.to_physical(dpi.scale_factor()); - - if previous_host_size != current_size { - self.host_needs_new_size_notified_on_show.set(Some(previous_host_size)); - } - } - } + self.initialize_window_dpi(window)?; let drop_target = ComObject::new(DropTarget::new(Rc::downgrade(window_state), window)); self._drop_target.set(Some(drop_target.clone())); @@ -673,113 +604,8 @@ unsafe fn wnd_proc_inner( None } - WM_SIZE => { - let width = (lparam & 0xFFFF) as u16 as u32; - let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; - - let new_size = PhysicalSize { width, height }; - let current_size = window_state.shared.current_size.get(); - - // Only send the event if anything changed - if current_size == new_size { - return None; - } - - let previous = window_state.shared.current_size.replace(new_size); - let new_size = WindowSize::from_physical(new_size, window_state.shared.scale_factor()); - - let handler = window_bv.handler.get()?; - if let Err(e) = handler.resized(new_size) { - warn!("Window Handler failed to resize: {}", e); - window_state.shared.current_size.set(previous); - - if let Err(e) = window_state.resize(previous.into()) { - warn!("Failed to resize back to previous window size: {}", e); - } - - return Some(-1); - } - - if let Err(e) = window_bv.request_host_resize(new_size) { - warn!("Resize request from Host failed: {}. Reverting to previous size.", e); - - if let Err(e) = handler.resized(new_size) { - warn!("Window Handler failed to resize to previous window size: {}", e); - } - - window_state.shared.current_size.set(previous); - if let Err(e) = window_state.resize(previous.into()) { - warn!("Failed to resize back to previous window size: {}", e); - } - - return Some(-1); - } - - window.invalidate_window(); - - None - } WM_SHOWWINDOW => { - if let Some(previous_size) = window_bv.host_needs_new_size_notified_on_show.take() { - let new_size = window_state.shared.current_size.get(); - let dpi = window_state.shared.current_dpi.get(); - - let _ = window_bv.adapt_host_window_to_size( - previous_size, - WindowSize::from_physical(new_size, dpi.scale_factor()), - ); - } - - None - } - WM_DPICHANGED => { - let suggested_nc_rect = Rect((lparam as *const RECT).read()); - let Some(dpi) = NonZeroU32::new((wparam & 0xFFFF) as u16 as u32) else { - return Some(-1); - }; - let dpi = Dpi(dpi); - - let dpi_ctx = DpiAwarenessGuard::new( - &window_state.user32, - window_state.shared.dpi_scaling_strategy.get(), - ) - .unwrap(); - let style = window.get_style().unwrap(); - let suggested_rect = - dpi_ctx.nc_area_to_client_area(suggested_nc_rect, style, Some(dpi)).unwrap(); - - let new_size = suggested_rect.size(); - - let changed = window_state.shared.current_size.get() != new_size - || window_state.shared.current_dpi.get() != dpi; - - window_state.shared.current_dpi.set(dpi); - let previous_size = window_state.shared.current_size.replace(new_size); - - // Windows makes us resize the window manually. This however will not send a WM_SIZE event, - // hence why we are notifying the window handler manually below. - let _ = window.set_nc_rect(suggested_nc_rect); - - if changed { - let handler = window_bv.handler.get()?; - let new_size = WindowSize::from_physical(new_size, dpi.scale_factor()); - - if let Err(e) = handler.resized(new_size) { - warn!("Window Handler failed to resize: {}", e); - window_state.shared.current_size.set(previous_size); - - if let Err(e) = window_state.resize(previous_size.into()) { - warn!("Failed to resize back to previous window size: {}", e); - } - - return Some(-1); - } - - if let Err(()) = window_bv.adapt_host_window_to_size(previous_size, new_size) { - return Some(-1); - } - } - + window_bv.handle_new_size_on_show(); None } // If WM_SETCURSOR returns `None`, WM_SETCURSOR continues to get handled by the outer window(s), @@ -798,48 +624,6 @@ unsafe fn wnd_proc_inner( None } } - WM_GETMINMAXINFO => { - let sizing = window_state.shared.sizing_strategy; - - // Only implement this message if we actually need to specify a min/max size - if let (None, None) = (sizing.min_size(), sizing.max_size()) { - return None; - } - - let info = lparam as *mut MINMAXINFO; - - let ctx = DpiAwarenessGuard::new( - &window_state.user32, - window_state.shared.dpi_scaling_strategy.get(), - ) - .unwrap(); - let style = window.get_style().unwrap(); - let dpi = window_state.shared.current_dpi.get(); - - if let Some(size) = sizing.min_size() { - let size = size.to_physical(window_state.shared.scale_factor()); - let size = ctx - .client_area_to_nc_area(size.into(), style, Some(dpi)) - .unwrap() - .size() - .cast(); - let pt = POINT { x: size.width, y: size.height }; - (&raw mut (*info).ptMinTrackSize).write(pt); - } - - if let Some(size) = sizing.max_size() { - let size = size.to_physical(window_state.shared.scale_factor()); - let size = ctx - .client_area_to_nc_area(size.into(), style, Some(dpi)) - .unwrap() - .size() - .cast(); - let pt = POINT { x: size.width, y: size.height }; - (&raw mut (*info).ptMaxTrackSize).write(pt); - } - - Some(0) - } // NOTE: `WM_NCDESTROY` is handled in the outer function because this deallocates the window // state BV_WINDOW_MUST_CLOSE => { diff --git a/src/platform/win/window/sizing.rs b/src/platform/win/window/sizing.rs new file mode 100644 index 00000000..d8156895 --- /dev/null +++ b/src/platform/win/window/sizing.rs @@ -0,0 +1,217 @@ +use crate::platform::{BaseviewWindow, PlatformError}; +use crate::wrappers::win32::window::{HWnd, WmSizing}; +use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect}; +use crate::{HandlerError, WindowSize}; +use dpi::PhysicalSize; +use tracing::warn; +use windows_sys::Win32::Foundation::POINT; +use windows_sys::Win32::UI::WindowsAndMessaging::MINMAXINFO; + +impl BaseviewWindow { + fn adapt_host_window_to_size( + &self, previous_size: PhysicalSize, new_size: WindowSize, + ) -> Result<(), ()> { + if let Err(e) = self.request_host_resize(new_size) { + warn!("Resize request from Host failed: {}. Reverting to previous size.", e); + + if let Some(handler) = self.handler.get() { + if let Err(e) = handler.resized(new_size) { + warn!("Window Handler failed to resize to previous window size: {}", e); + } + }; + + self.window_state.shared.current_size.set(previous_size); + if let Err(e) = self.window_state.resize(previous_size.into()) { + warn!("Failed to resize back to previous window size: {}", e); + } + + return Err(()); + } + + Ok(()) + } + + fn request_host_resize(&self, new_size: WindowSize) -> Result<(), HandlerError> { + if self.shared_state.resize_host_originated.get() { + return Ok(()); + }; + + self.host.request_resize(new_size) + } + + pub fn handle_new_size_on_show(&self) { + if let Some(previous_size) = self.host_needs_new_size_notified_on_show.take() { + let new_size = self.shared_state.current_size.get(); + let dpi = self.shared_state.current_dpi.get(); + + let _ = self.adapt_host_window_to_size( + previous_size, + WindowSize::from_physical(new_size, dpi.scale_factor()), + ); + } + } + + pub fn initialize_window_dpi(&self, window: HWnd) -> Result<(), PlatformError> { + let dpi = self + .shared_state + .dpi_scaling_strategy + .get() + .get_dpi_for_window(window, &self.shared_state.user32); + + let Some(dpi) = dpi else { return Ok(()) }; + + if dpi == self.shared_state.current_dpi.get() { + // If the host queried size before it gave us a parent, this is the one it's been using. + let previous_host_size = self.initial_size.to_physical(1.0); + let current_size = self.initial_size.to_physical(dpi.scale_factor()); + + if previous_host_size != current_size { + self.host_needs_new_size_notified_on_show.set(Some(previous_host_size)); + } + } else { + self.shared_state.current_dpi.set(dpi); + + // We cannot create a window in "logical" pixels, and we can't DPI-scale to physical pixels because we + // have no way to know where the window will end up. + // So, at window creation, we assume a DPI=96 (or parent DPI if available), and if it ends up wrong, we resize the window + // to the actual logical size the user desired. + 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. + let previous_size = self.shared_state.current_size.replace(new_size); + let guard = DpiAwarenessGuard::new( + &self.shared_state.user32, + self.shared_state.dpi_scaling_strategy.get(), + )?; + window.resize_and_activate(new_size, dpi, &guard)?; + + self.host_needs_new_size_notified_on_show.set(Some(previous_size)); + } + + Ok(()) + } +} + +impl WmSizing for BaseviewWindow { + fn on_size(&self, window: HWnd, new_size: PhysicalSize) -> Result<(), ()> { + let current_size = self.shared_state.current_size.get(); + + // Only send the event if anything changed + if current_size == new_size { + return Ok(()); + } + + let previous = self.shared_state.current_size.replace(new_size); + let new_size = WindowSize::from_physical(new_size, self.shared_state.scale_factor()); + + let Some(handler) = self.handler.get() else { return Ok(()) }; + if let Err(e) = handler.resized(new_size) { + warn!("Window Handler failed to resize: {}", e); + self.shared_state.current_size.set(previous); + + if let Err(e) = self.window_state.resize(previous.into()) { + warn!("Failed to resize back to previous window size: {}", e); + } + + return Err(()); + } + + if let Err(e) = self.request_host_resize(new_size) { + warn!("Resize request from Host failed: {}. Reverting to previous size.", e); + + if let Err(e) = handler.resized(new_size) { + warn!("Window Handler failed to resize to previous window size: {}", e); + } + + self.shared_state.current_size.set(previous); + if let Err(e) = self.window_state.resize(previous.into()) { + warn!("Failed to resize back to previous window size: {}", e); + } + + return Err(()); + } + + window.invalidate_window(); + + Ok(()) + } + + fn on_dpi_changed( + &self, window: HWnd, new_dpi: Dpi, suggested_nc_rect: Rect, + ) -> Result<(), ()> { + let dpi_ctx = DpiAwarenessGuard::new( + &self.shared_state.user32, + self.shared_state.dpi_scaling_strategy.get(), + ) + .unwrap(); + let style = window.get_style().unwrap(); + let suggested_rect = + dpi_ctx.nc_area_to_client_area(suggested_nc_rect, style, Some(new_dpi)).unwrap(); + + let new_size = suggested_rect.size(); + + let changed = self.shared_state.current_size.get() != new_size + || self.shared_state.current_dpi.get() != new_dpi; + + self.shared_state.current_dpi.set(new_dpi); + let previous_size = self.shared_state.current_size.replace(new_size); + + // Windows makes us resize the window manually. This however will not send a WM_SIZE event, + // hence why we are notifying the window handler manually below. + let _ = window.set_nc_rect(suggested_nc_rect); + + if changed { + let Some(handler) = self.handler.get() else { return Ok(()) }; + let new_size = WindowSize::from_physical(new_size, new_dpi.scale_factor()); + + if let Err(e) = handler.resized(new_size) { + warn!("Window Handler failed to resize: {}", e); + self.shared_state.current_size.set(previous_size); + + if let Err(e) = self.window_state.resize(previous_size.into()) { + warn!("Failed to resize back to previous window size: {}", e); + } + + return Err(()); + } + + self.adapt_host_window_to_size(previous_size, new_size)?; + } + + Ok(()) + } + + fn get_min_max_size( + &self, window: HWnd, + ) -> (Option>, Option>) { + let sizing = self.shared_state.sizing_strategy; + + // Only implement this message if we actually need to specify a min/max size + if let (None, None) = (sizing.min_size(), sizing.max_size()) { + return (None, None); + } + + let ctx = DpiAwarenessGuard::new( + &self.shared_state.user32, + self.shared_state.dpi_scaling_strategy.get(), + ) + .unwrap(); + let style = window.get_style().unwrap(); + let dpi = self.shared_state.current_dpi.get(); + + let min_size = sizing.min_size().map(|size| { + let size = size.to_physical(self.shared_state.scale_factor()); + + ctx.client_area_to_nc_area(size.into(), style, Some(dpi)).unwrap().size().cast() + }); + + let max_size = sizing.max_size().map(|size| { + let size = size.to_physical(self.shared_state.scale_factor()); + + ctx.client_area_to_nc_area(size.into(), style, Some(dpi)).unwrap().size().cast() + }); + + (min_size, max_size) + } +} diff --git a/src/wrappers/win32/window.rs b/src/wrappers/win32/window.rs index da2cb8a2..12dcb98c 100644 --- a/src/wrappers/win32/window.rs +++ b/src/wrappers/win32/window.rs @@ -1,8 +1,11 @@ mod data; mod handle; +mod interfaces; mod proc; mod window_class; +pub use interfaces::*; + #[cfg(feature = "opengl")] mod dc; @@ -29,7 +32,7 @@ use crate::wrappers::win32::style::WindowStyle; use windows_sys::Win32::Foundation::{LPARAM, LRESULT, WPARAM}; use windows_sys::Win32::UI::WindowsAndMessaging::CreateWindowExW; -pub trait WindowImpl: 'static { +pub trait WindowImpl: WmSizing + 'static { fn non_client_create( &self, window: HWnd, ) -> core::result::Result<(), crate::platform::PlatformError>; diff --git a/src/wrappers/win32/window/data.rs b/src/wrappers/win32/window/data.rs index 769be955..cb1e75dd 100644 --- a/src/wrappers/win32/window/data.rs +++ b/src/wrappers/win32/window/data.rs @@ -73,14 +73,4 @@ impl WindowData { pub fn inner(&self) -> Option<&W> { self.inner_impl.get() } - - pub unsafe fn handle_message( - &self, window: HWnd, message_code: u32, w_param: WPARAM, l_param: LPARAM, - ) -> Option { - if let Some(inner) = self.inner_impl.get() { - inner.handle_message(window, message_code, w_param, l_param) - } else { - None - } - } } diff --git a/src/wrappers/win32/window/interfaces.rs b/src/wrappers/win32/window/interfaces.rs new file mode 100644 index 00000000..6720368e --- /dev/null +++ b/src/wrappers/win32/window/interfaces.rs @@ -0,0 +1,67 @@ +use crate::wrappers::win32::window::{HWnd, WindowData, WindowImpl}; +use crate::wrappers::win32::{Dpi, Rect}; +use dpi::PhysicalSize; +use std::num::NonZeroU32; +use windows_sys::Win32::Foundation::{LPARAM, LRESULT, POINT, RECT, WPARAM}; +use windows_sys::Win32::UI::WindowsAndMessaging::*; + +pub trait WmSizing { + fn on_size(&self, window: HWnd, new_size: PhysicalSize) -> Result<(), ()>; + fn on_dpi_changed(&self, window: HWnd, new_dpi: Dpi, suggested_nc_rect: Rect) + -> Result<(), ()>; + fn get_min_max_size( + &self, window: HWnd, + ) -> (Option>, Option>); +} + +pub unsafe fn handle_message( + data: &WindowData, window: HWnd, message_code: u32, wparam: WPARAM, lparam: LPARAM, +) -> Option { + let inner = data.inner()?; + + match message_code { + WM_SIZE => { + let width = (lparam & 0xFFFF) as u16 as u32; + let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; + let new_size = PhysicalSize { width, height }; + + match inner.on_size(window, new_size) { + Ok(()) => None, + Err(()) => Some(-1), + } + } + WM_DPICHANGED => { + let suggested_nc_rect = Rect((lparam as *const RECT).read()); + let Some(dpi) = NonZeroU32::new((wparam & 0xFFFF) as u16 as u32) else { + return Some(-1); + }; + let dpi = Dpi(dpi); + + match inner.on_dpi_changed(window, dpi, suggested_nc_rect) { + Ok(()) => None, + Err(()) => Some(-1), + } + } + WM_GETMINMAXINFO => { + let (min_size, max_size) = inner.get_min_max_size(window); + + if matches!((min_size, max_size), (None, None)) { + return None; + } + + let info = lparam as *mut MINMAXINFO; + + if let Some(size) = min_size { + (&raw mut (*info).ptMinTrackSize).write(POINT { x: size.width, y: size.height }); + } + + if let Some(size) = max_size { + (&raw mut (*info).ptMaxTrackSize).write(POINT { x: size.width, y: size.height }); + } + + Some(0) + } + + _ => None, + } +} diff --git a/src/wrappers/win32/window/proc.rs b/src/wrappers/win32/window/proc.rs index ab2e0365..80507904 100644 --- a/src/wrappers/win32/window/proc.rs +++ b/src/wrappers/win32/window/proc.rs @@ -85,8 +85,7 @@ pub unsafe extern "system" fn wnd_proc( // even if the event handler leads to the window being destroyed unsafe { WindowData::handle(inner_ptr, |inner| { - inner - .handle_message(window, message_code, w_param, l_param) + handle_message(inner, window, message_code, w_param, l_param) .unwrap_or_else(handle_default) }) } From fe3c3b24e2504312926488c4af2b24991ebdf4eb Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:59:31 +0100 Subject: [PATCH 3/9] wip --- src/platform/win/window.rs | 144 +----------------------- src/platform/win/window/mouse.rs | 87 ++++++++++++++ src/wrappers/win32/timer.rs | 35 +++++- src/wrappers/win32/window.rs | 2 +- src/wrappers/win32/window/interfaces.rs | 93 ++++++++++++++- 5 files changed, 212 insertions(+), 149 deletions(-) create mode 100644 src/platform/win/window/mouse.rs diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index fd71efcd..1475dc49 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -20,20 +20,13 @@ use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::window::*; use crate::wrappers::win32::{ ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessGuard, LibraryModule, TimerId, - TimerType, WindowStyle, + TimerType, WindowStyle, }; use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowSize}; +mod mouse; mod sizing; -fn hi_word(wparam: WPARAM) -> u16 { - ((wparam >> 16) & 0xffff) as u16 -} - -fn lo_word(lparam: LPARAM) -> u16 { - (lparam & 0xffff) as u16 -} - pub struct WindowHandle { init: Cell>>, hwnd: Cell>, @@ -415,139 +408,6 @@ unsafe fn wnd_proc_inner( ) -> Option { let window_state = &window_bv.window_state; match msg { - WM_MOUSEMOVE => { - if window_state.mouse_was_outside_window.get() { - // this makes Windows track whether the mouse leaves the window. - // When the mouse leaves it results in a `WM_MOUSELEAVE` event. - // Couldn't find a good way to track whether the mouse enters, - // but if `WM_MOUSEMOVE` happens, the mouse must have entered. - let _ = window.start_cursor_leave_tracking(); - window_state.mouse_was_outside_window.set(false); - - let enter_event = Event::Mouse(MouseEvent::CursorEntered); - window_bv.handle_event(enter_event); - } - - let x = (lparam & 0xFFFF) as i16 as i32; - let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; - - let move_event = Event::Mouse(MouseEvent::CursorMoved { - position: PhysicalPosition { x, y }.cast(), - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - }); - - window_bv.handle_event(move_event); - Some(0) - } - - WM_MOUSELEAVE => { - window_bv.handle_event(Event::Mouse(MouseEvent::CursorLeft)); - - window_state.mouse_was_outside_window.set(true); - Some(0) - } - WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { - let value = (wparam >> 16) as i16; - let value = value as i32; - let value = value as f32 / WHEEL_DELTA as f32; - - let event = Event::Mouse(MouseEvent::WheelScrolled { - delta: if msg == WM_MOUSEWHEEL { - ScrollDelta::Lines { x: 0.0, y: value } - } else { - ScrollDelta::Lines { x: value, y: 0.0 } - }, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - }); - - window_bv.handle_event(event); - Some(0) - } - WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN - | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { - let mut mouse_button_counter = window_state.mouse_button_counter.get(); - - fn get_xbutton_wparam(wparam: WPARAM) -> u16 { - hi_word(wparam) - } - - const XBUTTON1: u16 = 0x1; - const XBUTTON2: u16 = 0x2; - - let button = match msg { - WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left), - WM_MBUTTONDOWN | WM_MBUTTONUP => Some(MouseButton::Middle), - WM_RBUTTONDOWN | WM_RBUTTONUP => Some(MouseButton::Right), - WM_XBUTTONDOWN | WM_XBUTTONUP => match get_xbutton_wparam(wparam) { - XBUTTON1 => Some(MouseButton::Back), - XBUTTON2 => Some(MouseButton::Forward), - _ => None, - }, - _ => None, - }; - - if let Some(button) = button { - let event = match msg { - WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => { - // Capture the mouse cursor on button down - mouse_button_counter = mouse_button_counter.saturating_add(1); - window.set_capture(); - MouseEvent::ButtonPressed { - button, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - } - } - WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => { - // Release the mouse cursor capture when all buttons are released - mouse_button_counter = mouse_button_counter.saturating_sub(1); - if mouse_button_counter == 0 { - HWnd::release_capture(); - } - - MouseEvent::ButtonReleased { - button, - modifiers: window_state - .keyboard_state - .borrow() - .get_modifiers_from_mouse_wparam(wparam), - } - } - _ => { - unreachable!() - } - }; - - window_state.mouse_button_counter.set(mouse_button_counter); - window_bv.handle_event(Event::Mouse(event)); - } - - None - } - WM_CAPTURECHANGED => { - let window_gaining_capture = - NonNull::new(lparam as _).map(|p| unsafe { HWnd::from_raw(p) }); - - if window_gaining_capture != Some(window) { - let previous_counter = window_state.mouse_button_counter.replace(0); - HWnd::release_capture(); - - if previous_counter == 0 { - window_bv.handle_event(Event::Mouse(MouseEvent::CursorLeft)); - window_state.mouse_was_outside_window.set(true); - } - } - - Some(0) - } WM_PAINT => { if let Some(rect) = window.get_update_rect() { window_bv.notify_damage(DamageArea::Rect(rect.into())); diff --git a/src/platform/win/window/mouse.rs b/src/platform/win/window/mouse.rs new file mode 100644 index 00000000..2527ab98 --- /dev/null +++ b/src/platform/win/window/mouse.rs @@ -0,0 +1,87 @@ +use crate::platform::BaseviewWindow; +use crate::wrappers::win32::window::{HWnd, WmMouse}; +use crate::{Event, MouseButton, MouseEvent, ScrollDelta}; +use dpi::PhysicalPosition; +use windows_sys::Win32::Foundation::WPARAM; + +impl WmMouse for BaseviewWindow { + fn on_move(&self, window: HWnd, position: PhysicalPosition, wparam: WPARAM) { + if self.window_state.mouse_was_outside_window.get() { + // this makes Windows track whether the mouse leaves the window. + // When the mouse leaves it results in a `WM_MOUSELEAVE` event. + // Couldn't find a good way to track whether the mouse enters, + // but if `WM_MOUSEMOVE` happens, the mouse must have entered. + let _ = window.start_cursor_leave_tracking(); + self.window_state.mouse_was_outside_window.set(false); + + self.handle_event(Event::Mouse(MouseEvent::CursorEntered)); + } + + let move_event = Event::Mouse(MouseEvent::CursorMoved { + position: position.cast(), + modifiers: self + .window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), + }); + + self.handle_event(move_event); + } + + fn on_leave(&self, _: HWnd) { + self.window_state.mouse_was_outside_window.set(true); + self.handle_event(Event::Mouse(MouseEvent::CursorLeft)); + } + + fn on_wheel(&self, _: HWnd, delta: ScrollDelta, wparam: WPARAM) { + let event = Event::Mouse(MouseEvent::WheelScrolled { + delta, + modifiers: self + .window_state + .keyboard_state + .borrow() + .get_modifiers_from_mouse_wparam(wparam), + }); + + self.handle_event(event); + } + + fn on_capture_changed(&self, window: HWnd, window_gaining_capture: Option) { + if window_gaining_capture != Some(window) { + let previous_counter = self.window_state.mouse_button_counter.replace(0); + HWnd::release_capture(); + + if previous_counter == 0 { + self.handle_event(Event::Mouse(MouseEvent::CursorLeft)); + self.window_state.mouse_was_outside_window.set(true); + } + } + } + + fn on_button(&self, window: HWnd, button: MouseButton, down: bool, wparam: WPARAM) { + let mut mouse_button_counter = self.window_state.mouse_button_counter.get(); + let modifiers = + self.window_state.keyboard_state.borrow().get_modifiers_from_mouse_wparam(wparam); + + let event = if down { + // Capture the mouse cursor on button down + mouse_button_counter = mouse_button_counter.saturating_add(1); + window.set_capture(); + + MouseEvent::ButtonPressed { button, modifiers } + } else { + mouse_button_counter = mouse_button_counter.saturating_sub(1); + + // Release the mouse cursor capture when all buttons are released + if mouse_button_counter == 0 { + HWnd::release_capture(); + } + + MouseEvent::ButtonReleased { button, modifiers } + }; + + self.window_state.mouse_button_counter.set(mouse_button_counter); + self.handle_event(Event::Mouse(event)); + } +} diff --git a/src/wrappers/win32/timer.rs b/src/wrappers/win32/timer.rs index 5e8406dc..d4b07e60 100644 --- a/src/wrappers/win32/timer.rs +++ b/src/wrappers/win32/timer.rs @@ -1,7 +1,10 @@ use crate::platform::{BaseviewWindow, PlatformError}; -use crate::wrappers::win32::window::{create_window, use_window, HWnd, WindowImpl}; -use crate::wrappers::win32::{DpiAwarenessGuard, WindowStyle}; -use dpi::PhysicalSize; +use crate::wrappers::win32::window::{ + create_window, use_window, HWnd, WindowImpl, WmMouse, WmSizing, +}; +use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect, WindowStyle}; +use crate::{MouseButton, ScrollDelta}; +use dpi::{PhysicalPosition, PhysicalSize}; use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::num::NonZeroUsize; @@ -206,8 +209,8 @@ impl WindowImpl for TimerWindow { // SAFETY: baseview_window is of type BaseviewWindow unsafe { - use_window::(self.baseview_window, |hwnd| { - (self.timer_fn)(hwnd, timer_id) + use_window::(self.baseview_window, |window| { + (self.timer_fn)(window, timer_id) }) }; @@ -216,3 +219,25 @@ impl WindowImpl for TimerWindow { fn before_destroy(&self, _: HWnd) {} } + +impl WmSizing for TimerWindow { + fn on_size(&self, _: HWnd, _: PhysicalSize) -> Result<(), ()> { + Ok(()) + } + + fn on_dpi_changed(&self, _: HWnd, _: Dpi, _: Rect) -> Result<(), ()> { + Ok(()) + } + + fn get_min_max_size(&self, _: HWnd) -> (Option>, Option>) { + (None, None) + } +} + +impl WmMouse for TimerWindow { + fn on_move(&self, window: HWnd, position: PhysicalPosition, wparam: WPARAM) {} + fn on_leave(&self, _: HWnd) {} + fn on_wheel(&self, window: HWnd, delta: ScrollDelta, wparam: WPARAM) {} + fn on_capture_changed(&self, _: HWnd, _: Option) {} + fn on_button(&self, window: HWnd, button: MouseButton, down: bool, wparam: WPARAM) {} +} diff --git a/src/wrappers/win32/window.rs b/src/wrappers/win32/window.rs index 12dcb98c..78981e2d 100644 --- a/src/wrappers/win32/window.rs +++ b/src/wrappers/win32/window.rs @@ -32,7 +32,7 @@ use crate::wrappers::win32::style::WindowStyle; use windows_sys::Win32::Foundation::{LPARAM, LRESULT, WPARAM}; use windows_sys::Win32::UI::WindowsAndMessaging::CreateWindowExW; -pub trait WindowImpl: WmSizing + 'static { +pub trait WindowImpl: WmSizing + WmMouse + 'static { fn non_client_create( &self, window: HWnd, ) -> core::result::Result<(), crate::platform::PlatformError>; diff --git a/src/wrappers/win32/window/interfaces.rs b/src/wrappers/win32/window/interfaces.rs index 6720368e..7d7bfc75 100644 --- a/src/wrappers/win32/window/interfaces.rs +++ b/src/wrappers/win32/window/interfaces.rs @@ -1,8 +1,11 @@ use crate::wrappers::win32::window::{HWnd, WindowData, WindowImpl}; use crate::wrappers::win32::{Dpi, Rect}; -use dpi::PhysicalSize; +use crate::{Event, MouseButton, MouseEvent, ScrollDelta}; +use dpi::{PhysicalPosition, PhysicalSize}; use std::num::NonZeroU32; +use std::ptr::NonNull; use windows_sys::Win32::Foundation::{LPARAM, LRESULT, POINT, RECT, WPARAM}; +use windows_sys::Win32::UI::Controls::WM_MOUSELEAVE; use windows_sys::Win32::UI::WindowsAndMessaging::*; pub trait WmSizing { @@ -14,12 +17,21 @@ pub trait WmSizing { ) -> (Option>, Option>); } +pub trait WmMouse { + fn on_move(&self, window: HWnd, position: PhysicalPosition, wparam: WPARAM); + fn on_leave(&self, window: HWnd); + fn on_wheel(&self, window: HWnd, delta: ScrollDelta, wparam: WPARAM); + fn on_capture_changed(&self, window: HWnd, window_gaining_capture: Option); + fn on_button(&self, window: HWnd, button: MouseButton, down: bool, wparam: WPARAM); +} + pub unsafe fn handle_message( data: &WindowData, window: HWnd, message_code: u32, wparam: WPARAM, lparam: LPARAM, ) -> Option { let inner = data.inner()?; match message_code { + // Sizing WM_SIZE => { let width = (lparam & 0xFFFF) as u16 as u32; let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; @@ -62,6 +74,85 @@ pub unsafe fn handle_message( Some(0) } + // Mouse + WM_MOUSEMOVE => { + inner.on_move( + window, + PhysicalPosition { + x: (lparam & 0xFFFF) as i16, + y: ((lparam >> 16) & 0xFFFF) as i16, + }, + wparam, + ); + + Some(0) + } + + WM_MOUSELEAVE => { + inner.on_leave(window); + Some(0) + } + WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { + let value = (wparam >> 16) as i16 as i32; + let value = value as f32 / WHEEL_DELTA as f32; + + let delta = if message_code == WM_MOUSEWHEEL { + ScrollDelta::Lines { x: 0.0, y: value } + } else { + ScrollDelta::Lines { x: value, y: 0.0 } + }; + + inner.on_wheel(window, delta, wparam); + + Some(0) + } + WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN + | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { + fn get_xbutton_wparam(wparam: WPARAM) -> u16 { + hi_word(wparam) + } + + const XBUTTON1: u16 = 0x1; + const XBUTTON2: u16 = 0x2; + + let button = match message_code { + WM_LBUTTONDOWN | WM_LBUTTONUP => MouseButton::Left, + WM_MBUTTONDOWN | WM_MBUTTONUP => MouseButton::Middle, + WM_RBUTTONDOWN | WM_RBUTTONUP => MouseButton::Right, + WM_XBUTTONDOWN | WM_XBUTTONUP => match get_xbutton_wparam(wparam) { + XBUTTON1 => MouseButton::Back, + XBUTTON2 => MouseButton::Forward, + _ => return None, + }, + _ => return None, + }; + + let down = match message_code { + WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN => true, + WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => false, + _ => unreachable!(), + }; + + inner.on_button(window, button, down, wparam); + + Some(0) + } + WM_CAPTURECHANGED => { + let window_gaining_capture = + NonNull::new(lparam as _).map(|p| unsafe { HWnd::from_raw(p) }); + + inner.on_capture_changed(window, window_gaining_capture); + + Some(0) + } _ => None, } } + +fn hi_word(wparam: WPARAM) -> u16 { + ((wparam >> 16) & 0xffff) as u16 +} + +fn lo_word(lparam: LPARAM) -> u16 { + (lparam & 0xffff) as u16 +} From 89295d7bfcf6b792bb27a7e597cad1e706032e32 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:29:58 +0100 Subject: [PATCH 4/9] cleanup --- src/platform/win/mod.rs | 120 +++++++----------------- src/platform/win/{window => }/mouse.rs | 0 src/platform/win/parent_handle.rs | 53 +++++++++++ src/platform/win/platform_handle.rs | 31 ++++++ src/platform/win/{window => }/sizing.rs | 0 src/platform/win/window.rs | 27 +----- src/platform/win/window_state.rs | 2 +- 7 files changed, 123 insertions(+), 110 deletions(-) rename src/platform/win/{window => }/mouse.rs (100%) create mode 100644 src/platform/win/parent_handle.rs create mode 100644 src/platform/win/platform_handle.rs rename src/platform/win/{window => }/sizing.rs (100%) diff --git a/src/platform/win/mod.rs b/src/platform/win/mod.rs index 6d38d505..8d176741 100644 --- a/src/platform/win/mod.rs +++ b/src/platform/win/mod.rs @@ -1,109 +1,61 @@ mod dpi; mod drop_target; mod error; +#[cfg(feature = "opengl")] +pub mod gl; mod hook; mod keyboard; +mod mouse; +mod parent_handle; +mod platform_handle; +mod sizing; mod waker; mod window; mod window_state; -use crate::wrappers::win32::h_instance::HInstance; -use crate::wrappers::win32::window::HWnd; -use crate::wrappers::win32::{Rect, TimerId}; pub(crate) use dpi::DpiScalingStrategy; pub use error::{PlatformError, Result}; -use raw_window_handle::{ - DisplayHandle, HandleError, HasWindowHandle, RawWindowHandle, Win32WindowHandle, -}; -use std::fmt::{Debug, Display, Formatter}; -use std::num::NonZeroIsize; -use std::ptr::NonNull; -use std::rc::Rc; +pub use parent_handle::*; +pub use platform_handle::*; pub use waker::WindowWaker; pub use window::*; -pub type TimerHandle = TimerId; +use crate::handler::WindowHandlerBuilder; +use crate::host::Host; +use crate::platform::win::drop_target::DropTarget; +use crate::platform::win::window_state::{WindowSharedState, WindowState}; +use crate::wrappers::win32::{Rect, TimerId}; +use crate::WindowHandler; +use ::dpi::{PhysicalSize, Size}; +use std::cell::{Cell, OnceCell}; +use std::rc::Rc; +use windows_core::ComObject; -#[cfg(feature = "opengl")] -pub mod gl; +pub type TimerHandle = TimerId; -pub type WindowContext = Rc; +pub type WindowContext = Rc; pub type DamageRect = Rect; -#[derive(Clone)] -pub struct PlatformHandle { - hwnd: NonZeroIsize, -} - -impl PlatformHandle { - pub fn window_handle(&self) -> Option> { - let mut handle = Win32WindowHandle::new(self.hwnd); - handle.hinstance = Some(HInstance::get_from_dll().addr()); - - Some(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) }) - } - - pub fn display_handle(&self) -> DisplayHandle<'_> { - DisplayHandle::windows() - } -} - -impl Debug for PlatformHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PlatformHandle (Win32)") - .field("hwnd", &self.hwnd.get()) - .field("hinstance", &HInstance::get_from_dll().addr().get()) - .finish() - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ParentWindowHandle { - handle: HWnd, +pub fn assume_standalone_in_process() { + dpi::set_process_dpi_awareness(); } -// SAFETY: ParentWindowHandle does not actually expose any thread-unsafe operation -unsafe impl Send for ParentWindowHandle {} -// SAFETY: ParentWindowHandle does not actually expose any thread-unsafe operation -unsafe impl Sync for ParentWindowHandle {} - -impl ParentWindowHandle { - pub fn extract( - parent: &impl HasWindowHandle, - ) -> core::result::Result { - let parent = match parent.window_handle()?.as_raw() { - RawWindowHandle::Win32(h) => h.hwnd, - h => return Err(ParentWindowHandleError::UnsupportedWindowHandleType(h)), - }; - - let Some(parent) = NonNull::new(parent.get() as _) else { unreachable!() }; +pub struct BaseviewWindow { + window_state: Rc, + shared_state: Rc, + initial_size: Size, - Ok(Self { handle: unsafe { HWnd::from_raw(parent) } }) - } -} + handler_builder: Cell>, + handler: OnceCell>, + host: Host, -pub enum ParentWindowHandleError { - HandleError(HandleError), - UnsupportedWindowHandleType(RawWindowHandle), -} + // Workaround some VST3 hosts (e.g. Bitwig) not allowing request_resize before show() occurs + pub host_needs_new_size_notified_on_show: Cell>>, -impl From for ParentWindowHandleError { - fn from(err: HandleError) -> Self { - Self::HandleError(err) - } -} + // Things not directly used, but kept so their Drop impl runs when the window is destroyed + _keyboard_hook: Cell>, + _drop_target: Cell>>, -impl Display for ParentWindowHandleError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - ParentWindowHandleError::HandleError(e) => Display::fmt(e, f), - ParentWindowHandleError::UnsupportedWindowHandleType(h) => { - write!(f, "Unsupported window handle type on Win32: {h:?}") - } - } - } -} - -pub fn assume_standalone_in_process() { - dpi::set_process_dpi_awareness(); + #[cfg(feature = "opengl")] + gl_config: Option, } diff --git a/src/platform/win/window/mouse.rs b/src/platform/win/mouse.rs similarity index 100% rename from src/platform/win/window/mouse.rs rename to src/platform/win/mouse.rs diff --git a/src/platform/win/parent_handle.rs b/src/platform/win/parent_handle.rs new file mode 100644 index 00000000..b42f8fb8 --- /dev/null +++ b/src/platform/win/parent_handle.rs @@ -0,0 +1,53 @@ +use crate::wrappers::win32::window::HWnd; +use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; +use std::fmt::{Display, Formatter}; +use std::ptr::NonNull; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ParentWindowHandle { + handle: HWnd, +} + +// SAFETY: ParentWindowHandle does not actually expose any thread-unsafe operation +unsafe impl Send for ParentWindowHandle {} +// SAFETY: ParentWindowHandle does not actually expose any thread-unsafe operation +unsafe impl Sync for ParentWindowHandle {} + +impl ParentWindowHandle { + pub fn extract(parent: &impl HasWindowHandle) -> Result { + let parent = match parent.window_handle()?.as_raw() { + RawWindowHandle::Win32(h) => h.hwnd, + h => return Err(ParentWindowHandleError::UnsupportedWindowHandleType(h)), + }; + + let Some(parent) = NonNull::new(parent.get() as _) else { unreachable!() }; + + Ok(Self { handle: unsafe { HWnd::from_raw(parent) } }) + } + + pub(crate) fn hwnd(&self) -> HWnd { + self.handle + } +} + +pub enum ParentWindowHandleError { + HandleError(HandleError), + UnsupportedWindowHandleType(RawWindowHandle), +} + +impl From for ParentWindowHandleError { + fn from(err: HandleError) -> Self { + Self::HandleError(err) + } +} + +impl Display for ParentWindowHandleError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ParentWindowHandleError::HandleError(e) => Display::fmt(e, f), + ParentWindowHandleError::UnsupportedWindowHandleType(h) => { + write!(f, "Unsupported window handle type on Win32: {h:?}") + } + } + } +} diff --git a/src/platform/win/platform_handle.rs b/src/platform/win/platform_handle.rs new file mode 100644 index 00000000..f5ced8ad --- /dev/null +++ b/src/platform/win/platform_handle.rs @@ -0,0 +1,31 @@ +use crate::wrappers::win32::h_instance::HInstance; +use raw_window_handle::{DisplayHandle, Win32WindowHandle}; +use std::fmt::{Debug, Formatter}; +use std::num::NonZeroIsize; + +#[derive(Clone)] +pub struct PlatformHandle { + hwnd: NonZeroIsize, +} + +impl PlatformHandle { + pub fn window_handle(&self) -> Option> { + let mut handle = Win32WindowHandle::new(self.hwnd); + handle.hinstance = Some(HInstance::get_from_dll().addr()); + + Some(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) }) + } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + DisplayHandle::windows() + } +} + +impl Debug for PlatformHandle { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PlatformHandle (Win32)") + .field("hwnd", &self.hwnd.get()) + .field("hinstance", &HInstance::get_from_dll().addr().get()) + .finish() + } +} diff --git a/src/platform/win/window/sizing.rs b/src/platform/win/sizing.rs similarity index 100% rename from src/platform/win/window/sizing.rs rename to src/platform/win/sizing.rs diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 1475dc49..d7a6487e 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -24,9 +24,6 @@ use crate::wrappers::win32::{ }; use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowSize}; -mod mouse; -mod sizing; - pub struct WindowHandle { init: Cell>>, hwnd: Cell>, @@ -142,7 +139,7 @@ impl WindowHandle { panic!("Called set_parent on a floating window") } - hwnd.set_parent(&new_parent.handle)?; + hwnd.set_parent(&new_parent.hwnd())?; Ok(()) } @@ -205,32 +202,12 @@ impl Drop for WindowHandle { } } -pub struct BaseviewWindow { - window_state: Rc, - shared_state: Rc, - initial_size: Size, - - handler_builder: Cell>, - handler: OnceCell>, - host: Host, - - // Workaround some VST3 hosts (e.g. Bitwig) not allowing request_resize before show() occurs - pub host_needs_new_size_notified_on_show: Cell>>, - - // Things not directly used, but kept so their Drop impl runs when the window is destroyed - _keyboard_hook: Cell>, - _drop_target: Cell>>, - - #[cfg(feature = "opengl")] - gl_config: Option, -} - impl BaseviewWindow { pub fn create(shared_state: Rc, init: WindowInitializer) -> Result { shared_state.init(&init); let style = WindowStyle::from_settings(&init.settings); - let parent = init.settings.parent.map(|p| p.inner.handle); + let parent = init.settings.parent.map(|p| p.inner.hwnd()); let dpi_ctx = DpiAwarenessGuard::new(&shared_state.user32, shared_state.dpi_scaling_strategy.get())?; diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index 395e6917..aee6a7fd 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -209,7 +209,7 @@ impl WindowSharedState { } pub fn init(&self, init: &WindowInitializer) { - let parent = init.settings.parent.as_ref().map(|p| p.inner.handle); + let parent = init.settings.parent.as_ref().map(|p| p.inner.hwnd()); let strategy = DpiScalingStrategy::get( Some(&self.user32), parent, From 4ac1357a76d327f27b2556efbdd661d6fa8d488c Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:21:08 +0100 Subject: [PATCH 5/9] wip --- src/platform/win/cursor.rs | 18 +++ src/platform/win/draw.rs | 36 ++++++ src/platform/win/keyboard.rs | 30 +++++ src/platform/win/mod.rs | 2 + src/platform/win/platform_handle.rs | 5 + src/platform/win/sizing.rs | 2 - src/platform/win/window.rs | 140 +++--------------------- src/platform/win/window_state.rs | 3 +- src/wrappers/win32/timer.rs | 68 +++++++----- src/wrappers/win32/window.rs | 15 +-- src/wrappers/win32/window/data.rs | 1 - src/wrappers/win32/window/interfaces.rs | 97 +++++++++++++++- 12 files changed, 249 insertions(+), 168 deletions(-) create mode 100644 src/platform/win/cursor.rs create mode 100644 src/platform/win/draw.rs diff --git a/src/platform/win/cursor.rs b/src/platform/win/cursor.rs new file mode 100644 index 00000000..6579fa03 --- /dev/null +++ b/src/platform/win/cursor.rs @@ -0,0 +1,18 @@ +use crate::platform::BaseviewWindow; +use crate::wrappers::win32::cursor::SystemCursor; +use crate::wrappers::win32::window::WmCursor; + +impl WmCursor for BaseviewWindow { + fn on_set_cursor(&self, mouse_in_window: bool) -> bool { + if mouse_in_window { + // Here we need to set the cursor back to what the state says, since it can have changed when outside the window + if let Ok(cursor) = SystemCursor::load(self.window_state.cursor_icon.get()) { + cursor.set() + } + true + } else { + // Cursor is being changed by some other window, e.g. when having mouse on the borders to resize it + false + } + } +} diff --git a/src/platform/win/draw.rs b/src/platform/win/draw.rs new file mode 100644 index 00000000..16773c2a --- /dev/null +++ b/src/platform/win/draw.rs @@ -0,0 +1,36 @@ +use crate::platform::win::window_state::RedrawTimer; +use crate::platform::BaseviewWindow; +use crate::wrappers::win32::window::{HWnd, WmDraw}; +use crate::wrappers::win32::{TimerId, TimerType}; +use crate::DamageArea; + +impl WmDraw for BaseviewWindow { + fn on_timer(&self, window: HWnd, timer_id: TimerId) -> bool { + if timer_id != RedrawTimer::ID { + return false; + } + + if !self.window_state.redraw_timer.is_running() { + return false; + }; + + window.invalidate_window(); + true + } + + fn on_paint(&self, window: HWnd) { + if let Some(rect) = window.get_update_rect() { + self.notify_damage(DamageArea::Rect(rect.into())); + self.handle_draw(); + window.validate_rect(rect); + } + } + + fn on_redraw_requested(&self) { + self.window_state.request_redraw(); + } + + fn on_poll_requested(&self) { + self.handle_poll(); + } +} diff --git a/src/platform/win/keyboard.rs b/src/platform/win/keyboard.rs index 03411513..9be8cd53 100644 --- a/src/platform/win/keyboard.rs +++ b/src/platform/win/keyboard.rs @@ -28,6 +28,9 @@ use std::collections::{HashMap, HashSet}; use std::mem; use std::ops::RangeInclusive; +use crate::platform::BaseviewWindow; +use crate::wrappers::win32::window::{HWnd, WmKeyboard}; +use crate::{Event, WindowEvent}; use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers, NamedKey}; use windows_sys::Win32::{ Foundation::{HWND, LPARAM, WPARAM}, @@ -695,3 +698,30 @@ impl KeyboardState { } } } + +impl WmKeyboard for BaseviewWindow { + fn on_key_event(&self, window: HWnd, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool { + let opt_event = unsafe { + self.window_state.keyboard_state.borrow_mut().process_message( + window.as_raw(), + msg, + wparam, + lparam, + ) + }; + + if let Some(event) = opt_event { + self.handle_event(Event::Keyboard(event)); + } + + msg != WM_SYSKEYDOWN + } + + fn on_focus(&self) { + self.handle_event(Event::Window(WindowEvent::Focused)); + } + + fn on_unfocus(&self) { + self.handle_event(Event::Window(WindowEvent::Unfocused)); + } +} diff --git a/src/platform/win/mod.rs b/src/platform/win/mod.rs index 8d176741..73263459 100644 --- a/src/platform/win/mod.rs +++ b/src/platform/win/mod.rs @@ -1,4 +1,6 @@ +mod cursor; mod dpi; +mod draw; mod drop_target; mod error; #[cfg(feature = "opengl")] diff --git a/src/platform/win/platform_handle.rs b/src/platform/win/platform_handle.rs index f5ced8ad..446c4ea1 100644 --- a/src/platform/win/platform_handle.rs +++ b/src/platform/win/platform_handle.rs @@ -1,4 +1,5 @@ use crate::wrappers::win32::h_instance::HInstance; +use crate::wrappers::win32::window::HWnd; use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::fmt::{Debug, Formatter}; use std::num::NonZeroIsize; @@ -9,6 +10,10 @@ pub struct PlatformHandle { } impl PlatformHandle { + pub fn new(hwnd: HWnd) -> Self { + let Some(hwnd) = NonZeroIsize::new(hwnd.as_raw() as _) else { unreachable!() }; + Self { hwnd } + } pub fn window_handle(&self) -> Option> { let mut handle = Win32WindowHandle::new(self.hwnd); handle.hinstance = Some(HInstance::get_from_dll().addr()); diff --git a/src/platform/win/sizing.rs b/src/platform/win/sizing.rs index d8156895..bdeff3c9 100644 --- a/src/platform/win/sizing.rs +++ b/src/platform/win/sizing.rs @@ -4,8 +4,6 @@ use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect}; use crate::{HandlerError, WindowSize}; use dpi::PhysicalSize; use tracing::warn; -use windows_sys::Win32::Foundation::POINT; -use windows_sys::Win32::UI::WindowsAndMessaging::MINMAXINFO; impl BaseviewWindow { fn adapt_host_window_to_size( diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index d7a6487e..2baf0028 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -1,28 +1,21 @@ use windows_core::{ComObject, HSTRING}; -use windows_sys::Win32::{ - Foundation::{LPARAM, LRESULT, WPARAM}, - UI::{Controls::WM_MOUSELEAVE, WindowsAndMessaging::*}, -}; -use crate::dpi::{PhysicalPosition, PhysicalSize, Size}; -use crate::{warn, DamageArea, EventStatus, WindowHandler}; +use crate::dpi::Size; +use crate::{warn, DamageArea, EventStatus}; use std::cell::{Cell, OnceCell}; use super::drop_target::DropTarget; use super::*; -use crate::handler::WindowHandlerBuilder; -use crate::host::Host; -use crate::platform::win::window_state::{RedrawTimer, WindowSharedState, WindowState}; +use crate::platform::win::window_state::{WindowSharedState, WindowState}; use crate::platform::PlatformError; use crate::utils::SizingStrategy; use crate::window::WindowInitializer; -use crate::wrappers::win32::cursor::SystemCursor; use crate::wrappers::win32::window::*; use crate::wrappers::win32::{ ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessGuard, LibraryModule, TimerId, - TimerType, WindowStyle, + WindowStyle, }; -use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowSize}; +use crate::{Event, WindowEvent, WindowSize}; pub struct WindowHandle { init: Cell>>, @@ -213,6 +206,8 @@ impl BaseviewWindow { DpiAwarenessGuard::new(&shared_state.user32, shared_state.dpi_scaling_strategy.get())?; let window_size = shared_state.current_size.get(); + let rect = dpi_ctx.client_area_to_nc_area(window_size.into(), style, None)?; + let title = HSTRING::from(init.settings.title); let initializer = { let shared_state = Rc::clone(&shared_state); @@ -243,12 +238,7 @@ impl BaseviewWindow { } } }; - - let rect = dpi_ctx.client_area_to_nc_area(window_size.into(), style, None)?; - let title = HSTRING::from(init.settings.title); - let window = create_window(&title, style, rect.size(), parent, &dpi_ctx, initializer)?; - - Ok(window) + Ok(create_window(&title, style, rect.size(), parent, &dpi_ctx, initializer)?) } fn notify_destroyed_to_host(&self) { @@ -365,119 +355,21 @@ impl WindowImpl for BaseviewWindow { Ok(()) } - unsafe fn handle_message( - &self, window: HWnd, msg: u32, wparam: WPARAM, lparam: LPARAM, - ) -> Option { - unsafe { wnd_proc_inner(window, msg, wparam, lparam, self) } - } - fn before_destroy(&self, window: HWnd) { let _ = window.revoke_drag_drop(); // No need to destroy timers: they are all destroyed when the window is invalidated. } -} - -/// Our custom `wnd_proc` handler. If the result contains a value, then this is returned after -/// handling any deferred tasks. otherwise the default window procedure is invoked. -#[allow(clippy::unwrap_used, reason = "Refactor this in a later PR")] // TODO -unsafe fn wnd_proc_inner( - window: HWnd, msg: u32, wparam: WPARAM, lparam: LPARAM, window_bv: &BaseviewWindow, -) -> Option { - let window_state = &window_bv.window_state; - match msg { - WM_PAINT => { - if let Some(rect) = window.get_update_rect() { - window_bv.notify_damage(DamageArea::Rect(rect.into())); - window_bv.handle_draw(); - window.validate_rect(rect); - } - Some(0) - } - WM_TIMER => { - let timer_id = TimerId::from_raw(wparam)?; - - if timer_id == RedrawTimer::ID { - if !window_state.redraw_timer.is_running() { - return None; - }; - window.invalidate_window(); - return Some(0); - } - - None - } - WM_CLOSE => { - window_bv.handle_event(Event::Window(WindowEvent::WillClose)); - - None - } - WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP - | WM_INPUTLANGCHANGE => { - let opt_event = window_state.keyboard_state.borrow_mut().process_message( - window.as_raw(), - msg, - wparam, - lparam, - ); - - if let Some(event) = opt_event { - window_bv.handle_event(Event::Keyboard(event)); - } - - if msg != WM_SYSKEYDOWN { - Some(0) - } else { - None - } - } - WM_SETFOCUS => { - window_bv.handle_event(Event::Window(WindowEvent::Focused)); - - None - } - WM_KILLFOCUS => { - window_bv.handle_event(Event::Window(WindowEvent::Unfocused)); - - None - } - WM_SHOWWINDOW => { - window_bv.handle_new_size_on_show(); - None - } - // If WM_SETCURSOR returns `None`, WM_SETCURSOR continues to get handled by the outer window(s), - // If it returns `Some(1)`, the current window decides what the cursor is - WM_SETCURSOR => { - let low_word = lo_word(lparam) as u32; - let mouse_in_window = low_word == HTCLIENT; - if mouse_in_window { - // Here we need to set the cursor back to what the state says, since it can have changed when outside the window - if let Ok(cursor) = SystemCursor::load(window_state.cursor_icon.get()) { - cursor.set() - } - Some(1) - } else { - // Cursor is being changed by some other window, e.g. when having mouse on the borders to resize it - None - } - } - // NOTE: `WM_NCDESTROY` is handled in the outer function because this deallocates the window - // state - BV_WINDOW_MUST_CLOSE => { - let _ = window.destroy(); - Some(0) - } + fn on_close(&self) { + self.handle_event(Event::Window(WindowEvent::WillClose)); + } - BV_REQUEST_REDRAW => { - window_state.request_redraw(); - Some(0) - } + fn on_show_window(&self) { + self.handle_new_size_on_show(); + } - BV_REQUEST_POLL => { - window_bv.handle_poll(); - Some(0) - } - _ => None, + fn on_close_requested(&self, window: HWnd) { + let _ = window.destroy(); } } diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index aee6a7fd..4b4c309b 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -129,8 +129,7 @@ impl WindowState { } pub fn platform_handle(&self) -> PlatformHandle { - let Some(hwnd) = NonZeroIsize::new(self.hwnd.as_raw() as _) else { unreachable!() }; - PlatformHandle { hwnd } + PlatformHandle::new(self.hwnd) } pub fn request_redraw(&self) { diff --git a/src/wrappers/win32/timer.rs b/src/wrappers/win32/timer.rs index d4b07e60..39e7fb74 100644 --- a/src/wrappers/win32/timer.rs +++ b/src/wrappers/win32/timer.rs @@ -1,6 +1,6 @@ use crate::platform::{BaseviewWindow, PlatformError}; use crate::wrappers::win32::window::{ - create_window, use_window, HWnd, WindowImpl, WmMouse, WmSizing, + create_window, use_window, HWnd, WindowImpl, WmCursor, WmDraw, WmKeyboard, WmMouse, WmSizing, }; use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect, WindowStyle}; use crate::{MouseButton, ScrollDelta}; @@ -9,9 +9,8 @@ use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::num::NonZeroUsize; use std::time::Duration; -use windows::Win32::UI::WindowsAndMessaging::WM_TIMER; use windows_core::{Error, HSTRING}; -use windows_sys::Win32::Foundation::{LPARAM, LRESULT, WPARAM}; +use windows_sys::Win32::Foundation::{LPARAM, WPARAM}; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct TimerId(NonZeroUsize); @@ -193,51 +192,62 @@ impl WindowImpl for TimerWindow { fn non_client_create(&self, _: HWnd) -> Result<(), PlatformError> { Ok(()) } - fn after_create(&self, _: HWnd) -> Result<(), PlatformError> { Ok(()) } - - unsafe fn handle_message( - &self, _: HWnd, message_code: u32, wparam: WPARAM, _: LPARAM, - ) -> Option { - if message_code != WM_TIMER { - return None; - } - - let timer_id = TimerId::from_raw(wparam)?; - - // SAFETY: baseview_window is of type BaseviewWindow - unsafe { - use_window::(self.baseview_window, |window| { - (self.timer_fn)(window, timer_id) - }) - }; - - Some(0) - } - fn before_destroy(&self, _: HWnd) {} + fn on_close(&self) {} + fn on_close_requested(&self, _: HWnd) {} + fn on_show_window(&self) {} } impl WmSizing for TimerWindow { fn on_size(&self, _: HWnd, _: PhysicalSize) -> Result<(), ()> { Ok(()) } - fn on_dpi_changed(&self, _: HWnd, _: Dpi, _: Rect) -> Result<(), ()> { Ok(()) } - fn get_min_max_size(&self, _: HWnd) -> (Option>, Option>) { (None, None) } } impl WmMouse for TimerWindow { - fn on_move(&self, window: HWnd, position: PhysicalPosition, wparam: WPARAM) {} + fn on_move(&self, _: HWnd, _: PhysicalPosition, _: WPARAM) {} fn on_leave(&self, _: HWnd) {} - fn on_wheel(&self, window: HWnd, delta: ScrollDelta, wparam: WPARAM) {} + fn on_wheel(&self, _: HWnd, _: ScrollDelta, _: WPARAM) {} fn on_capture_changed(&self, _: HWnd, _: Option) {} - fn on_button(&self, window: HWnd, button: MouseButton, down: bool, wparam: WPARAM) {} + fn on_button(&self, _: HWnd, _: MouseButton, _: bool, _: WPARAM) {} +} + +impl WmKeyboard for TimerWindow { + fn on_key_event(&self, _: HWnd, _: u32, _: WPARAM, _: LPARAM) -> bool { + false + } + fn on_focus(&self) {} + fn on_unfocus(&self) {} +} + +impl WmDraw for TimerWindow { + fn on_timer(&self, _: HWnd, timer_id: TimerId) -> bool { + // SAFETY: baseview_window is of type BaseviewWindow + unsafe { + use_window::(self.baseview_window, |window| { + (self.timer_fn)(window, timer_id) + }) + }; + + true + } + + fn on_paint(&self, _: HWnd) {} + fn on_redraw_requested(&self) {} + fn on_poll_requested(&self) {} +} + +impl WmCursor for TimerWindow { + fn on_set_cursor(&self, _: bool) -> bool { + false + } } diff --git a/src/wrappers/win32/window.rs b/src/wrappers/win32/window.rs index 78981e2d..bb2d862b 100644 --- a/src/wrappers/win32/window.rs +++ b/src/wrappers/win32/window.rs @@ -29,10 +29,9 @@ use windows_core::{Error, Result, HSTRING}; use crate::wrappers::win32::dpi::DpiAwarenessGuard; use crate::wrappers::win32::h_instance::HInstance; use crate::wrappers::win32::style::WindowStyle; -use windows_sys::Win32::Foundation::{LPARAM, LRESULT, WPARAM}; use windows_sys::Win32::UI::WindowsAndMessaging::CreateWindowExW; -pub trait WindowImpl: WmSizing + WmMouse + 'static { +pub trait WindowImpl: WmSizing + WmMouse + WmKeyboard + WmDraw + WmCursor + 'static { fn non_client_create( &self, window: HWnd, ) -> core::result::Result<(), crate::platform::PlatformError>; @@ -47,9 +46,7 @@ pub trait WindowImpl: WmSizing + WmMouse + 'static { fn after_create( &self, window: HWnd, ) -> core::result::Result<(), crate::platform::PlatformError>; - unsafe fn handle_message( - &self, window: HWnd, message_code: u32, w_param: WPARAM, l_param: LPARAM, - ) -> Option; + /// Called during the processing of the WM_DESTROY message, but before any other de-initialization /// takes place. /// @@ -58,6 +55,10 @@ pub trait WindowImpl: WmSizing + WmMouse + 'static { /// /// This function is not fallible. Any errors will be ignored. fn before_destroy(&self, window: HWnd); + + fn on_close(&self); + fn on_close_requested(&self, window: HWnd); + fn on_show_window(&self); } /// # Safety @@ -81,8 +82,8 @@ pub unsafe fn use_window(hwnd: HWnd, handler: impl FnOnce(&W)) { /// The initialization function is called during the handling of the WM_CREATE function, which allows /// it to receive a valid HWND, but before it has to handle any messages. /// -/// Note that any message sent to the window by the given `initializer` will not be sent to the -/// [`WindowImpl::handle_message`] function. +/// Note that any message sent to the window by the given `initializer` will not be sent to any of +/// its handler methods. /// For any non-trivial operations (e.g. window resizing, GL context creation, etc.), put them in /// [`WindowImpl::after_create`] instead. pub fn create_window( diff --git a/src/wrappers/win32/window/data.rs b/src/wrappers/win32/window/data.rs index cb1e75dd..36ef2990 100644 --- a/src/wrappers/win32/window/data.rs +++ b/src/wrappers/win32/window/data.rs @@ -3,7 +3,6 @@ use std::cell::{Cell, OnceCell}; use std::mem::ManuallyDrop; use std::ptr::NonNull; use std::rc::Rc; -use windows_sys::Win32::Foundation::{LPARAM, LRESULT, WPARAM}; type Initializer = dyn FnOnce(HWnd) -> W + 'static; diff --git a/src/wrappers/win32/window/interfaces.rs b/src/wrappers/win32/window/interfaces.rs index 7d7bfc75..d73248f5 100644 --- a/src/wrappers/win32/window/interfaces.rs +++ b/src/wrappers/win32/window/interfaces.rs @@ -1,6 +1,8 @@ -use crate::wrappers::win32::window::{HWnd, WindowData, WindowImpl}; -use crate::wrappers::win32::{Dpi, Rect}; -use crate::{Event, MouseButton, MouseEvent, ScrollDelta}; +use crate::wrappers::win32::window::{ + HWnd, WindowData, WindowImpl, BV_REQUEST_POLL, BV_REQUEST_REDRAW, BV_WINDOW_MUST_CLOSE, +}; +use crate::wrappers::win32::{Dpi, Rect, TimerId}; +use crate::{MouseButton, ScrollDelta}; use dpi::{PhysicalPosition, PhysicalSize}; use std::num::NonZeroU32; use std::ptr::NonNull; @@ -25,12 +27,43 @@ pub trait WmMouse { fn on_button(&self, window: HWnd, button: MouseButton, down: bool, wparam: WPARAM); } +pub trait WmKeyboard { + /// Returns true if event was handled, false otherwise. + fn on_key_event(&self, window: HWnd, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool; + fn on_focus(&self); + fn on_unfocus(&self); +} + +pub trait WmDraw { + /// Returns true if this matched a known timer, false otherwise. + fn on_timer(&self, window: HWnd, timer_id: TimerId) -> bool; + fn on_paint(&self, window: HWnd); + fn on_redraw_requested(&self); + fn on_poll_requested(&self); +} + +pub trait WmCursor { + /// Returns true to override the cursor, if false then a default cursor is set for the current window area. + fn on_set_cursor(&self, mouse_in_window: bool) -> bool; +} + pub unsafe fn handle_message( data: &WindowData, window: HWnd, message_code: u32, wparam: WPARAM, lparam: LPARAM, ) -> Option { let inner = data.inner()?; match message_code { + // Cursor + WM_SETCURSOR => { + let low_word = lo_word(lparam) as u32; + let mouse_in_window = low_word == HTCLIENT; + + if inner.on_set_cursor(mouse_in_window) { + Some(1) + } else { + None + } + } // Sizing WM_SIZE => { let width = (lparam & 0xFFFF) as u16 as u32; @@ -74,6 +107,48 @@ pub unsafe fn handle_message( Some(0) } + // Draw + WM_PAINT => { + inner.on_paint(window); + + Some(0) + } + WM_TIMER => { + let timer_id = TimerId::from_raw(wparam)?; + + if inner.on_timer(window, timer_id) { + Some(0) + } else { + None + } + } + BV_REQUEST_REDRAW => { + inner.on_redraw_requested(); + Some(0) + } + BV_REQUEST_POLL => { + inner.on_poll_requested(); + Some(0) + } + + // Keyboard + WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP + | WM_INPUTLANGCHANGE => { + if inner.on_key_event(window, message_code, wparam, lparam) { + Some(0) + } else { + None + } + } + WM_SETFOCUS => { + inner.on_focus(); + None + } + WM_KILLFOCUS => { + inner.on_unfocus(); + None + } + // Mouse WM_MOUSEMOVE => { inner.on_move( @@ -145,6 +220,22 @@ pub unsafe fn handle_message( Some(0) } + + // Other window functions + WM_CLOSE => { + inner.on_close(); + + None + } + WM_SHOWWINDOW => { + inner.on_show_window(); + None + } + BV_WINDOW_MUST_CLOSE => { + inner.on_close_requested(window); + Some(0) + } + _ => None, } } From 390351a1b983d0d17445772176c0cec7756df133 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:33:17 +0100 Subject: [PATCH 6/9] Final fixes --- src/platform/macos/context.rs | 2 +- src/platform/macos/window.rs | 2 +- src/platform/win/error.rs | 2 +- src/platform/win/mod.rs | 2 +- src/platform/win/sizing.rs | 48 ++++++++++++++----------- src/platform/win/window.rs | 20 ++++++----- src/wrappers/win32/timer.rs | 4 +-- src/wrappers/win32/window/interfaces.rs | 11 ++++-- 8 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index bf447593..9acd7219 100644 --- a/src/platform/macos/context.rs +++ b/src/platform/macos/context.rs @@ -1,7 +1,7 @@ use crate::dpi::Size; use crate::platform::macos::view::BaseviewView; use crate::platform::{PlatformError, PlatformHandle, WindowSharedState}; -use crate::platform::{Result, WindowWaker}; +use crate::platform::{PlatformResult, WindowWaker}; use crate::wrappers::appkit::{View, ViewRef}; use crate::*; use dispatch2::MainThreadBound; diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 4a82a6ca..2cb1307a 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -10,7 +10,7 @@ use std::cell::Cell; use std::rc::Rc; use crate::platform::macos::view::{BaseviewView, ViewParentingType}; -use crate::platform::Result; +use crate::platform::PlatformResult; use crate::platform::{ParentWindowHandle, WindowWaker}; use crate::utils::SizingStrategy; use crate::wrappers::appkit::{create_window, View}; diff --git a/src/platform/win/error.rs b/src/platform/win/error.rs index 91742dd2..402ab0f8 100644 --- a/src/platform/win/error.rs +++ b/src/platform/win/error.rs @@ -2,7 +2,7 @@ use crate::HandlerError; use dpi::PhysicalSize; use std::fmt::Display; -pub type Result = std::result::Result; +pub type PlatformResult = Result; #[derive(Debug)] pub enum PlatformError { diff --git a/src/platform/win/mod.rs b/src/platform/win/mod.rs index 73263459..8982821f 100644 --- a/src/platform/win/mod.rs +++ b/src/platform/win/mod.rs @@ -16,7 +16,7 @@ mod window; mod window_state; pub(crate) use dpi::DpiScalingStrategy; -pub use error::{PlatformError, Result}; +pub use error::{PlatformError, PlatformResult}; pub use parent_handle::*; pub use platform_handle::*; pub use waker::WindowWaker; diff --git a/src/platform/win/sizing.rs b/src/platform/win/sizing.rs index bdeff3c9..39593168 100644 --- a/src/platform/win/sizing.rs +++ b/src/platform/win/sizing.rs @@ -1,6 +1,6 @@ -use crate::platform::{BaseviewWindow, PlatformError}; +use crate::platform::{BaseviewWindow, PlatformError, PlatformResult}; use crate::wrappers::win32::window::{HWnd, WmSizing}; -use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect}; +use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect, WindowStyle}; use crate::{HandlerError, WindowSize}; use dpi::PhysicalSize; use tracing::warn; @@ -8,7 +8,7 @@ use tracing::warn; impl BaseviewWindow { fn adapt_host_window_to_size( &self, previous_size: PhysicalSize, new_size: WindowSize, - ) -> Result<(), ()> { + ) -> Result<(), PlatformError> { if let Err(e) = self.request_host_resize(new_size) { warn!("Resize request from Host failed: {}. Reverting to previous size.", e); @@ -23,7 +23,7 @@ impl BaseviewWindow { warn!("Failed to resize back to previous window size: {}", e); } - return Err(()); + return Err(e.into()); } Ok(()) @@ -137,15 +137,14 @@ impl WmSizing for BaseviewWindow { fn on_dpi_changed( &self, window: HWnd, new_dpi: Dpi, suggested_nc_rect: Rect, - ) -> Result<(), ()> { + ) -> PlatformResult<()> { let dpi_ctx = DpiAwarenessGuard::new( &self.shared_state.user32, self.shared_state.dpi_scaling_strategy.get(), - ) - .unwrap(); - let style = window.get_style().unwrap(); + )?; + let style = window.get_style()?; let suggested_rect = - dpi_ctx.nc_area_to_client_area(suggested_nc_rect, style, Some(new_dpi)).unwrap(); + dpi_ctx.nc_area_to_client_area(suggested_nc_rect, style, Some(new_dpi))?; let new_size = suggested_rect.size(); @@ -171,7 +170,7 @@ impl WmSizing for BaseviewWindow { warn!("Failed to resize back to previous window size: {}", e); } - return Err(()); + return Err(e.into()); } self.adapt_host_window_to_size(previous_size, new_size)?; @@ -190,25 +189,34 @@ impl WmSizing for BaseviewWindow { return (None, None); } - let ctx = DpiAwarenessGuard::new( + let Ok(ctx) = DpiAwarenessGuard::new( &self.shared_state.user32, self.shared_state.dpi_scaling_strategy.get(), - ) - .unwrap(); - let style = window.get_style().unwrap(); + ) else { + return (None, None); + }; + + let style = window.get_style().unwrap_or(WindowStyle::dummy()); let dpi = self.shared_state.current_dpi.get(); - let min_size = sizing.min_size().map(|size| { + let min_size = if let Some(size) = sizing.min_size() { let size = size.to_physical(self.shared_state.scale_factor()); + let nc_size = ctx.client_area_to_nc_area(size.into(), style, Some(dpi)); - ctx.client_area_to_nc_area(size.into(), style, Some(dpi)).unwrap().size().cast() - }); + nc_size.ok().map(|rect| rect.size().cast()) + } else { + None + }; - let max_size = sizing.max_size().map(|size| { + let max_size = if let Some(size) = sizing.max_size() { let size = size.to_physical(self.shared_state.scale_factor()); - ctx.client_area_to_nc_area(size.into(), style, Some(dpi)).unwrap().size().cast() - }); + let nc_size = ctx.client_area_to_nc_area(size.into(), style, Some(dpi)); + + nc_size.ok().map(|rect| rect.size().cast()) + } else { + None + }; (min_size, max_size) } diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 2baf0028..cdd40550 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -24,7 +24,7 @@ pub struct WindowHandle { } impl WindowHandle { - pub fn run_until_closed(self) -> Result<()> { + pub fn run_until_closed(self) -> PlatformResult<()> { self.show()?; run_thread_message_loop_until(|| !self.is_open())?; @@ -39,7 +39,7 @@ impl WindowHandle { self.state.size() } - pub fn resize(&self, new_size: Size) -> Result<()> { + pub fn resize(&self, new_size: Size) -> PlatformResult<()> { let new_size = self.state.sizing_strategy.adjust_size(new_size, self.size()).physical; if new_size == self.state.current_size.get() { @@ -70,7 +70,7 @@ impl WindowHandle { self.state.sizing_strategy } - pub fn suggest_scale_factor(&self, new_host_scale_factor: f64) -> Result<()> { + pub fn suggest_scale_factor(&self, new_host_scale_factor: f64) -> PlatformResult<()> { let new_dpi = Dpi::from_scale_factor(new_host_scale_factor) .ok_or(PlatformError::InvalidScaleFactor)?; @@ -114,7 +114,7 @@ impl WindowHandle { } } - pub fn set_parent(&self, new_parent: ParentWindowHandle) -> Result<()> { + pub fn set_parent(&self, new_parent: ParentWindowHandle) -> PlatformResult<()> { let hwnd = match self.hwnd.get() { Some(hwnd) => hwnd, None => { @@ -142,7 +142,7 @@ impl WindowHandle { // No-op } - pub fn show(&self) -> Result<()> { + pub fn show(&self) -> PlatformResult<()> { let hwnd = match self.hwnd.get() { Some(hwnd) => hwnd, None => { @@ -160,7 +160,7 @@ impl WindowHandle { Ok(()) } - pub fn hide(&self) -> Result<()> { + pub fn hide(&self) -> PlatformResult<()> { let Some(hwnd) = self.hwnd.get() else { return Ok(()) }; hwnd.hide(); @@ -171,7 +171,7 @@ impl WindowHandle { self.state.window_waker_source.waker() } - pub fn request_poll(&self) -> Result<()> { + pub fn request_poll(&self) -> PlatformResult<()> { if let Some(hwnd) = self.hwnd.get() { hwnd.post_request_poll(); } @@ -196,7 +196,9 @@ impl Drop for WindowHandle { } impl BaseviewWindow { - pub fn create(shared_state: Rc, init: WindowInitializer) -> Result { + pub fn create( + shared_state: Rc, init: WindowInitializer, + ) -> PlatformResult { shared_state.init(&init); let style = WindowStyle::from_settings(&init.settings); @@ -374,7 +376,7 @@ impl WindowImpl for BaseviewWindow { } impl WindowHandle { - pub fn create_window(init: WindowInitializer) -> Result { + pub fn create_window(init: WindowInitializer) -> PlatformResult { let extended_user_32 = LibraryModule::load()?; let shared_state = WindowSharedState::new(extended_user_32, &init); diff --git a/src/wrappers/win32/timer.rs b/src/wrappers/win32/timer.rs index 39e7fb74..f931cada 100644 --- a/src/wrappers/win32/timer.rs +++ b/src/wrappers/win32/timer.rs @@ -1,4 +1,4 @@ -use crate::platform::{BaseviewWindow, PlatformError}; +use crate::platform::{BaseviewWindow, PlatformError, PlatformResult}; use crate::wrappers::win32::window::{ create_window, use_window, HWnd, WindowImpl, WmCursor, WmDraw, WmKeyboard, WmMouse, WmSizing, }; @@ -205,7 +205,7 @@ impl WmSizing for TimerWindow { fn on_size(&self, _: HWnd, _: PhysicalSize) -> Result<(), ()> { Ok(()) } - fn on_dpi_changed(&self, _: HWnd, _: Dpi, _: Rect) -> Result<(), ()> { + fn on_dpi_changed(&self, _: HWnd, _: Dpi, _: Rect) -> PlatformResult<()> { Ok(()) } fn get_min_max_size(&self, _: HWnd) -> (Option>, Option>) { diff --git a/src/wrappers/win32/window/interfaces.rs b/src/wrappers/win32/window/interfaces.rs index d73248f5..4eb0ccff 100644 --- a/src/wrappers/win32/window/interfaces.rs +++ b/src/wrappers/win32/window/interfaces.rs @@ -1,3 +1,4 @@ +use crate::platform::PlatformResult; use crate::wrappers::win32::window::{ HWnd, WindowData, WindowImpl, BV_REQUEST_POLL, BV_REQUEST_REDRAW, BV_WINDOW_MUST_CLOSE, }; @@ -12,8 +13,9 @@ use windows_sys::Win32::UI::WindowsAndMessaging::*; pub trait WmSizing { fn on_size(&self, window: HWnd, new_size: PhysicalSize) -> Result<(), ()>; - fn on_dpi_changed(&self, window: HWnd, new_dpi: Dpi, suggested_nc_rect: Rect) - -> Result<(), ()>; + fn on_dpi_changed( + &self, window: HWnd, new_dpi: Dpi, suggested_nc_rect: Rect, + ) -> PlatformResult<()>; fn get_min_max_size( &self, window: HWnd, ) -> (Option>, Option>); @@ -84,7 +86,10 @@ pub unsafe fn handle_message( match inner.on_dpi_changed(window, dpi, suggested_nc_rect) { Ok(()) => None, - Err(()) => Some(-1), + Err(e) => { + crate::warn!("Could not handle WM_DPICHANGED message: {e}"); + Some(-1) + } } } WM_GETMINMAXINFO => { From c0c7d9bdc229d411db29069e682dcecee81b38c0 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 28 Oct 2026 17:05:19 +0100 Subject: [PATCH 7/9] fixes --- src/platform/macos/context.rs | 8 ++++---- src/platform/macos/gl.rs | 8 ++++---- src/platform/macos/mod.rs | 6 ++---- src/platform/macos/view.rs | 2 +- src/platform/macos/window.rs | 33 +++++++++++-------------------- src/platform/x11/window_thread.rs | 12 ----------- 6 files changed, 22 insertions(+), 47 deletions(-) diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index 9acd7219..6693195b 100644 --- a/src/platform/macos/context.rs +++ b/src/platform/macos/context.rs @@ -69,7 +69,7 @@ impl WindowContext { view.isEqual(Some(&*first_responder)) } - pub fn focus(&self) -> Result<()> { + pub fn focus(&self) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; autoreleasepool(|_| { @@ -81,7 +81,7 @@ impl WindowContext { Ok(()) } - pub fn resize(&self, size: Size) -> Result<()> { + pub fn resize(&self, size: Size) -> PlatformResult<()> { if self.state.closed.get() { return Ok(()); } @@ -98,7 +98,7 @@ impl WindowContext { Ok(()) } - pub fn set_mouse_cursor(&self, cursor: MouseCursor) -> Result<()> { + pub fn set_mouse_cursor(&self, cursor: MouseCursor) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; @@ -137,7 +137,7 @@ impl WindowContext { } #[inline] - pub fn create_timer(&self, duration: Duration) -> Result { + pub fn create_timer(&self, duration: Duration) -> PlatformResult { let Some(view_retained) = self.view.load() else { return Err(PlatformError::InvalidWindowContext); }; diff --git a/src/platform/macos/gl.rs b/src/platform/macos/gl.rs index e550363c..18be53ac 100644 --- a/src/platform/macos/gl.rs +++ b/src/platform/macos/gl.rs @@ -60,7 +60,7 @@ pub struct PlatformGlContext { impl PlatformGlContext { pub(crate) fn create( parent_view: &NSView, config: GlConfig, marker: MainThreadMarker, - ) -> Result { + ) -> PlatformResult { let version = if config.version < (3, 2) && config.profile == Profile::Compatibility { NSOpenGLProfileVersionLegacy } else if config.version == (3, 2) && config.profile == Profile::Core { @@ -136,14 +136,14 @@ impl PlatformGlContext { Ok(PlatformGlContext { view, context, gl_bundle }) } - pub unsafe fn make_current(&self) -> Result<()> { + pub unsafe fn make_current(&self) -> PlatformResult<()> { autoreleasepool(|_| { self.context.makeCurrentContext(); }); Ok(()) } - pub unsafe fn make_not_current(&self) -> Result<()> { + pub unsafe fn make_not_current(&self) -> PlatformResult<()> { autoreleasepool(|_| { NSOpenGLContext::clearCurrentContext(); }); @@ -173,7 +173,7 @@ impl PlatformGlContext { autoreleasepool(|_| self.gl_bundle.function_pointer_for_name(Some(&symbol_name))) } - pub fn swap_buffers(&self) -> Result<()> { + pub fn swap_buffers(&self) -> PlatformResult<()> { autoreleasepool(|_| { self.context.flushBuffer(); }); diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 6f20e192..790ef801 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -25,7 +25,7 @@ pub use timer::TimerHandle; pub use waker::WindowWaker; pub use window::*; -pub(crate) type Result = std::result::Result; +pub(crate) type PlatformResult = Result; #[cfg(feature = "opengl")] pub mod gl; @@ -82,9 +82,7 @@ pub struct ParentWindowHandle { } impl ParentWindowHandle { - pub fn extract( - window: &impl HasWindowHandle, - ) -> core::result::Result { + pub fn extract(window: &impl HasWindowHandle) -> Result { let view = extract_raw_window_handle(window.window_handle()?)?; let mtm = view.mtm(); diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index 07cd39c6..86426581 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -91,7 +91,7 @@ impl BaseviewView { pub fn new( init: WindowInitializer, parenting: ViewParentingType, final_size: LogicalSize, mtm: MainThreadMarker, - ) -> Result<(Retained>, Rc)> { + ) -> PlatformResult<(Retained>, Rc)> { let view_rect = NSRect::new(NSPoint::ZERO, NSSize::new(final_size.width, final_size.height)); diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 2cb1307a..7b6da2de 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -33,7 +33,7 @@ impl Drop for WindowHandle { } impl WindowHandle { - pub fn create_window(mut init: WindowInitializer) -> Result { + pub fn create_window(mut init: WindowInitializer) -> PlatformResult { autoreleasepool(|_| { let Some(mtm) = MainThreadMarker::new() else { panic!("macOS: Windows can only be created on the main thread!") @@ -52,7 +52,7 @@ impl WindowHandle { fn create_window_parented( init: WindowInitializer, parent_view: Retained, mtm: MainThreadMarker, - ) -> Result { + ) -> PlatformResult { let parenting = ViewParentingType::Parented { parent_view: Weak::from_retained(&parent_view) }; @@ -67,7 +67,7 @@ impl WindowHandle { pub fn create_window_standalone( init: WindowInitializer, mtm: MainThreadMarker, - ) -> Result { + ) -> PlatformResult { let window = create_window_with_options(&init.settings, mtm); window.setAcceptsMouseMovedEvents(true); @@ -81,7 +81,7 @@ impl WindowHandle { Ok(Self { mtm, state, view: Weak::from_retained(&view), _window: Some(window) }) } - pub fn run_until_closed(self) -> Result<()> { + pub fn run_until_closed(self) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; @@ -103,18 +103,6 @@ impl WindowHandle { self.state.closed.get() } - pub fn is_resizable(&self) -> bool { - self.state.sizing_strategy.is_resizable() - } - - pub fn min_size(&self) -> Option { - self.state.sizing_strategy.min_size() - } - - pub fn max_size(&self) -> Option { - self.state.sizing_strategy.max_size() - } - #[inline] pub fn handle_main_thread_callback(&self) { // No-op @@ -124,7 +112,7 @@ impl WindowHandle { WindowSize::from_logical(self.state.size.get(), self.state.scale_factor.get()) } - pub fn resize(&self, size: Size) -> Result<()> { + pub fn resize(&self, size: Size) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; @@ -135,12 +123,12 @@ impl WindowHandle { Ok(()) } - pub fn suggest_scale_factor(&self, _scale_factor: f64) -> Result<()> { + pub fn suggest_scale_factor(&self, _scale_factor: f64) -> PlatformResult<()> { // This does not do anything on macOS: all coordinates are already logical Ok(()) } - pub fn set_parent(&self, new_parent: ParentWindowHandle) -> Result<()> { + pub fn set_parent(&self, new_parent: ParentWindowHandle) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; @@ -151,7 +139,7 @@ impl WindowHandle { Ok(()) } - pub fn show(&self) -> Result<()> { + pub fn show(&self) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; @@ -161,7 +149,7 @@ impl WindowHandle { Ok(()) } - pub fn hide(&self) -> Result<()> { + pub fn hide(&self) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; @@ -171,11 +159,12 @@ impl WindowHandle { Ok(()) } + #[inline] pub fn sizing_strategy(&self) -> SizingStrategy { self.state.sizing_strategy } - pub fn request_poll(&self) -> Result<()> { + pub fn request_poll(&self) -> PlatformResult<()> { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; diff --git a/src/platform/x11/window_thread.rs b/src/platform/x11/window_thread.rs index a5e6e545..7316c7d0 100644 --- a/src/platform/x11/window_thread.rs +++ b/src/platform/x11/window_thread.rs @@ -214,18 +214,6 @@ impl WindowThreadHandle { !self.shared.stopped.load(Ordering::Relaxed) } - pub fn is_resizable(&self) -> bool { - self.sizing_strategy().is_resizable() - } - - pub fn min_size(&self) -> Option { - self.sizing_strategy().min_size() - } - - pub fn max_size(&self) -> Option { - self.sizing_strategy().max_size() - } - pub fn handle_main_thread_callback(&self) { loop { let Some(receiver) = self.callback_receiver.as_ref() else { return }; From 63beb5f4d7b9521c7a0b5a6202d0d8bdac8323fd Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 28 Oct 2026 17:09:15 +0100 Subject: [PATCH 8/9] fixes --- src/platform/win/sizing.rs | 2 +- src/wrappers/win32/window/interfaces.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/win/sizing.rs b/src/platform/win/sizing.rs index 39593168..49343a41 100644 --- a/src/platform/win/sizing.rs +++ b/src/platform/win/sizing.rs @@ -1,9 +1,9 @@ use crate::platform::{BaseviewWindow, PlatformError, PlatformResult}; +use crate::warn; use crate::wrappers::win32::window::{HWnd, WmSizing}; use crate::wrappers::win32::{Dpi, DpiAwarenessGuard, Rect, WindowStyle}; use crate::{HandlerError, WindowSize}; use dpi::PhysicalSize; -use tracing::warn; impl BaseviewWindow { fn adapt_host_window_to_size( diff --git a/src/wrappers/win32/window/interfaces.rs b/src/wrappers/win32/window/interfaces.rs index 4eb0ccff..e56d1a7f 100644 --- a/src/wrappers/win32/window/interfaces.rs +++ b/src/wrappers/win32/window/interfaces.rs @@ -87,7 +87,7 @@ pub unsafe fn handle_message( match inner.on_dpi_changed(window, dpi, suggested_nc_rect) { Ok(()) => None, Err(e) => { - crate::warn!("Could not handle WM_DPICHANGED message: {e}"); + crate::warn!("Could not handle WM_DPICHANGED message: {}", e); Some(-1) } } From cba10b98f20b3588f8e5b2825a3d0c5b7a6ddfa1 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:02:37 +0100 Subject: [PATCH 9/9] fixes --- src/platform/win/sizing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/win/sizing.rs b/src/platform/win/sizing.rs index 49343a41..07af03c1 100644 --- a/src/platform/win/sizing.rs +++ b/src/platform/win/sizing.rs @@ -10,7 +10,7 @@ impl BaseviewWindow { &self, previous_size: PhysicalSize, new_size: WindowSize, ) -> Result<(), PlatformError> { if let Err(e) = self.request_host_resize(new_size) { - warn!("Resize request from Host failed: {}. Reverting to previous size.", e); + warn!("Resize request from Host failed: {}. Reverting to previous size.", &e); if let Some(handler) = self.handler.get() { if let Err(e) = handler.resized(new_size) { @@ -163,7 +163,7 @@ impl WmSizing for BaseviewWindow { let new_size = WindowSize::from_physical(new_size, new_dpi.scale_factor()); if let Err(e) = handler.resized(new_size) { - warn!("Window Handler failed to resize: {}", e); + warn!("Window Handler failed to resize: {}", &e); self.shared_state.current_size.set(previous_size); if let Err(e) = self.window_state.resize(previous_size.into()) {