mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Store the dragged files so we can use them in XDND
This commit is contained in:
@@ -14,8 +14,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "boost-fix.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
// Generated inside of the build directory
|
// Generated inside of the build directory
|
||||||
|
|||||||
@@ -17,10 +17,12 @@
|
|||||||
#include "xdnd-proxy.h"
|
#include "xdnd-proxy.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
// FIXME: Remove
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "boost-fix.h"
|
||||||
|
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The window class name Wine uses for its `DoDragDrop()` tracker window.
|
* The window class name Wine uses for its `DoDragDrop()` tracker window.
|
||||||
*
|
*
|
||||||
@@ -28,6 +30,25 @@
|
|||||||
*/
|
*/
|
||||||
static constexpr char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
|
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
|
* 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
|
* 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<FORMATETC, 16> 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<FORMATETC, 16> supported_formats{};
|
||||||
unsigned int num_formats = 0;
|
unsigned int num_formats = 0;
|
||||||
enumerator->Next(supported_formats.size(), supported_formats.data(),
|
enumerator->Next(supported_formats.size(), supported_formats.data(),
|
||||||
&num_formats);
|
&num_formats);
|
||||||
enumerator->Release();
|
enumerator->Release();
|
||||||
|
|
||||||
|
// This will contain the normal, Unix-style paths to the files
|
||||||
|
boost::container::small_vector<std::string, 4> dragged_files;
|
||||||
for (unsigned int format_idx = 0; format_idx < num_formats; format_idx++) {
|
for (unsigned int format_idx = 0; format_idx < num_formats; format_idx++) {
|
||||||
STGMEDIUM storage{};
|
STGMEDIUM storage{};
|
||||||
if (HRESULT result = tracker_info->dataObject->GetData(
|
if (HRESULT result = tracker_info->dataObject->GetData(
|
||||||
@@ -97,33 +126,31 @@ void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/,
|
|||||||
switch (storage.tymed) {
|
switch (storage.tymed) {
|
||||||
case TYMED_HGLOBAL: {
|
case TYMED_HGLOBAL: {
|
||||||
auto drop = static_cast<HDROP>(GlobalLock(storage.hGlobal));
|
auto drop = static_cast<HDROP>(GlobalLock(storage.hGlobal));
|
||||||
|
if (!drop) {
|
||||||
|
std::cerr << "Failed to lock global memory in "
|
||||||
|
"drag-and-drop operation"
|
||||||
|
<< std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::array<WCHAR, 1024> file_name{0};
|
std::array<WCHAR, 1024> file_name{0};
|
||||||
const uint32_t num_files = DragQueryFileW(
|
const uint32_t num_files = DragQueryFileW(
|
||||||
drop, 0xFFFFFFFF, file_name.data(), file_name.size());
|
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;
|
for (uint32_t file_idx = 0; file_idx < num_files;
|
||||||
file_idx++) {
|
file_idx++) {
|
||||||
file_name[0] = 0;
|
file_name[0] = 0;
|
||||||
DragQueryFileW(drop, file_idx, file_name.data(),
|
DragQueryFileW(drop, file_idx, file_name.data(),
|
||||||
file_name.size());
|
file_name.size());
|
||||||
|
|
||||||
std::cerr << "- "
|
dragged_files.emplace_back(
|
||||||
<< wine_get_unix_file_name(file_name.data())
|
wine_get_unix_file_name(file_name.data()));
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalUnlock(GlobalLock(storage.hGlobal));
|
GlobalUnlock(storage.hGlobal);
|
||||||
} break;
|
} break;
|
||||||
case TYMED_FILE: {
|
case TYMED_FILE: {
|
||||||
std::cerr << "Plugin wanted to drag-and-drop 1 file:"
|
dragged_files.emplace_back(
|
||||||
<< std::endl;
|
wine_get_unix_file_name(storage.lpszFileName));
|
||||||
std::cerr << "- "
|
|
||||||
<< wine_get_unix_file_name(storage.lpszFileName)
|
|
||||||
<< std::endl;
|
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
std::cerr << "Unknown drag-and-drop format "
|
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()
|
WineXdndProxy::WineXdndProxy()
|
||||||
@@ -150,14 +197,6 @@ WineXdndProxy::WineXdndProxy()
|
|||||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS),
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS),
|
||||||
UnhookWinEvent) {}
|
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(WineXdndProxy* proxy) : proxy(proxy) {}
|
||||||
|
|
||||||
WineXdndProxy::Handle::~Handle() noexcept {
|
WineXdndProxy::Handle::~Handle() noexcept {
|
||||||
@@ -175,14 +214,8 @@ WineXdndProxy::Handle::Handle(Handle&& o) noexcept : proxy(o.proxy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WineXdndProxy::Handle WineXdndProxy::init_proxy() {
|
WineXdndProxy::Handle WineXdndProxy::init_proxy() {
|
||||||
// We're doing a bit of a hybrid between a COM-style reference counted smart
|
// See the `instance` global above for an explanation on what's going on
|
||||||
// pointer and a singleton here because we need to ensure that there's only
|
// here.
|
||||||
// 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;
|
|
||||||
if (instance_reference_count.fetch_add(1) == 0) {
|
if (instance_reference_count.fetch_add(1) == 0) {
|
||||||
instance = new WineXdndProxy{};
|
instance = new WineXdndProxy{};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user