diff --git a/src/platform/win/drop_target.rs b/src/platform/win/drop_target.rs index 269aa407..7dc35601 100644 --- a/src/platform/win/drop_target.rs +++ b/src/platform/win/drop_target.rs @@ -10,15 +10,15 @@ use windows::Win32::System::Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYME use windows::Win32::System::Ole::*; use windows::Win32::System::SystemServices::MODIFIERKEYS_FLAGS; use windows_core::Ref; -use windows_sys::Win32::{ - Foundation::POINT, Graphics::Gdi::ScreenToClient, UI::Shell::DragQueryFileW, -}; +use windows_sys::Win32::UI::Shell::DragQueryFileW; use super::window_state::WindowState; +use crate::wrappers::win32::window::HWnd; use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent}; #[implement(IDropTarget)] pub(crate) struct DropTarget { + hwnd: HWnd, window_state: Weak, // These are cached since DragOver and DragLeave callbacks don't provide them, @@ -28,8 +28,9 @@ pub(crate) struct DropTarget { } impl DropTarget { - pub(crate) fn new(window_state: Weak) -> Self { + pub(crate) fn new(window_state: Weak, hwnd: HWnd) -> Self { Self { + hwnd, window_state, drag_position: Cell::new(PhysicalPosition::new(0, 0)), drop_data: RefCell::new(DropData::None), @@ -42,29 +43,27 @@ impl DropTarget { return; }; - unsafe { - let event = Event::Mouse(event); - let event_status = window_state.handle_event(event); - - if let Some(pdwEffect) = pdwEffect { - match event_status { - EventStatus::AcceptDrop(DropEffect::Copy) => *pdwEffect = DROPEFFECT_COPY, - EventStatus::AcceptDrop(DropEffect::Move) => *pdwEffect = DROPEFFECT_MOVE, - EventStatus::AcceptDrop(DropEffect::Link) => *pdwEffect = DROPEFFECT_LINK, - EventStatus::AcceptDrop(DropEffect::Scroll) => *pdwEffect = DROPEFFECT_SCROLL, - _ => *pdwEffect = DROPEFFECT_NONE, - } - } + let event = Event::Mouse(event); + let event_status = window_state.handle_event(event); + + let effect = match event_status { + EventStatus::AcceptDrop(DropEffect::Copy) => DROPEFFECT_COPY, + EventStatus::AcceptDrop(DropEffect::Move) => DROPEFFECT_MOVE, + EventStatus::AcceptDrop(DropEffect::Link) => DROPEFFECT_LINK, + EventStatus::AcceptDrop(DropEffect::Scroll) => DROPEFFECT_SCROLL, + _ => DROPEFFECT_NONE, + }; + + if let Some(pdwEffect) = pdwEffect { + unsafe { pdwEffect.write(effect) }; } } fn parse_coordinates(&self, pt: POINTL) { - let Some(window_state) = self.window_state.upgrade() else { + let Ok(phy_point) = self.hwnd.screen_to_client(PhysicalPosition::new(pt.x, pt.y)) else { return; }; - let mut pt = POINT { x: pt.x, y: pt.y }; - unsafe { ScreenToClient(window_state.hwnd, &mut pt as *mut POINT) }; - let phy_point = PhysicalPosition::new(pt.x, pt.y); + self.drag_position.set(phy_point); } diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index e45d2f4d..177a4a41 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -52,7 +52,7 @@ const WIN_FRAME_TIMER: NonZeroUsize = match NonZeroUsize::new(4242) { }; pub struct WindowHandle { - hwnd: Cell>, + hwnd: Cell>, is_open: Rc>, } @@ -60,7 +60,7 @@ impl WindowHandle { pub fn close(&self) { if let Some(hwnd) = self.hwnd.take() { unsafe { - PostMessageW(hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); + PostMessageW(hwnd.as_raw(), BV_WINDOW_MUST_CLOSE, 0, 0); } } } @@ -127,7 +127,7 @@ impl WindowImpl for BaseviewWindow { } } - let drop_target = ComObject::new(DropTarget::new(Rc::downgrade(window_state))); + let drop_target = ComObject::new(DropTarget::new(Rc::downgrade(window_state), window)); self._drop_target.set(Some(drop_target.clone())); ole_initialize()?; @@ -457,12 +457,8 @@ impl Window { let extended_user_32 = extended_user_32.clone(); move |hwnd: HWnd| { - let window_state = Rc::new(WindowState::new( - hwnd.as_raw(), - window_size, - options.scale, - extended_user_32, - )); + let window_state = + Rc::new(WindowState::new(hwnd, window_size, options.scale, extended_user_32)); BaseviewWindow { window_state, @@ -479,13 +475,10 @@ impl Window { } }; - let hwnd = + let window = create_window(&title, style, rect.size(), parent as *mut _, &dpi_ctx, initializer) .unwrap(); - // SAFETY: this handle should be safe to use - let window = unsafe { HWnd::from_raw(hwnd) }; - // FIXME: this SetTimer call could be in after_create, but for some reason it changes the ordering // for a parent+child window situation, which results in the parent drawing over the child. // This timer should be replaced by proper window redrawing/damage/vsync handling, but this @@ -495,7 +488,7 @@ impl Window { window.show_and_activate(); - WindowHandle { hwnd: Some(hwnd).into(), is_open: Rc::clone(&is_open) } + WindowHandle { hwnd: Some(window).into(), is_open: Rc::clone(&is_open) } } } diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index 7156c316..14f4fecd 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -9,13 +9,12 @@ use dpi::{PhysicalSize, Size}; use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::cell::{Cell, OnceCell, Ref, RefCell}; use std::num::NonZeroIsize; -use windows_sys::Win32::Foundation::HWND; use windows_sys::Win32::UI::WindowsAndMessaging::PostMessageW; /// All data associated with the window. pub(crate) struct WindowState { /// The HWND belonging to this window. - pub hwnd: HWND, + pub hwnd: HWnd, pub current_size: Cell>, pub current_dpi: Cell, // None if in non-system scale policy pub keyboard_state: RefCell, @@ -34,7 +33,7 @@ pub(crate) struct WindowState { impl WindowState { pub fn new( - hwnd: HWND, current_size: PhysicalSize, scale_policy: WindowScalePolicy, + hwnd: HWnd, current_size: PhysicalSize, scale_policy: WindowScalePolicy, user32: ExtendedUser32, ) -> Self { Self { @@ -92,21 +91,21 @@ impl WindowState { pub fn request_close(&self) { unsafe { - PostMessageW(self.hwnd, crate::platform::win::window::BV_WINDOW_MUST_CLOSE, 0, 0); + PostMessageW( + self.hwnd.as_raw(), + crate::platform::win::window::BV_WINDOW_MUST_CLOSE, + 0, + 0, + ); } } pub fn has_focus(&self) -> bool { - HWnd::get_focused_window() == self.hwnd + HWnd::get_focused_window() == self.hwnd.as_raw() } pub fn focus(&self) { - self.hwnd().set_focus().unwrap() - } - - fn hwnd(&self) -> HWnd { - // SAFETY: this handle should be safe to use - unsafe { HWnd::from_raw(self.hwnd) } + self.hwnd.set_focus().unwrap() } pub fn resize(&self, size: Size) { @@ -115,7 +114,7 @@ impl WindowState { let dpi = self.current_dpi.get(); let new_size = size.to_physical(dpi.scale_factor()); - self.hwnd().resize_and_activate(new_size, dpi, &self.user32).unwrap(); + self.hwnd.resize_and_activate(new_size, dpi, &self.user32).unwrap(); } pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { @@ -132,7 +131,7 @@ impl WindowState { } pub fn window_handle(&self) -> Option> { - let Some(hwnd) = NonZeroIsize::new(self.hwnd as _) else { unreachable!() }; + let Some(hwnd) = NonZeroIsize::new(self.hwnd.as_raw() as _) else { unreachable!() }; let mut handle = Win32WindowHandle::new(hwnd); handle.hinstance = Some(HInstance::get_from_dll().addr()); @@ -144,7 +143,7 @@ impl WindowState { } pub fn platform_handle(&self) -> PlatformHandle { - let Some(hwnd) = NonZeroIsize::new(self.hwnd as _) else { unreachable!() }; + let Some(hwnd) = NonZeroIsize::new(self.hwnd.as_raw() as _) else { unreachable!() }; PlatformHandle { hwnd } } } diff --git a/src/wrappers/win32/window.rs b/src/wrappers/win32/window.rs index 94bd72a8..b6ac247e 100644 --- a/src/wrappers/win32/window.rs +++ b/src/wrappers/win32/window.rs @@ -7,7 +7,7 @@ use data::WindowData; use dpi::PhysicalSize; pub use handle::HWnd; pub use proc::wnd_proc; -use std::ptr::null_mut; +use std::ptr::{null_mut, NonNull}; use std::rc::Rc; use window_class::RegisteredClass; use windows_core::{Error, Result, HSTRING}; @@ -53,7 +53,7 @@ pub trait WindowImpl: 'static { pub fn create_window( title: &HSTRING, style: WindowStyle, nc_size: PhysicalSize, parent: HWND, _dpi_ctx: &DpiAwarenessContext, initializer: impl FnOnce(HWnd) -> W + 'static, -) -> Result { +) -> Result { let instance = HInstance::get_from_dll(); let window_class = RegisteredClass::register_new(instance, Some(wnd_proc::))?; @@ -76,9 +76,9 @@ pub fn create_window( ) }; - if hwnd.is_null() { - return Err(Error::from_thread()); - } + let Some(hwnd) = NonNull::new(hwnd) else { return Err(Error::from_thread()) }; + // SAFETY: This Hwnd is valid since it came from CreateWindowExW + let hwnd = unsafe { HWnd::from_raw(hwnd) }; Ok(hwnd) } diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 8051a7d6..4a14d41e 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -1,12 +1,14 @@ use crate::wrappers::win32::style::WindowStyle; use crate::wrappers::win32::user32::ExtendedUser32; use crate::wrappers::win32::{Dpi, DpiAwarenessContext, Rect}; -use dpi::PhysicalSize; +use dpi::{PhysicalPosition, PhysicalSize}; +use std::ffi::c_void; use std::num::NonZeroUsize; use std::ptr::{null_mut, NonNull}; use windows::Win32::System::Ole::IDropTarget; use windows_core::{Error, Interface, InterfaceRef, Result, HRESULT}; -use windows_sys::Win32::Foundation::{SetLastError, HWND, S_OK}; +use windows_sys::Win32::Foundation::{SetLastError, HWND, POINT, S_OK}; +use windows_sys::Win32::Graphics::Gdi::ScreenToClient; use windows_sys::Win32::System::Ole::{RegisterDragDrop, RevokeDragDrop}; use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ GetFocus, ReleaseCapture, SetCapture, SetFocus, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT, @@ -24,19 +26,19 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ /// /// The role of this type is to help safely encapsulating most of the unsafe Win32 HWND APIs. #[derive(Copy, Clone)] -pub struct HWnd(HWND); +pub struct HWnd(NonNull); impl HWnd { - pub unsafe fn from_raw(hwnd: HWND) -> Self { + pub unsafe fn from_raw(hwnd: NonNull) -> Self { Self(hwnd) } pub fn as_raw(&self) -> HWND { - self.0 + self.0.as_ptr() } pub fn get_userdata_ptr(&self) -> Option> { - let ptr = unsafe { GetWindowLongPtrW(self.0, GWLP_USERDATA) }; + let ptr = unsafe { GetWindowLongPtrW(self.as_raw(), GWLP_USERDATA) }; NonNull::new(ptr as *mut T) } @@ -44,7 +46,7 @@ impl HWnd { // SAFETY: This function is always safe to call unsafe { SetLastError(0) }; // SAFETY: This type guarantees the HWND is safe to use. - let previous = unsafe { SetWindowLongPtrW(self.0, GWLP_USERDATA, data as isize) }; + let previous = unsafe { SetWindowLongPtrW(self.as_raw(), GWLP_USERDATA, data as isize) }; if previous != 0 { return Ok(()); } @@ -63,7 +65,7 @@ impl HWnd { // SAFETY: This function is always safe to call unsafe { SetLastError(0) }; // SAFETY: This type guarantees the HWND is still valid. - let result = unsafe { GetWindowLongW(self.0, index) }; + let result = unsafe { GetWindowLongW(self.as_raw(), index) }; if result != 0 { return Ok(result); } @@ -91,7 +93,7 @@ impl HWnd { }; // SAFETY: This type guarantees the HWND is safe to use. - match unsafe { get_dpi_for_window(self.0) } { + match unsafe { get_dpi_for_window(self.as_raw()) } { 0 => Err(Error::from_thread()), dpi => Ok(Dpi(dpi)), } @@ -100,7 +102,7 @@ impl HWnd { pub fn register_drag_drop(&self, drop_target: InterfaceRef) -> Result<()> { // SAFETY: This type guarantees the HWND is safe to use, // and the interface pointer comes from a valid InterfaceRef. - let result = unsafe { RegisterDragDrop(self.0, drop_target.as_raw()) }; + let result = unsafe { RegisterDragDrop(self.as_raw(), drop_target.as_raw()) }; match result { S_OK => Ok(()), @@ -109,18 +111,18 @@ impl HWnd { } pub fn revoke_drag_drop(&self) -> Result<()> { - let result = unsafe { RevokeDragDrop(self.0) }; + let result = unsafe { RevokeDragDrop(self.as_raw()) }; match result { S_OK => Ok(()), - e => Err(Error::new(HRESULT(e), "RegisterDragDrop failed")), + e => Err(Error::new(HRESULT(e), "RevokeDragDrop failed")), } } pub fn resize_nc_and_activate(&self, size: PhysicalSize) -> Result<()> { let result = unsafe { SetWindowPos( - self.0, + self.as_raw(), null_mut(), // Ignored by SWP_NOZORDER 0, // Ignored by SWP_NOMOVE 0, // Ignored by SWP_NOMOVE @@ -151,7 +153,7 @@ impl HWnd { /// Returns true if the window was previously visible, false otherwise pub fn show_and_activate(&self) -> bool { - let result = unsafe { ShowWindow(self.0, SW_SHOW) }; + let result = unsafe { ShowWindow(self.as_raw(), SW_SHOW) }; result != 0 } @@ -161,7 +163,7 @@ impl HWnd { let result = unsafe { SetWindowPos( - self.0, + self.as_raw(), null_mut(), // Ignored by SWP_NOZORDER nc_rect.0.left, nc_rect.0.top, @@ -179,7 +181,7 @@ impl HWnd { } pub fn set_timer(&self, timer_id: NonZeroUsize, elapse: u32) -> Result<()> { - let result = unsafe { SetTimer(self.0, timer_id.get(), elapse, None) }; + let result = unsafe { SetTimer(self.as_raw(), timer_id.get(), elapse, None) }; if result == 0 { return Err(Error::from_thread()); @@ -189,7 +191,7 @@ impl HWnd { } pub fn set_focus(&self) -> Result<()> { - let previous = unsafe { SetFocus(self.0) }; + let previous = unsafe { SetFocus(self.as_raw()) }; if !previous.is_null() { return Ok(()); } @@ -205,7 +207,7 @@ impl HWnd { } pub fn destroy(&self) -> Result<()> { - let result = unsafe { DestroyWindow(self.0) }; + let result = unsafe { DestroyWindow(self.as_raw()) }; if result == 0 { return Err(Error::from_thread()); @@ -221,7 +223,7 @@ impl HWnd { pub fn set_capture(&self) { // SAFETY: This type guarantees the HWND is safe to use. - unsafe { SetCapture(self.0) }; + unsafe { SetCapture(self.as_raw()) }; } pub fn release_capture() { @@ -234,7 +236,7 @@ impl HWnd { cbSize: size_of::() as u32, dwFlags: TME_LEAVE, dwHoverTime: 0, - hwndTrack: self.0, + hwndTrack: self.as_raw(), }; // SAFETY: eventtrack pointer comes from a reference, and the struct it points to is filled @@ -244,4 +246,15 @@ impl HWnd { _ => Ok(()), } } + + pub fn screen_to_client(&self, pt: PhysicalPosition) -> Result> { + let mut pt = POINT { x: pt.x, y: pt.y }; + let result = unsafe { ScreenToClient(self.as_raw(), &mut pt as *mut POINT) }; + + if result == 0 { + return Err(Error::from_thread()); + } + + Ok(PhysicalPosition::new(pt.x, pt.y)) + } } diff --git a/src/wrappers/win32/window/proc.rs b/src/wrappers/win32/window/proc.rs index 8656fee0..ee8bc7c6 100644 --- a/src/wrappers/win32/window/proc.rs +++ b/src/wrappers/win32/window/proc.rs @@ -11,6 +11,7 @@ pub unsafe extern "system" fn wnd_proc( window: HWND, message_code: u32, w_param: WPARAM, l_param: LPARAM, ) -> LRESULT { let handle_default = || unsafe { DefWindowProcW(window, message_code, w_param, l_param) }; + let Some(window) = NonNull::new(window) else { return -1 }; let window = unsafe { HWnd::from_raw(window) }; match message_code {