Fix panic when dragging text onto a window on Windws (#697) (#711)

* Fix panic when dragging text onto a window on Windws (#697)

* Changed `panic` to `debug` (log) when unknow error occurs in `GetData` while processing a drag-drop / hover event. Plus added appropriate cursor effect if hovered item can not be processed.

* Improved code clarity.

* Add documentation to clarify behaviour of `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled`

* Add period at the end of sentences in documentation.
This commit is contained in:
Artúr Kovács 2018-11-20 09:28:26 +01:00 committed by Francesca Plebani
parent b049a4dc66
commit 04ca2cf9f4
3 changed files with 51 additions and 18 deletions

View file

@ -7,6 +7,7 @@
- On Windows, fix issue where resizing or moving window would eat `Awakened` events. - On Windows, fix issue where resizing or moving window would eat `Awakened` events.
- On X11, fixed a segfault when using virtual monitors with XRandR. - On X11, fixed a segfault when using virtual monitors with XRandR.
- Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum. - Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum.
- On Windows, fix issue where hovering or dropping a non file item would create a panic.
# Version 0.18.0 (2018-11-07) # Version 0.18.0 (2018-11-07)

View file

@ -37,12 +37,21 @@ pub enum WindowEvent {
Destroyed, Destroyed,
/// A file has been dropped into the window. /// A file has been dropped into the window.
///
/// When the user drops multiple files at once, this event will be emitted for each file
/// separately.
DroppedFile(PathBuf), DroppedFile(PathBuf),
/// A file is being hovered over the window. /// A file is being hovered over the window.
///
/// When the user hovers multiple files at once, this event will be emitted for each file
/// separately.
HoveredFile(PathBuf), HoveredFile(PathBuf),
/// A file was hovered, but has exited the window. /// A file was hovered, but has exited the window.
///
/// There will be a single `HoveredFileCancelled` event triggered even if multiple files were
/// hovered.
HoveredFileCancelled, HoveredFileCancelled,
/// The window received a unicode character. /// The window received a unicode character.

View file

@ -10,7 +10,7 @@ use winapi::shared::minwindef::{DWORD, MAX_PATH, UINT, ULONG};
use winapi::shared::windef::{HWND, POINTL}; use winapi::shared::windef::{HWND, POINTL};
use winapi::shared::winerror::S_OK; use winapi::shared::winerror::S_OK;
use winapi::um::objidl::IDataObject; use winapi::um::objidl::IDataObject;
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl}; use winapi::um::oleidl::{DROPEFFECT_COPY, DROPEFFECT_NONE, IDropTarget, IDropTargetVtbl};
use winapi::um::winnt::HRESULT; use winapi::um::winnt::HRESULT;
use winapi::um::{shellapi, unknwnbase}; use winapi::um::{shellapi, unknwnbase};
@ -24,6 +24,8 @@ pub struct FileDropHandlerData {
pub interface: IDropTarget, pub interface: IDropTarget,
refcount: AtomicUsize, refcount: AtomicUsize,
window: HWND, window: HWND,
cursor_effect: DWORD,
hovered_is_valid: bool, // If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted
} }
pub struct FileDropHandler { pub struct FileDropHandler {
@ -39,6 +41,8 @@ impl FileDropHandler {
}, },
refcount: AtomicUsize::new(1), refcount: AtomicUsize::new(1),
window, window,
cursor_effect: DROPEFFECT_NONE,
hovered_is_valid: false,
}); });
FileDropHandler { FileDropHandler {
data: Box::into_raw(data), data: Box::into_raw(data),
@ -77,36 +81,48 @@ impl FileDropHandler {
pDataObj: *const IDataObject, pDataObj: *const IDataObject,
_grfKeyState: DWORD, _grfKeyState: DWORD,
_pt: *const POINTL, _pt: *const POINTL,
_pdwEffect: *mut DWORD, pdwEffect: *mut DWORD,
) -> HRESULT { ) -> HRESULT {
use events::WindowEvent::HoveredFile; use events::WindowEvent::HoveredFile;
let drop_handler = Self::from_interface(this); let drop_handler = Self::from_interface(this);
Self::iterate_filenames(pDataObj, |filename| { let hdrop = Self::iterate_filenames(pDataObj, |filename| {
send_event(Event::WindowEvent { send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(drop_handler.window)), window_id: SuperWindowId(WindowId(drop_handler.window)),
event: HoveredFile(filename), event: HoveredFile(filename),
}); });
}); });
drop_handler.hovered_is_valid = hdrop.is_some();
drop_handler.cursor_effect = if drop_handler.hovered_is_valid {
DROPEFFECT_COPY
} else {
DROPEFFECT_NONE
};
*pdwEffect = drop_handler.cursor_effect;
S_OK S_OK
} }
pub unsafe extern "system" fn DragOver( pub unsafe extern "system" fn DragOver(
_this: *mut IDropTarget, this: *mut IDropTarget,
_grfKeyState: DWORD, _grfKeyState: DWORD,
_pt: *const POINTL, _pt: *const POINTL,
_pdwEffect: *mut DWORD, pdwEffect: *mut DWORD,
) -> HRESULT { ) -> HRESULT {
let drop_handler = Self::from_interface(this);
*pdwEffect = drop_handler.cursor_effect;
S_OK S_OK
} }
pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT {
use events::WindowEvent::HoveredFileCancelled; use events::WindowEvent::HoveredFileCancelled;
let drop_handler = Self::from_interface(this); let drop_handler = Self::from_interface(this);
send_event(Event::WindowEvent { if drop_handler.hovered_is_valid {
window_id: SuperWindowId(WindowId(drop_handler.window)), send_event(Event::WindowEvent {
event: HoveredFileCancelled, window_id: SuperWindowId(WindowId(drop_handler.window)),
}); event: HoveredFileCancelled,
});
}
S_OK S_OK
} }
@ -126,7 +142,9 @@ impl FileDropHandler {
event: DroppedFile(filename), event: DroppedFile(filename),
}); });
}); });
shellapi::DragFinish(hdrop); if let Some(hdrop) = hdrop {
shellapi::DragFinish(hdrop);
}
S_OK S_OK
} }
@ -135,12 +153,12 @@ impl FileDropHandler {
&mut *(this as *mut _) &mut *(this as *mut _)
} }
unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> shellapi::HDROP unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> Option<shellapi::HDROP>
where where
F: Fn(PathBuf), F: Fn(PathBuf),
{ {
use winapi::ctypes::wchar_t; use winapi::ctypes::wchar_t;
use winapi::shared::winerror::SUCCEEDED; use winapi::shared::winerror::{SUCCEEDED, DV_E_FORMATETC};
use winapi::shared::wtypes::{CLIPFORMAT, DVASPECT_CONTENT}; use winapi::shared::wtypes::{CLIPFORMAT, DVASPECT_CONTENT};
use winapi::um::objidl::{FORMATETC, TYMED_HGLOBAL}; use winapi::um::objidl::{FORMATETC, TYMED_HGLOBAL};
use winapi::um::shellapi::DragQueryFileW; use winapi::um::shellapi::DragQueryFileW;
@ -155,7 +173,8 @@ impl FileDropHandler {
}; };
let mut medium = mem::uninitialized(); let mut medium = mem::uninitialized();
if SUCCEEDED((*data_obj).GetData(&mut drop_format, &mut medium)) { let get_data_result = (*data_obj).GetData(&mut drop_format, &mut medium);
if SUCCEEDED(get_data_result) {
let hglobal = (*medium.u).hGlobal(); let hglobal = (*medium.u).hGlobal();
let hdrop = (*hglobal) as shellapi::HDROP; let hdrop = (*hglobal) as shellapi::HDROP;
@ -173,12 +192,16 @@ impl FileDropHandler {
} }
} }
return hdrop; return Some(hdrop);
} else if get_data_result == DV_E_FORMATETC {
// If the dropped item is not a file this error will occur.
// In this case it is OK to return without taking further action.
debug!("Error occured while processing dropped/hovered item: item is not a file.");
return None;
} else {
debug!("Unexpected error occured while processing dropped/hovered item.");
return None;
} }
// The call to `GetData` must succeed and the file handle must be returned before this
// point
unreachable!();
} }
} }