mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 20:10:13 +02:00
Add a proof of concept drag-and-drop snooper
This doesn't do anything useful yet, but it will print the names of files that are being dragged and dropped on the Wine server (using the OLE API) while the plugin is open.
This commit is contained in:
@@ -16,17 +16,116 @@
|
||||
|
||||
#include "xdnd-proxy.h"
|
||||
|
||||
void CALLBACK dnd_winevent_callback(HWINEVENTHOOK hWinEventHook,
|
||||
// FIXME: Remove
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* The window class name Wine uses for its `DoDragDrop()` tracker window.
|
||||
*
|
||||
* https://github.com/wine-mirror/wine/blob/d10887b8f56792ebcca717ccc28a289f7bcaf107/dlls/ole32/ole2.c#L101-L104
|
||||
*/
|
||||
static constexpr char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
|
||||
|
||||
/**
|
||||
* 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
|
||||
* contains the actual data.
|
||||
*
|
||||
* https://github.com/wine-mirror/wine/blob/d10887b8f56792ebcca717ccc28a289f7bcaf107/dlls/ole32/ole2.c#L54-L73
|
||||
*/
|
||||
struct TrackerWindowInfo {
|
||||
IDataObject* dataObject;
|
||||
IDropSource* dropSource;
|
||||
// ... more fields that we don't need
|
||||
};
|
||||
|
||||
void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/,
|
||||
DWORD event,
|
||||
HWND hwnd,
|
||||
LONG idObject,
|
||||
LONG idChild,
|
||||
DWORD idEventThread,
|
||||
DWORD dwmsEventTime) {
|
||||
// TODO: `EVENT_OBJECT_DESTROY` doesn't seem to be implemented by Wine, so
|
||||
// we can't rely on that.
|
||||
if (event == EVENT_OBJECT_CREATE && idObject == OBJID_WINDOW) {
|
||||
// TODO
|
||||
LONG /*idChild*/,
|
||||
DWORD /*idEventThread*/,
|
||||
DWORD /*dwmsEventTime*/) {
|
||||
// FIXME: Prevent this from being run by multiple processes at the same time
|
||||
|
||||
// XXX: `EVENT_OBJECT_DESTROY` doesn't seem to be implemented by Wine, so we
|
||||
// can't rely on that.
|
||||
if (!(event == EVENT_OBJECT_CREATE && idObject == OBJID_WINDOW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wine's drag-and-drop tracker windows always have the same window
|
||||
// class name, so we can easily identify them
|
||||
{
|
||||
std::array<char, 64> window_class_name{0};
|
||||
GetClassName(hwnd, window_class_name.data(), window_class_name.size());
|
||||
if (strcmp(window_class_name.data(), OLEDD_DRAGTRACKERCLASS) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// They apaprently use 0 instead of `GWLP_USERDATA` to store the tracker
|
||||
// data
|
||||
auto tracker_info =
|
||||
reinterpret_cast<TrackerWindowInfo*>(GetWindowLongPtr(hwnd, 0));
|
||||
if (!tracker_info) {
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumFORMATETC* enumerator = nullptr;
|
||||
tracker_info->dataObject->EnumFormatEtc(DATADIR_GET, &enumerator);
|
||||
if (!enumerator) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<FORMATETC, 16> supported_formats;
|
||||
unsigned int num_formats = 0;
|
||||
enumerator->Next(supported_formats.size(), supported_formats.data(),
|
||||
&num_formats);
|
||||
enumerator->Release();
|
||||
for (unsigned int format_idx = 0; format_idx < num_formats; format_idx++) {
|
||||
STGMEDIUM storage{};
|
||||
if (HRESULT result = tracker_info->dataObject->GetData(
|
||||
&supported_formats[format_idx], &storage);
|
||||
result == S_OK) {
|
||||
switch (storage.tymed) {
|
||||
case TYMED_HGLOBAL: {
|
||||
auto drop = static_cast<HDROP>(GlobalLock(storage.hGlobal));
|
||||
|
||||
std::array<WCHAR, 1024> 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
|
||||
<< " 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;
|
||||
}
|
||||
|
||||
GlobalUnlock(GlobalLock(storage.hGlobal));
|
||||
} break;
|
||||
case TYMED_FILE: {
|
||||
std::cerr << "Plugin wanted to drag-and-drop '"
|
||||
<< wine_get_unix_file_name(storage.lpszFileName)
|
||||
<< "'" << std::endl;
|
||||
} break;
|
||||
default: {
|
||||
std::cerr << "Unknown format " << storage.tymed
|
||||
<< std::endl;
|
||||
} break;
|
||||
}
|
||||
|
||||
// We won't release the storage, because we're not actually doing
|
||||
// the Windows drag and drop. We're just snooping in on the data
|
||||
// that's being dragged.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user