diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index bf447593..6693195b 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; @@ -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 4a82a6ca..7b6da2de 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}; @@ -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/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/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/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 6d38d505..8982821f 100644 --- a/src/platform/win/mod.rs +++ b/src/platform/win/mod.rs @@ -1,109 +1,63 @@ +mod cursor; mod dpi; +mod draw; 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 error::{PlatformError, PlatformResult}; +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/mouse.rs b/src/platform/win/mouse.rs new file mode 100644 index 00000000..2527ab98 --- /dev/null +++ b/src/platform/win/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/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..446c4ea1 --- /dev/null +++ b/src/platform/win/platform_handle.rs @@ -0,0 +1,36 @@ +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; + +#[derive(Clone)] +pub struct PlatformHandle { + hwnd: NonZeroIsize, +} + +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()); + + 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/sizing.rs b/src/platform/win/sizing.rs new file mode 100644 index 00000000..07af03c1 --- /dev/null +++ b/src/platform/win/sizing.rs @@ -0,0 +1,223 @@ +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; + +impl BaseviewWindow { + fn adapt_host_window_to_size( + &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); + + 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(e.into()); + } + + 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, + ) -> PlatformResult<()> { + let dpi_ctx = DpiAwarenessGuard::new( + &self.shared_state.user32, + self.shared_state.dpi_scaling_strategy.get(), + )?; + let style = window.get_style()?; + let suggested_rect = + dpi_ctx.nc_area_to_client_area(suggested_nc_rect, style, Some(new_dpi))?; + + 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(e.into()); + } + + 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 Ok(ctx) = DpiAwarenessGuard::new( + &self.shared_state.user32, + self.shared_state.dpi_scaling_strategy.get(), + ) else { + return (None, None); + }; + + let style = window.get_style().unwrap_or(WindowStyle::dummy()); + let dpi = self.shared_state.current_dpi.get(); + + 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)); + + nc_size.ok().map(|rect| rect.size().cast()) + } else { + None + }; + + let max_size = if let Some(size) = sizing.max_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)); + + 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 4a47047d..cdd40550 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -1,47 +1,30 @@ use windows_core::{ComObject, HSTRING}; -use windows_sys::Win32::{ - Foundation::{LPARAM, LRESULT, RECT, WPARAM}, - UI::{Controls::WM_MOUSELEAVE, WindowsAndMessaging::*}, -}; -use crate::dpi::{PhysicalPosition, PhysicalSize, Size}; -use crate::{warn, DamageArea, EventStatus, HandlerError, WindowHandler}; +use crate::dpi::Size; +use crate::{warn, DamageArea, EventStatus}; use std::cell::{Cell, OnceCell}; -use std::num::NonZeroU32; -use windows_sys::Win32::Foundation::POINT; 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, Rect, - TimerId, TimerType, WindowStyle, + ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessGuard, LibraryModule, TimerId, + WindowStyle, }; -use crate::{Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowSize}; - -fn hi_word(wparam: WPARAM) -> u16 { - ((wparam >> 16) & 0xffff) as u16 -} - -fn lo_word(lparam: LPARAM) -> u16 { - (lparam & 0xffff) as u16 -} +use crate::{Event, WindowEvent, WindowSize}; pub struct WindowHandle { - init: Cell>, + init: Cell>>, hwnd: Cell>, state: Rc, } 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())?; @@ -52,35 +35,20 @@ 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() } - 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() { 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(); @@ -97,11 +65,12 @@ impl WindowHandle { } } + #[inline] pub fn sizing_strategy(&self) -> SizingStrategy { 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)?; @@ -145,14 +114,14 @@ 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 => { 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(()); @@ -163,7 +132,7 @@ impl WindowHandle { panic!("Called set_parent on a floating window") } - hwnd.set_parent(&new_parent.handle)?; + hwnd.set_parent(&new_parent.hwnd())?; Ok(()) } @@ -173,16 +142,16 @@ impl WindowHandle { // No-op } - pub fn show(&self) -> Result<()> { + pub fn show(&self) -> PlatformResult<()> { let hwnd = match self.hwnd.get() { Some(hwnd) => hwnd, 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 } }; @@ -191,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(); @@ -202,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(); } @@ -226,37 +195,21 @@ 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 { + pub fn create( + shared_state: Rc, init: WindowInitializer, + ) -> PlatformResult { 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())?; 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); @@ -287,12 +240,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) { @@ -303,37 +251,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); @@ -410,42 +327,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())); @@ -475,404 +357,26 @@ 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_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())); - 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_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); - } - } - - 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 - } - } - 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 => { - 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(); } } 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); @@ -881,7 +385,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/platform/win/window_state.rs b/src/platform/win/window_state.rs index 395e6917..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) { @@ -209,7 +208,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, 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 }; 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. diff --git a/src/wrappers/win32/timer.rs b/src/wrappers/win32/timer.rs index 5e8406dc..f931cada 100644 --- a/src/wrappers/win32/timer.rs +++ b/src/wrappers/win32/timer.rs @@ -1,14 +1,16 @@ -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::platform::{BaseviewWindow, PlatformError, PlatformResult}; +use crate::wrappers::win32::window::{ + create_window, use_window, HWnd, WindowImpl, WmCursor, WmDraw, WmKeyboard, 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; 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); @@ -190,29 +192,62 @@ impl WindowImpl for TimerWindow { fn non_client_create(&self, _: HWnd) -> Result<(), PlatformError> { Ok(()) } - fn after_create(&self, _: HWnd) -> Result<(), PlatformError> { Ok(()) } + fn before_destroy(&self, _: HWnd) {} + fn on_close(&self) {} + fn on_close_requested(&self, _: HWnd) {} + fn on_show_window(&self) {} +} - unsafe fn handle_message( - &self, _: HWnd, message_code: u32, wparam: WPARAM, _: LPARAM, - ) -> Option { - if message_code != WM_TIMER { - return None; - } +impl WmSizing for TimerWindow { + fn on_size(&self, _: HWnd, _: PhysicalSize) -> Result<(), ()> { + Ok(()) + } + fn on_dpi_changed(&self, _: HWnd, _: Dpi, _: Rect) -> PlatformResult<()> { + Ok(()) + } + fn get_min_max_size(&self, _: HWnd) -> (Option>, Option>) { + (None, None) + } +} - let timer_id = TimerId::from_raw(wparam)?; +impl WmMouse for TimerWindow { + fn on_move(&self, _: HWnd, _: PhysicalPosition, _: WPARAM) {} + fn on_leave(&self, _: HWnd) {} + fn on_wheel(&self, _: HWnd, _: ScrollDelta, _: WPARAM) {} + fn on_capture_changed(&self, _: HWnd, _: Option) {} + 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, |hwnd| { - (self.timer_fn)(hwnd, timer_id) + use_window::(self.baseview_window, |window| { + (self.timer_fn)(window, timer_id) }) }; - Some(0) + true } - fn before_destroy(&self, _: HWnd) {} + 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 da2cb8a2..bb2d862b 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; @@ -26,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: 'static { +pub trait WindowImpl: WmSizing + WmMouse + WmKeyboard + WmDraw + WmCursor + 'static { fn non_client_create( &self, window: HWnd, ) -> core::result::Result<(), crate::platform::PlatformError>; @@ -44,9 +46,7 @@ pub trait WindowImpl: '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. /// @@ -55,6 +55,10 @@ pub trait WindowImpl: '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 @@ -78,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 769be955..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; @@ -73,14 +72,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..e56d1a7f --- /dev/null +++ b/src/wrappers/win32/window/interfaces.rs @@ -0,0 +1,254 @@ +use crate::platform::PlatformResult; +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; +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 { + fn on_size(&self, window: HWnd, new_size: PhysicalSize) -> 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>); +} + +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 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; + 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(e) => { + crate::warn!("Could not handle WM_DPICHANGED message: {}", e); + 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) + } + + // 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( + 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) + } + + // 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, + } +} + +fn hi_word(wparam: WPARAM) -> u16 { + ((wparam >> 16) & 0xffff) as u16 +} + +fn lo_word(lparam: LPARAM) -> u16 { + (lparam & 0xffff) as u16 +} 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) }) }