mpv/video/out/win32/droptarget.c

234 lines
7.1 KiB
C
Raw Normal View History

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <windows.h>
#include <ole2.h>
#include <shobjidl.h>
#include "common/msg.h"
#include "common/common.h"
#include "input/input.h"
#include "input/event.h"
#include "osdep/atomic.h"
#include "osdep/io.h"
#include "mpv_talloc.h"
#include "droptarget.h"
typedef struct tagDropTarget {
IDropTarget iface;
atomic_int refCnt;
DWORD lastEffect;
IDataObject* dataObj;
struct mp_log *log;
struct input_ctx *input_ctx;
} DropTarget;
static FORMATETC fmtetc_file = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
static FORMATETC fmtetc_url = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
static void DropTarget_Destroy(DropTarget* This)
{
if (This->dataObj != NULL) {
This->dataObj->lpVtbl->Release(This->dataObj);
This->dataObj->lpVtbl = NULL;
}
talloc_free(This);
}
static HRESULT STDMETHODCALLTYPE DropTarget_QueryInterface(IDropTarget* This,
REFIID riid,
void** ppvObject)
{
if (!IsEqualGUID(riid, &IID_IUnknown) ||
!IsEqualGUID(riid, &IID_IDataObject)) {
*ppvObject = NULL;
return E_NOINTERFACE;
}
*ppvObject = This;
This->lpVtbl->AddRef(This);
return S_OK;
}
static ULONG STDMETHODCALLTYPE DropTarget_AddRef(IDropTarget* This)
{
DropTarget* t = (DropTarget*)This;
return atomic_fetch_add(&t->refCnt, 1) + 1;
}
static ULONG STDMETHODCALLTYPE DropTarget_Release(IDropTarget* This)
{
DropTarget* t = (DropTarget*)This;
ULONG cRef = atomic_fetch_add(&t->refCnt, -1) - 1;
if (cRef == 0) {
DropTarget_Destroy(t);
}
return cRef;
}
static HRESULT STDMETHODCALLTYPE DropTarget_DragEnter(IDropTarget* This,
IDataObject* pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect)
{
DropTarget* t = (DropTarget*)This;
pDataObj->lpVtbl->AddRef(pDataObj);
if (pDataObj->lpVtbl->QueryGetData(pDataObj, &fmtetc_file) != S_OK &&
pDataObj->lpVtbl->QueryGetData(pDataObj, &fmtetc_url) != S_OK) {
*pdwEffect = DROPEFFECT_NONE;
}
if (t->dataObj != NULL) {
t->dataObj->lpVtbl->Release(t->dataObj);
}
t->dataObj = pDataObj;
t->lastEffect = *pdwEffect;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE DropTarget_DragOver(IDropTarget* This,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect)
{
DropTarget* t = (DropTarget*)This;
*pdwEffect = t->lastEffect;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE DropTarget_DragLeave(IDropTarget* This)
{
DropTarget* t = (DropTarget*)This;
if (t->dataObj != NULL) {
t->dataObj->lpVtbl->Release(t->dataObj);
t->dataObj = NULL;
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE DropTarget_Drop(IDropTarget* This,
IDataObject* pDataObj,
DWORD grfKeyState, POINTL pt,
DWORD* pdwEffect)
{
DropTarget* t = (DropTarget*)This;
STGMEDIUM medium;
if (t->dataObj != NULL) {
t->dataObj->lpVtbl->Release(t->dataObj);
t->dataObj = NULL;
}
enum mp_dnd_action action = (grfKeyState & MK_SHIFT) ? DND_APPEND : DND_REPLACE;
pDataObj->lpVtbl->AddRef(pDataObj);
if (pDataObj->lpVtbl->GetData(pDataObj, &fmtetc_file, &medium) == S_OK) {
if (GlobalLock(medium.hGlobal) != NULL) {
HDROP hDrop = (HDROP)medium.hGlobal;
UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
char** files = talloc_zero_array(NULL, char*, numFiles);
UINT nrecvd_files = 0;
for (UINT i = 0; i < numFiles; i++) {
UINT len = DragQueryFileW(hDrop, i, NULL, 0);
wchar_t* buf = talloc_array(NULL, wchar_t, len + 1);
if (DragQueryFileW(hDrop, i, buf, len + 1) == len) {
char* fname = mp_to_utf8(files, buf);
files[nrecvd_files++] = fname;
MP_VERBOSE(t, "received dropped file: %s\n", fname);
} else {
MP_ERR(t, "error getting dropped file name\n");
}
talloc_free(buf);
}
GlobalUnlock(medium.hGlobal);
mp_event_drop_files(t->input_ctx, nrecvd_files, files,
action);
talloc_free(files);
}
ReleaseStgMedium(&medium);
} else if (pDataObj->lpVtbl->GetData(pDataObj,
&fmtetc_url, &medium) == S_OK) {
// get the URL encoded in US-ASCII
wchar_t* wurl = GlobalLock(medium.hGlobal);
if (wurl != NULL) {
char *url = mp_to_utf8(NULL, wurl);
if (mp_event_drop_mime_data(t->input_ctx, "text/uri-list",
bstr0(url), action) > 0) {
MP_VERBOSE(t, "received dropped URL: %s\n", url);
} else {
MP_ERR(t, "error getting dropped URL\n");
}
talloc_free(url);
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
else {
t->lastEffect = DROPEFFECT_NONE;
}
pDataObj->lpVtbl->Release(pDataObj);
*pdwEffect = t->lastEffect;
return S_OK;
}
IDropTarget *mp_w32_droptarget_create(struct mp_log *log,
struct input_ctx *input_ctx)
{
DropTarget* dropTarget = talloc(NULL, DropTarget);
IDropTargetVtbl* vtbl = talloc(dropTarget, IDropTargetVtbl);
*vtbl = (IDropTargetVtbl){
DropTarget_QueryInterface, DropTarget_AddRef, DropTarget_Release,
DropTarget_DragEnter, DropTarget_DragOver, DropTarget_DragLeave,
DropTarget_Drop
};
fmtetc_url.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocatorW"));
dropTarget->iface.lpVtbl = vtbl;
atomic_store(&dropTarget->refCnt, 0);
dropTarget->lastEffect = 0;
dropTarget->dataObj = NULL;
dropTarget->log = mp_log_new(dropTarget, log, "droptarget");
dropTarget->input_ctx = input_ctx;
return &dropTarget->iface;
}