diff --git a/Cargo.toml b/Cargo.toml index af46b66..162fe60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ xcb-util = { version = "0.3", features = ["icccm"] } nix = "0.22.0" [target.'cfg(target_os="windows")'.dependencies] -winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi", "ole2", "oleidl", "winerror"] } +winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi", "ole2", "oleidl", "shellapi", "winerror"] } uuid = { version = "0.8", features = ["v4"], optional = true } [target.'cfg(target_os="macos")'.dependencies] diff --git a/src/event.rs b/src/event.rs index 594905f..b7ce911 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use keyboard_types::{KeyboardEvent, Modifiers}; use crate::{Point, WindowInfo}; @@ -32,7 +34,7 @@ pub enum ScrollDelta { }, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum MouseEvent { /// The mouse cursor was moved CursorMoved { @@ -77,10 +79,16 @@ pub enum MouseEvent { CursorLeft, // TODO: Document - DragEntered, + DragEntered { + data: Option, + }, + DragMoved, DragLeft, - DragDropped, + + DragDropped { + data: Option, + }, } #[derive(Debug, Clone)] @@ -106,6 +114,11 @@ pub enum DropEffect { Scroll, } +#[derive(Debug, Clone, PartialEq)] +pub enum DropData { + Files(Vec), +} + /// Return value for [WindowHandler::on_event](`crate::WindowHandler::on_event()`), /// indicating whether the event was handled by your window or should be passed /// back to the platform. diff --git a/src/win/window.rs b/src/win/window.rs index 3c9c336..7a3181c 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -4,10 +4,12 @@ use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM, DWOR use winapi::shared::ntdef::{HRESULT, ULONG}; use winapi::shared::windef::{HWND, RECT, POINTL}; use winapi::shared::winerror::{S_OK, E_NOINTERFACE}; +use winapi::shared::wtypes::DVASPECT_CONTENT; use winapi::um::combaseapi::CoCreateGuid; -use winapi::um::objidl::IDataObject; +use winapi::um::objidl::{IDataObject, STGMEDIUM, FORMATETC, TYMED_HGLOBAL}; use winapi::um::ole2::{RegisterDragDrop, OleInitialize}; use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl, LPDROPTARGET, DROPEFFECT_COPY, DROPEFFECT_NONE, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL}; +use winapi::um::shellapi::DragQueryFileW; use winapi::um::unknwnbase::{IUnknownVtbl, IUnknown}; use winapi::um::winuser::{ AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, @@ -20,15 +22,16 @@ use winapi::um::winuser::{ WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, - XBUTTON1, XBUTTON2, + XBUTTON1, XBUTTON2, CF_HDROP, }; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; -use std::ffi::{c_void, OsStr}; +use std::ffi::{c_void, OsStr, OsString}; use std::marker::PhantomData; use std::mem::transmute; use std::os::windows::ffi::OsStrExt; +use std::os::windows::prelude::OsStringExt; use std::ptr::null_mut; use std::rc::Rc; use std::sync::Arc; @@ -39,7 +42,7 @@ const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1; use crate::{ Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, - WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, DropEffect, EventStatus, + WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, DropEffect, EventStatus, DropData, }; use super::keyboard::KeyboardState; @@ -843,6 +846,49 @@ impl DropTarget { } } + fn parse_drop_data(data_object: &IDataObject) -> Option { + let format = FORMATETC { + cfFormat: CF_HDROP as u16, + ptd: null_mut(), + dwAspect: DVASPECT_CONTENT, + lindex: -1, + tymed: TYMED_HGLOBAL, + }; + + let mut medium = STGMEDIUM { + tymed: 0, + u: null_mut(), + pUnkForRelease: null_mut(), + }; + + unsafe { + let hresult = data_object.GetData(&format, &mut medium); + assert!(hresult == S_OK); // TODO: Error handling + + let hdrop = transmute((*medium.u).hGlobal()); + + let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0); + if item_count == 0 { + return None; + } + + let mut paths = Vec::with_capacity(item_count as usize); + + for i in 0..item_count { + let characters = DragQueryFileW(hdrop, i, null_mut(), 0); + let buffer_size = characters as usize + 1; + let mut buffer = Vec::::with_capacity(buffer_size); + + DragQueryFileW(hdrop, i, transmute(buffer.spare_capacity_mut().as_mut_ptr()), buffer_size as u32); + buffer.set_len(buffer_size); + + paths.push(OsString::from_wide(&buffer[..characters as usize]).into()) + } + + Some(DropData::Files(paths)) + } + } + unsafe extern "system" fn query_interface( this: *mut IUnknown, riid: REFIID, @@ -888,7 +934,12 @@ impl DropTarget { pdwEffect: *mut DWORD, ) -> HRESULT { - Self::on_event(this, Some(pdwEffect), MouseEvent::DragEntered {}); + let data = Self::parse_drop_data(&*pDataObj); + let event = MouseEvent::DragEntered { + data + }; + + Self::on_event(this, Some(pdwEffect), event); S_OK } @@ -899,12 +950,13 @@ impl DropTarget { pdwEffect: *mut DWORD, ) -> HRESULT { - Self::on_event(this, Some(pdwEffect), MouseEvent::DragMoved {}); + let event = MouseEvent::DragMoved {}; + Self::on_event(this, Some(pdwEffect), event); S_OK } unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT { - Self::on_event(this, None, MouseEvent::DragLeft {}); + Self::on_event(this, None, MouseEvent::DragLeft); S_OK } @@ -916,7 +968,12 @@ impl DropTarget { pdwEffect: *mut DWORD, ) -> HRESULT { - Self::on_event(this, Some(pdwEffect), MouseEvent::DragDropped {}); + let data = Self::parse_drop_data(&*pDataObj); + let event = MouseEvent::DragDropped { + data + }; + + Self::on_event(this, Some(pdwEffect), event); S_OK } }