@@ -10,8 +10,10 @@ use windows::Win32::System::Ole::*;
1010use windows:: Win32 :: System :: SystemServices :: MODIFIERKEYS_FLAGS ;
1111use windows_core:: Ref ;
1212use windows_sys:: Win32 :: {
13- Foundation :: POINT , Graphics :: Gdi :: ScreenToClient , UI :: Shell :: DragQueryFileW ,
14- UI :: WindowsAndMessaging :: GetCursorPos ,
13+ Foundation :: { POINT , RECT } ,
14+ Graphics :: Gdi :: ScreenToClient ,
15+ UI :: Shell :: DragQueryFileW ,
16+ UI :: WindowsAndMessaging :: { GetClientRect , GetCursorPos } ,
1517} ;
1618
1719use crate :: { DropData , DropEffect , Event , EventStatus , MouseEvent , PhyPoint , Point } ;
@@ -26,6 +28,10 @@ pub(super) struct DropTarget {
2628 // and handling drag move events gets awkward on the client end otherwise
2729 drag_position : Cell < Point > ,
2830 drop_data : RefCell < DropData > ,
31+ /// Whether drag client coordinates are physical pixels (`true`) or already logical
32+ /// (`false`). Cached when `is_drag_coords_physical` is called, and cleared on `DragLeave`
33+ /// and `Drop`.
34+ drag_coords_physical : Cell < Option < bool > > ,
2935}
3036
3137impl DropTarget {
@@ -34,6 +40,7 @@ impl DropTarget {
3440 window_state,
3541 drag_position : Cell :: new ( Point :: new ( 0.0 , 0.0 ) ) ,
3642 drop_data : RefCell :: new ( DropData :: None ) ,
43+ drag_coords_physical : Cell :: new ( None ) ,
3744 }
3845 }
3946
@@ -62,23 +69,52 @@ impl DropTarget {
6269 }
6370 }
6471
65- fn parse_coordinates ( & self , _pt : POINTL ) {
72+ /// Returns `true` when client coordinates from `GetCursorPos`/`ScreenToClient` are physical
73+ /// pixels and should be scaled with [`PhyPoint::to_logical`]. Returns `false` when they are
74+ /// already in logical space.
75+ ///
76+ /// For some reason, this can vary based on the combination of parent window AND drag source.
77+ /// Most of the time the coordinates are physical, but logical coordinates have been observed
78+ /// with Bitwig as the parent and Windows Explorer as the drag source.
79+ ///
80+ /// Cached on self.drag_coords_physical.
81+ fn is_drag_coords_physical ( & self , window_state : & WindowState ) -> bool {
82+ match self . drag_coords_physical . get ( ) {
83+ Some ( physical) => physical,
84+ None => {
85+ let mut rect = RECT { left : 0 , top : 0 , right : 0 , bottom : 0 } ;
86+ unsafe { GetClientRect ( window_state. hwnd , & mut rect) } ;
87+ let client_w = ( rect. right - rect. left ) as u32 ;
88+ let physical_w = window_state. window_info ( ) . physical_size ( ) . width ;
89+ let logical_w = window_state. window_info ( ) . logical_size ( ) . width as u32 ;
90+ let physical = client_w. abs_diff ( physical_w) < client_w. abs_diff ( logical_w) ;
91+
92+ self . drag_coords_physical . set ( Some ( physical) ) ;
93+ physical
94+ }
95+ }
96+ }
97+
98+ fn end_drag_session ( & self ) {
99+ self . drag_coords_physical . set ( None ) ;
100+ }
101+
102+ fn parse_coordinates ( & self ) {
66103 let Some ( window_state) = self . window_state . upgrade ( ) else {
67104 return ;
68105 } ;
69- // OLE-supplied points can disagree with the actual cursor position for embedded
70- // child windows (DPI virtualization / DragEnter quirks). Query the cursor directly
71- // so drag coordinates match WM_MOUSEMOVE.
106+
107+ // Some parents pass weird coordinates via OLE `pt`. Query the cursor directly instead.
72108 let mut pt = POINT { x : 0 , y : 0 } ;
73109 unsafe {
74110 GetCursorPos ( & mut pt as * mut POINT ) ;
75111 ScreenToClient ( window_state. hwnd , & mut pt as * mut POINT ) ;
76112 }
77- let logical_point = if window_state. has_parent ( ) {
78- // If the window has a parent, the coordinates are already in logical coordinates
79- Point :: new ( pt. x as f64 , pt. y as f64 )
80- } else {
113+
114+ let logical_point = if self . is_drag_coords_physical ( & window_state) {
81115 PhyPoint :: new ( pt. x , pt. y ) . to_logical ( & window_state. window_info ( ) )
116+ } else {
117+ Point :: new ( pt. x as f64 , pt. y as f64 )
82118 } ;
83119 self . drag_position . set ( logical_point) ;
84120 }
@@ -126,7 +162,7 @@ impl DropTarget {
126162#[ allow( non_snake_case) ]
127163impl IDropTarget_Impl for DropTarget_Impl {
128164 fn DragEnter (
129- & self , pdataobj : Ref < IDataObject > , grfkeystate : MODIFIERKEYS_FLAGS , pt : & POINTL ,
165+ & self , pdataobj : Ref < IDataObject > , grfkeystate : MODIFIERKEYS_FLAGS , _pt : & POINTL ,
130166 pdweffect : * mut DROPEFFECT ,
131167 ) -> windows_core:: Result < ( ) > {
132168 let Some ( window_state) = self . window_state . upgrade ( ) else {
@@ -136,7 +172,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
136172 let modifiers =
137173 window_state. keyboard_state ( ) . get_modifiers_from_mouse_wparam ( grfkeystate. 0 as usize ) ;
138174
139- self . parse_coordinates ( * pt ) ;
175+ self . parse_coordinates ( ) ;
140176 self . parse_drop_data ( pdataobj. unwrap ( ) ) ;
141177
142178 let event = MouseEvent :: DragEntered {
@@ -150,7 +186,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
150186 }
151187
152188 fn DragOver (
153- & self , grfkeystate : MODIFIERKEYS_FLAGS , pt : & POINTL , pdweffect : * mut DROPEFFECT ,
189+ & self , grfkeystate : MODIFIERKEYS_FLAGS , _pt : & POINTL , pdweffect : * mut DROPEFFECT ,
154190 ) -> windows_core:: Result < ( ) > {
155191 let Some ( window_state) = self . window_state . upgrade ( ) else {
156192 return Err ( E_UNEXPECTED . into ( ) ) ;
@@ -159,7 +195,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
159195 let modifiers =
160196 window_state. keyboard_state ( ) . get_modifiers_from_mouse_wparam ( grfkeystate. 0 as usize ) ;
161197
162- self . parse_coordinates ( * pt ) ;
198+ self . parse_coordinates ( ) ;
163199
164200 let event = MouseEvent :: DragMoved {
165201 position : self . drag_position . get ( ) ,
@@ -172,12 +208,13 @@ impl IDropTarget_Impl for DropTarget_Impl {
172208 }
173209
174210 fn DragLeave ( & self ) -> windows_core:: Result < ( ) > {
211+ self . end_drag_session ( ) ;
175212 self . on_event ( None , MouseEvent :: DragLeft ) ;
176213 Ok ( ( ) )
177214 }
178215
179216 fn Drop (
180- & self , pdataobj : Ref < IDataObject > , grfkeystate : MODIFIERKEYS_FLAGS , pt : & POINTL ,
217+ & self , pdataobj : Ref < IDataObject > , grfkeystate : MODIFIERKEYS_FLAGS , _pt : & POINTL ,
181218 pdweffect : * mut DROPEFFECT ,
182219 ) -> windows_core:: Result < ( ) > {
183220 let Some ( window_state) = self . window_state . upgrade ( ) else {
@@ -187,7 +224,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
187224 let modifiers =
188225 window_state. keyboard_state ( ) . get_modifiers_from_mouse_wparam ( grfkeystate. 0 as usize ) ;
189226
190- self . parse_coordinates ( * pt ) ;
227+ self . parse_coordinates ( ) ;
191228 self . parse_drop_data ( pdataobj. unwrap ( ) ) ;
192229
193230 let event = MouseEvent :: DragDropped {
@@ -197,6 +234,7 @@ impl IDropTarget_Impl for DropTarget_Impl {
197234 } ;
198235
199236 self . on_event ( Some ( pdweffect) , event) ;
237+ self . end_drag_session ( ) ;
200238 Ok ( ( ) )
201239 }
202240}
0 commit comments