mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 13:31:29 +11:00
Implement HoveredFile and HoveredFileCancelled on Windows (#662)
* Implement HoveredFile and HoveredFileCancelled on Windows (#448) * Update CHANGELOG. * Applied code organizational corrections and fixed IDropHandler leak on window destroy. * Moved FileDropHandle to a separate file.
This commit is contained in:
parent
da1d479e55
commit
214e157e5d
|
@ -13,6 +13,7 @@
|
||||||
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
|
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
|
||||||
- Fixed UTF8 handling bug in X11 `set_title` function.
|
- Fixed UTF8 handling bug in X11 `set_title` function.
|
||||||
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
|
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
|
||||||
|
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
|
||||||
- On Windows, fix `Window::set_maximized`.
|
- On Windows, fix `Window::set_maximized`.
|
||||||
- On Windows 10, fix transparency (#260).
|
- On Windows 10, fix transparency (#260).
|
||||||
- On macOS, fix modifiers during key repeat.
|
- On macOS, fix modifiers during key repeat.
|
||||||
|
|
|
@ -39,15 +39,19 @@ version = "0.3.6"
|
||||||
features = [
|
features = [
|
||||||
"combaseapi",
|
"combaseapi",
|
||||||
"dwmapi",
|
"dwmapi",
|
||||||
|
"errhandlingapi",
|
||||||
"hidusage",
|
"hidusage",
|
||||||
"libloaderapi",
|
"libloaderapi",
|
||||||
"objbase",
|
"objbase",
|
||||||
|
"ole2",
|
||||||
"processthreadsapi",
|
"processthreadsapi",
|
||||||
"shellapi",
|
"shellapi",
|
||||||
"shellscalingapi",
|
"shellscalingapi",
|
||||||
"shobjidl_core",
|
"shobjidl_core",
|
||||||
"unknwnbase",
|
"unknwnbase",
|
||||||
|
"winbase",
|
||||||
"windowsx",
|
"windowsx",
|
||||||
|
"winerror",
|
||||||
"wingdi",
|
"wingdi",
|
||||||
"winnt",
|
"winnt",
|
||||||
"winuser",
|
"winuser",
|
||||||
|
|
203
src/platform/windows/drop_handler.rs
Normal file
203
src/platform/windows/drop_handler.rs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::{mem, ptr};
|
||||||
|
|
||||||
|
use winapi::ctypes::c_void;
|
||||||
|
use winapi::shared::guiddef::REFIID;
|
||||||
|
use winapi::shared::minwindef::{DWORD, MAX_PATH, UINT, ULONG};
|
||||||
|
use winapi::shared::windef::{HWND, POINTL};
|
||||||
|
use winapi::shared::winerror::S_OK;
|
||||||
|
use winapi::um::objidl::IDataObject;
|
||||||
|
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl};
|
||||||
|
use winapi::um::winnt::HRESULT;
|
||||||
|
use winapi::um::{shellapi, unknwnbase};
|
||||||
|
|
||||||
|
use platform::platform::events_loop::send_event;
|
||||||
|
use platform::platform::WindowId;
|
||||||
|
|
||||||
|
use {Event, WindowId as SuperWindowId};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FileDropHandlerData {
|
||||||
|
pub interface: IDropTarget,
|
||||||
|
refcount: AtomicUsize,
|
||||||
|
window: HWND,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FileDropHandler {
|
||||||
|
pub data: *mut FileDropHandlerData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl FileDropHandler {
|
||||||
|
pub fn new(window: HWND) -> FileDropHandler {
|
||||||
|
let data = Box::new(FileDropHandlerData {
|
||||||
|
interface: IDropTarget {
|
||||||
|
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
|
||||||
|
},
|
||||||
|
refcount: AtomicUsize::new(1),
|
||||||
|
window,
|
||||||
|
});
|
||||||
|
FileDropHandler {
|
||||||
|
data: Box::into_raw(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement IUnknown
|
||||||
|
pub unsafe extern "system" fn QueryInterface(
|
||||||
|
_this: *mut unknwnbase::IUnknown,
|
||||||
|
_riid: REFIID,
|
||||||
|
_ppvObject: *mut *mut c_void,
|
||||||
|
) -> HRESULT {
|
||||||
|
// This function doesn't appear to be required for an `IDropTarget`.
|
||||||
|
// An implementation would be nice however.
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "system" fn AddRef(this: *mut unknwnbase::IUnknown) -> ULONG {
|
||||||
|
let drop_handler_data = Self::from_interface(this);
|
||||||
|
let count = drop_handler_data.refcount.fetch_add(1, Ordering::Release) + 1;
|
||||||
|
count as ULONG
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "system" fn Release(this: *mut unknwnbase::IUnknown) -> ULONG {
|
||||||
|
let drop_handler = Self::from_interface(this);
|
||||||
|
let count = drop_handler.refcount.fetch_sub(1, Ordering::Release) - 1;
|
||||||
|
if count == 0 {
|
||||||
|
// Destroy the underlying data
|
||||||
|
Box::from_raw(drop_handler as *mut FileDropHandlerData);
|
||||||
|
}
|
||||||
|
count as ULONG
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "system" fn DragEnter(
|
||||||
|
this: *mut IDropTarget,
|
||||||
|
pDataObj: *const IDataObject,
|
||||||
|
_grfKeyState: DWORD,
|
||||||
|
_pt: *const POINTL,
|
||||||
|
_pdwEffect: *mut DWORD,
|
||||||
|
) -> HRESULT {
|
||||||
|
use events::WindowEvent::HoveredFile;
|
||||||
|
let drop_handler = Self::from_interface(this);
|
||||||
|
Self::iterate_filenames(pDataObj, |filename| {
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(drop_handler.window)),
|
||||||
|
event: HoveredFile(filename),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "system" fn DragOver(
|
||||||
|
_this: *mut IDropTarget,
|
||||||
|
_grfKeyState: DWORD,
|
||||||
|
_pt: *const POINTL,
|
||||||
|
_pdwEffect: *mut DWORD,
|
||||||
|
) -> HRESULT {
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT {
|
||||||
|
use events::WindowEvent::HoveredFileCancelled;
|
||||||
|
let drop_handler = Self::from_interface(this);
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(drop_handler.window)),
|
||||||
|
event: HoveredFileCancelled,
|
||||||
|
});
|
||||||
|
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "system" fn Drop(
|
||||||
|
this: *mut IDropTarget,
|
||||||
|
pDataObj: *const IDataObject,
|
||||||
|
_grfKeyState: DWORD,
|
||||||
|
_pt: *const POINTL,
|
||||||
|
_pdwEffect: *mut DWORD,
|
||||||
|
) -> HRESULT {
|
||||||
|
use events::WindowEvent::DroppedFile;
|
||||||
|
let drop_handler = Self::from_interface(this);
|
||||||
|
let hdrop = Self::iterate_filenames(pDataObj, |filename| {
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(drop_handler.window)),
|
||||||
|
event: DroppedFile(filename),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
shellapi::DragFinish(hdrop);
|
||||||
|
|
||||||
|
S_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_interface<'a, InterfaceT>(this: *mut InterfaceT) -> &'a mut FileDropHandlerData {
|
||||||
|
&mut *(this as *mut _)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> shellapi::HDROP
|
||||||
|
where
|
||||||
|
F: Fn(PathBuf),
|
||||||
|
{
|
||||||
|
use winapi::ctypes::wchar_t;
|
||||||
|
use winapi::shared::winerror::SUCCEEDED;
|
||||||
|
use winapi::shared::wtypes::{CLIPFORMAT, DVASPECT_CONTENT};
|
||||||
|
use winapi::um::objidl::{FORMATETC, TYMED_HGLOBAL};
|
||||||
|
use winapi::um::shellapi::DragQueryFileW;
|
||||||
|
use winapi::um::winuser::CF_HDROP;
|
||||||
|
|
||||||
|
let mut drop_format = FORMATETC {
|
||||||
|
cfFormat: CF_HDROP as CLIPFORMAT,
|
||||||
|
ptd: ptr::null(),
|
||||||
|
dwAspect: DVASPECT_CONTENT,
|
||||||
|
lindex: -1,
|
||||||
|
tymed: TYMED_HGLOBAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut medium = mem::uninitialized();
|
||||||
|
if SUCCEEDED((*data_obj).GetData(&mut drop_format, &mut medium)) {
|
||||||
|
let hglobal = (*medium.u).hGlobal();
|
||||||
|
let hdrop = (*hglobal) as shellapi::HDROP;
|
||||||
|
|
||||||
|
// The second parameter (0xFFFFFFFF) instructs the function to return the item count
|
||||||
|
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||||
|
|
||||||
|
let mut pathbuf: [wchar_t; MAX_PATH] = mem::uninitialized();
|
||||||
|
|
||||||
|
for i in 0..item_count {
|
||||||
|
let character_count =
|
||||||
|
DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), MAX_PATH as UINT) as usize;
|
||||||
|
|
||||||
|
if character_count > 0 {
|
||||||
|
callback(OsString::from_wide(&pathbuf[0..character_count]).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hdrop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The call to `GetData` must succeed and the file handle must be returned before this
|
||||||
|
// point
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FileDropHandler {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
FileDropHandler::Release(self.data as *mut unknwnbase::IUnknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
|
||||||
|
parent: unknwnbase::IUnknownVtbl {
|
||||||
|
QueryInterface: FileDropHandler::QueryInterface,
|
||||||
|
AddRef: FileDropHandler::AddRef,
|
||||||
|
Release: FileDropHandler::Release,
|
||||||
|
},
|
||||||
|
DragEnter: FileDropHandler::DragEnter,
|
||||||
|
DragOver: FileDropHandler::DragOver,
|
||||||
|
DragLeave: FileDropHandler::DragLeave,
|
||||||
|
Drop: FileDropHandler::Drop,
|
||||||
|
};
|
|
@ -15,8 +15,6 @@
|
||||||
use std::{mem, ptr, thread};
|
use std::{mem, ptr, thread};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::os::windows::ffi::OsStringExt;
|
|
||||||
use std::os::windows::io::AsRawHandle;
|
use std::os::windows::io::AsRawHandle;
|
||||||
use std::sync::{Arc, Barrier, mpsc, Mutex};
|
use std::sync::{Arc, Barrier, mpsc, Mutex};
|
||||||
|
|
||||||
|
@ -29,13 +27,14 @@ use winapi::shared::minwindef::{
|
||||||
LOWORD,
|
LOWORD,
|
||||||
LPARAM,
|
LPARAM,
|
||||||
LRESULT,
|
LRESULT,
|
||||||
MAX_PATH,
|
|
||||||
UINT,
|
UINT,
|
||||||
WPARAM,
|
WPARAM,
|
||||||
};
|
};
|
||||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||||
use winapi::shared::windowsx;
|
use winapi::shared::windowsx;
|
||||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
use winapi::shared::winerror::S_OK;
|
||||||
|
use winapi::um::{winuser, processthreadsapi, ole2};
|
||||||
|
use winapi::um::oleidl::LPDROPTARGET;
|
||||||
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
|
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
@ -57,6 +56,7 @@ use platform::platform::dpi::{
|
||||||
enable_non_client_dpi_scaling,
|
enable_non_client_dpi_scaling,
|
||||||
get_hwnd_scale_factor,
|
get_hwnd_scale_factor,
|
||||||
};
|
};
|
||||||
|
use platform::platform::drop_handler::FileDropHandler;
|
||||||
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
||||||
use platform::platform::icon::WinIcon;
|
use platform::platform::icon::WinIcon;
|
||||||
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
|
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
|
||||||
|
@ -161,7 +161,8 @@ impl EventsLoop {
|
||||||
*context_stash.borrow_mut() = Some(ThreadLocalData {
|
*context_stash.borrow_mut() = Some(ThreadLocalData {
|
||||||
sender: tx,
|
sender: tx,
|
||||||
windows: HashMap::with_capacity(4),
|
windows: HashMap::with_capacity(4),
|
||||||
mouse_buttons_down: 0
|
file_drop_handlers: HashMap::with_capacity(4),
|
||||||
|
mouse_buttons_down: 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -371,11 +372,12 @@ thread_local!(static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::
|
||||||
struct ThreadLocalData {
|
struct ThreadLocalData {
|
||||||
sender: mpsc::Sender<Event>,
|
sender: mpsc::Sender<Event>,
|
||||||
windows: HashMap<HWND, Arc<Mutex<WindowState>>>,
|
windows: HashMap<HWND, Arc<Mutex<WindowState>>>,
|
||||||
mouse_buttons_down: u32
|
file_drop_handlers: HashMap<HWND, FileDropHandler>, // Each window has its own drop handler.
|
||||||
|
mouse_buttons_down: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function that dispatches an event on the current thread.
|
// Utility function that dispatches an event on the current thread.
|
||||||
fn send_event(event: Event) {
|
pub fn send_event(event: Event) {
|
||||||
CONTEXT_STASH.with(|context_stash| {
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
let context_stash = context_stash.borrow();
|
let context_stash = context_stash.borrow();
|
||||||
|
|
||||||
|
@ -423,6 +425,30 @@ pub unsafe extern "system" fn callback(
|
||||||
lparam: LPARAM,
|
lparam: LPARAM,
|
||||||
) -> LRESULT {
|
) -> LRESULT {
|
||||||
match msg {
|
match msg {
|
||||||
|
winuser::WM_CREATE => {
|
||||||
|
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE};
|
||||||
|
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
|
||||||
|
// It is ok if the initialize result is `S_FALSE` because it might happen that
|
||||||
|
// multiple windows are created on the same thread.
|
||||||
|
if ole_init_result == OLE_E_WRONGCOMPOBJ {
|
||||||
|
panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`");
|
||||||
|
} else if ole_init_result == RPC_E_CHANGED_MODE {
|
||||||
|
panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`");
|
||||||
|
}
|
||||||
|
|
||||||
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
|
|
||||||
|
let drop_handlers = &mut context_stash.as_mut().unwrap().file_drop_handlers;
|
||||||
|
let new_handler = FileDropHandler::new(window);
|
||||||
|
let handler_interface_ptr = &mut (*new_handler.data).interface as LPDROPTARGET;
|
||||||
|
drop_handlers.insert(window, new_handler);
|
||||||
|
|
||||||
|
assert_eq!(ole2::RegisterDragDrop(window, handler_interface_ptr), S_OK);
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
winuser::WM_NCCREATE => {
|
winuser::WM_NCCREATE => {
|
||||||
enable_non_client_dpi_scaling(window);
|
enable_non_client_dpi_scaling(window);
|
||||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
@ -441,7 +467,10 @@ pub unsafe extern "system" fn callback(
|
||||||
use events::WindowEvent::Destroyed;
|
use events::WindowEvent::Destroyed;
|
||||||
CONTEXT_STASH.with(|context_stash| {
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
let mut context_stash = context_stash.borrow_mut();
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
context_stash.as_mut().unwrap().windows.remove(&window);
|
ole2::RevokeDragDrop(window);
|
||||||
|
let context_stash_mut = context_stash.as_mut().unwrap();
|
||||||
|
context_stash_mut.file_drop_handlers.remove(&window);
|
||||||
|
context_stash_mut.windows.remove(&window);
|
||||||
});
|
});
|
||||||
send_event(Event::WindowEvent {
|
send_event(Event::WindowEvent {
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
@ -993,24 +1022,7 @@ pub unsafe extern "system" fn callback(
|
||||||
},
|
},
|
||||||
|
|
||||||
winuser::WM_DROPFILES => {
|
winuser::WM_DROPFILES => {
|
||||||
use events::WindowEvent::DroppedFile;
|
// See `FileDropHandler` for implementation.
|
||||||
|
|
||||||
let hdrop = wparam as shellapi::HDROP;
|
|
||||||
let mut pathbuf: [u16; MAX_PATH] = mem::uninitialized();
|
|
||||||
let num_drops = shellapi::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
|
||||||
|
|
||||||
for i in 0..num_drops {
|
|
||||||
let nch = shellapi::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
|
||||||
MAX_PATH as u32) as usize;
|
|
||||||
if nch > 0 {
|
|
||||||
send_event(Event::WindowEvent {
|
|
||||||
window_id: SuperWindowId(WindowId(window)),
|
|
||||||
event: DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shellapi::DragFinish(hdrop);
|
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ unsafe impl Send for WindowId {}
|
||||||
unsafe impl Sync for WindowId {}
|
unsafe impl Sync for WindowId {}
|
||||||
|
|
||||||
mod dpi;
|
mod dpi;
|
||||||
|
mod drop_handler;
|
||||||
mod event;
|
mod event;
|
||||||
mod events_loop;
|
mod events_loop;
|
||||||
mod icon;
|
mod icon;
|
||||||
|
|
Loading…
Reference in a new issue