From df75427f69cedb120163fa4e9ef2c4b118a1519a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 10 Jul 2021 13:06:46 +0200 Subject: [PATCH] Store the dragged files so we can use them in XDND --- src/wine-host/group-host.cpp | 2 - src/wine-host/xdnd-proxy.cpp | 97 ++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/wine-host/group-host.cpp b/src/wine-host/group-host.cpp index 00d1492b..566dfb17 100644 --- a/src/wine-host/group-host.cpp +++ b/src/wine-host/group-host.cpp @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "boost-fix.h" - #include // Generated inside of the build directory diff --git a/src/wine-host/xdnd-proxy.cpp b/src/wine-host/xdnd-proxy.cpp index 108b45a3..65e8a89e 100644 --- a/src/wine-host/xdnd-proxy.cpp +++ b/src/wine-host/xdnd-proxy.cpp @@ -17,10 +17,12 @@ #include "xdnd-proxy.h" #include - -// FIXME: Remove #include +#include "boost-fix.h" + +#include + /** * The window class name Wine uses for its `DoDragDrop()` tracker window. * @@ -28,6 +30,25 @@ */ static constexpr char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32"; +/** + * We're doing a bit of a hybrid between a COM-style reference counted smart + * pointer and a singleton here because we need to ensure that there's only one + * proxy per process, but we want to free up the X11 connection when it's not + * needed anymore. Because of that this pointer may point to deallocated memory, + * so the reference count should be leading here. Oh and explained elsewhere, we + * won't even bother making this thread safe because it can only be called from + * the GUI thread anyways. + */ +static WineXdndProxy* instance = nullptr; + +/** + * The number of handles to our Wine->X11 drag-and-drop proxy object. To prevent + * running out of X11 connections when opening and closing a lot of plugin + * editors in a project, we'll free this again after the last editor in this + * process gets closed. + */ +static std::atomic_size_t instance_reference_count = 0; + /** * Part of the struct Wine uses to keep track of the data during an OLE * drag-and-drop operation. We only really care about the first field that @@ -84,11 +105,19 @@ void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/, return; } - std::array supported_formats; + // The plugin will indicate which formats they support for the + // drag-and-drop. In practice this is always going to be a single `HDROP` + // (through some `HGLOBAL` global memory) that contains a single file path. + // With this information we will set up XDND with those file paths, so we + // can drop the files onto native applications. + std::array supported_formats{}; unsigned int num_formats = 0; enumerator->Next(supported_formats.size(), supported_formats.data(), &num_formats); enumerator->Release(); + + // This will contain the normal, Unix-style paths to the files + boost::container::small_vector dragged_files; for (unsigned int format_idx = 0; format_idx < num_formats; format_idx++) { STGMEDIUM storage{}; if (HRESULT result = tracker_info->dataObject->GetData( @@ -97,33 +126,31 @@ void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/, switch (storage.tymed) { case TYMED_HGLOBAL: { auto drop = static_cast(GlobalLock(storage.hGlobal)); + if (!drop) { + std::cerr << "Failed to lock global memory in " + "drag-and-drop operation" + << std::endl; + continue; + } std::array file_name{0}; const uint32_t num_files = DragQueryFileW( drop, 0xFFFFFFFF, file_name.data(), file_name.size()); - - std::cerr << "Plugin wanted to drag-and-drop " << num_files - << (num_files == 1 ? " file:" : " files:") - << std::endl; for (uint32_t file_idx = 0; file_idx < num_files; file_idx++) { file_name[0] = 0; DragQueryFileW(drop, file_idx, file_name.data(), file_name.size()); - std::cerr << "- " - << wine_get_unix_file_name(file_name.data()) - << std::endl; + dragged_files.emplace_back( + wine_get_unix_file_name(file_name.data())); } - GlobalUnlock(GlobalLock(storage.hGlobal)); + GlobalUnlock(storage.hGlobal); } break; case TYMED_FILE: { - std::cerr << "Plugin wanted to drag-and-drop 1 file:" - << std::endl; - std::cerr << "- " - << wine_get_unix_file_name(storage.lpszFileName) - << std::endl; + dragged_files.emplace_back( + wine_get_unix_file_name(storage.lpszFileName)); } break; default: { std::cerr << "Unknown drag-and-drop format " @@ -136,6 +163,26 @@ void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/, } } } + + if (dragged_files.empty()) { + std::cerr + << "Plugin wanted to drag-and-drop, but didn't specify any files" + << std::endl; + return; + } + + std::cerr << "Plugin wanted to drag-and-drop " << dragged_files.size() + << (dragged_files.size() == 1 ? " file:" : " files:") + << std::endl; + for (const auto& file : dragged_files) { + std::cerr << "- " << file << std::endl; + } + + // This shouldn't be possible, but you can never be too sure! + if (instance_reference_count <= 0 || !instance) { + std::cerr << "Drag-and-drop proxy wasn't initialized yet" << std::endl; + return; + } } WineXdndProxy::WineXdndProxy() @@ -150,14 +197,6 @@ WineXdndProxy::WineXdndProxy() WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS), UnhookWinEvent) {} -/** - * The number of handles to our Wine->X11 drag-and-drop proxy object. To prevent - * running out of X11 connections when opening and closing a lot of plugin - * editors in a project, we'll free this again after the last editor in this - * process gets closed. - */ -static std::atomic_size_t instance_reference_count = 0; - WineXdndProxy::Handle::Handle(WineXdndProxy* proxy) : proxy(proxy) {} WineXdndProxy::Handle::~Handle() noexcept { @@ -175,14 +214,8 @@ WineXdndProxy::Handle::Handle(Handle&& o) noexcept : proxy(o.proxy) { } WineXdndProxy::Handle WineXdndProxy::init_proxy() { - // We're doing a bit of a hybrid between a COM-style reference counted smart - // pointer and a singleton here because we need to ensure that there's only - // one proxy per process, but we want to free up the X11 connection when - // it's not needed anymore. Because of that this pointer may point to - // deallocated memory, so the reference count should be leading here. Oh and - // explained elsewhere, we won't even bother making this thread safe because - // it can only be called from the GUI thread anyways. - static WineXdndProxy* instance = nullptr; + // See the `instance` global above for an explanation on what's going on + // here. if (instance_reference_count.fetch_add(1) == 0) { instance = new WineXdndProxy{}; }