winit/platform_impl/windows/
drop_handler.rs1use std::ffi::{c_void, OsString};
2use std::os::windows::ffi::OsStringExt;
3use std::path::PathBuf;
4use std::ptr;
5use std::sync::atomic::{AtomicUsize, Ordering};
6
7use windows_sys::core::{IUnknown, GUID, HRESULT};
8use windows_sys::Win32::Foundation::{DV_E_FORMATETC, HWND, POINTL, S_OK};
9use windows_sys::Win32::System::Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYMED_HGLOBAL};
10use windows_sys::Win32::System::Ole::{CF_HDROP, DROPEFFECT_COPY, DROPEFFECT_NONE};
11use windows_sys::Win32::UI::Shell::{DragFinish, DragQueryFileW, HDROP};
12
13use tracing::debug;
14
15use crate::platform_impl::platform::definitions::{
16 IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl,
17};
18use crate::platform_impl::platform::WindowId;
19
20use crate::event::Event;
21use crate::window::WindowId as RootWindowId;
22
23#[repr(C)]
24pub struct FileDropHandlerData {
25 pub interface: IDropTarget,
26 refcount: AtomicUsize,
27 window: HWND,
28 send_event: Box<dyn Fn(Event<()>)>,
29 cursor_effect: u32,
30 hovered_is_valid: bool, }
33
34pub struct FileDropHandler {
35 pub data: *mut FileDropHandlerData,
36}
37
38#[allow(non_snake_case)]
39impl FileDropHandler {
40 pub fn new(window: HWND, send_event: Box<dyn Fn(Event<()>)>) -> FileDropHandler {
41 let data = Box::new(FileDropHandlerData {
42 interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl },
43 refcount: AtomicUsize::new(1),
44 window,
45 send_event,
46 cursor_effect: DROPEFFECT_NONE,
47 hovered_is_valid: false,
48 });
49 FileDropHandler { data: Box::into_raw(data) }
50 }
51
52 pub unsafe extern "system" fn QueryInterface(
54 _this: *mut IUnknown,
55 _riid: *const GUID,
56 _ppvObject: *mut *mut c_void,
57 ) -> HRESULT {
58 unimplemented!();
61 }
62
63 pub unsafe extern "system" fn AddRef(this: *mut IUnknown) -> u32 {
64 let drop_handler_data = unsafe { Self::from_interface(this) };
65 let count = drop_handler_data.refcount.fetch_add(1, Ordering::Release) + 1;
66 count as u32
67 }
68
69 pub unsafe extern "system" fn Release(this: *mut IUnknown) -> u32 {
70 let drop_handler = unsafe { Self::from_interface(this) };
71 let count = drop_handler.refcount.fetch_sub(1, Ordering::Release) - 1;
72 if count == 0 {
73 drop(unsafe { Box::from_raw(drop_handler as *mut FileDropHandlerData) });
75 }
76 count as u32
77 }
78
79 pub unsafe extern "system" fn DragEnter(
80 this: *mut IDropTarget,
81 pDataObj: *const IDataObject,
82 _grfKeyState: u32,
83 _pt: *const POINTL,
84 pdwEffect: *mut u32,
85 ) -> HRESULT {
86 use crate::event::WindowEvent::HoveredFile;
87 let drop_handler = unsafe { Self::from_interface(this) };
88 let hdrop = unsafe {
89 Self::iterate_filenames(pDataObj, |filename| {
90 drop_handler.send_event(Event::WindowEvent {
91 window_id: RootWindowId(WindowId(drop_handler.window)),
92 event: HoveredFile(filename),
93 });
94 })
95 };
96 drop_handler.hovered_is_valid = hdrop.is_some();
97 drop_handler.cursor_effect =
98 if drop_handler.hovered_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE };
99 unsafe {
100 *pdwEffect = drop_handler.cursor_effect;
101 }
102
103 S_OK
104 }
105
106 pub unsafe extern "system" fn DragOver(
107 this: *mut IDropTarget,
108 _grfKeyState: u32,
109 _pt: *const POINTL,
110 pdwEffect: *mut u32,
111 ) -> HRESULT {
112 let drop_handler = unsafe { Self::from_interface(this) };
113 unsafe {
114 *pdwEffect = drop_handler.cursor_effect;
115 }
116
117 S_OK
118 }
119
120 pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT {
121 use crate::event::WindowEvent::HoveredFileCancelled;
122 let drop_handler = unsafe { Self::from_interface(this) };
123 if drop_handler.hovered_is_valid {
124 drop_handler.send_event(Event::WindowEvent {
125 window_id: RootWindowId(WindowId(drop_handler.window)),
126 event: HoveredFileCancelled,
127 });
128 }
129
130 S_OK
131 }
132
133 pub unsafe extern "system" fn Drop(
134 this: *mut IDropTarget,
135 pDataObj: *const IDataObject,
136 _grfKeyState: u32,
137 _pt: *const POINTL,
138 _pdwEffect: *mut u32,
139 ) -> HRESULT {
140 use crate::event::WindowEvent::DroppedFile;
141 let drop_handler = unsafe { Self::from_interface(this) };
142 let hdrop = unsafe {
143 Self::iterate_filenames(pDataObj, |filename| {
144 drop_handler.send_event(Event::WindowEvent {
145 window_id: RootWindowId(WindowId(drop_handler.window)),
146 event: DroppedFile(filename),
147 });
148 })
149 };
150 if let Some(hdrop) = hdrop {
151 unsafe { DragFinish(hdrop) };
152 }
153
154 S_OK
155 }
156
157 unsafe fn from_interface<'a, InterfaceT>(this: *mut InterfaceT) -> &'a mut FileDropHandlerData {
158 unsafe { &mut *(this as *mut _) }
159 }
160
161 unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> Option<HDROP>
162 where
163 F: Fn(PathBuf),
164 {
165 let drop_format = FORMATETC {
166 cfFormat: CF_HDROP,
167 ptd: ptr::null_mut(),
168 dwAspect: DVASPECT_CONTENT,
169 lindex: -1,
170 tymed: TYMED_HGLOBAL as u32,
171 };
172
173 let mut medium = unsafe { std::mem::zeroed() };
174 let get_data_fn = unsafe { (*(*data_obj).cast::<IDataObjectVtbl>()).GetData };
175 let get_data_result = unsafe { get_data_fn(data_obj as *mut _, &drop_format, &mut medium) };
176 if get_data_result >= 0 {
177 let hdrop = unsafe { medium.u.hGlobal as HDROP };
178
179 let item_count = unsafe { DragQueryFileW(hdrop, 0xffffffff, ptr::null_mut(), 0) };
181
182 for i in 0..item_count {
183 let character_count =
187 unsafe { DragQueryFileW(hdrop, i, ptr::null_mut(), 0) as usize };
188 let str_len = character_count + 1;
189
190 let mut path_buf = Vec::with_capacity(str_len);
192 unsafe {
193 DragQueryFileW(hdrop, i, path_buf.as_mut_ptr(), str_len as u32);
194 path_buf.set_len(str_len);
195 }
196
197 callback(OsString::from_wide(&path_buf[0..character_count]).into());
198 }
199
200 Some(hdrop)
201 } else if get_data_result == DV_E_FORMATETC {
202 debug!("Error occurred while processing dropped/hovered item: item is not a file.");
205 None
206 } else {
207 debug!("Unexpected error occurred while processing dropped/hovered item.");
208 None
209 }
210 }
211}
212
213impl FileDropHandlerData {
214 fn send_event(&self, event: Event<()>) {
215 (self.send_event)(event);
216 }
217}
218
219impl Drop for FileDropHandler {
220 fn drop(&mut self) {
221 unsafe {
222 FileDropHandler::Release(self.data as *mut IUnknown);
223 }
224 }
225}
226
227static DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
228 parent: IUnknownVtbl {
229 QueryInterface: FileDropHandler::QueryInterface,
230 AddRef: FileDropHandler::AddRef,
231 Release: FileDropHandler::Release,
232 },
233 DragEnter: FileDropHandler::DragEnter,
234 DragOver: FileDropHandler::DragOver,
235 DragLeave: FileDropHandler::DragLeave,
236 Drop: FileDropHandler::Drop,
237};