mirror of https://github.com/mpv-player/mpv
231 lines
6.8 KiB
C
231 lines
6.8 KiB
C
/*
|
|
* 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 <stdatomic.h>
|
|
|
|
#include <windows.h>
|
|
#include <ole2.h>
|
|
#include <shellapi.h>
|
|
#include <shobjidl.h>
|
|
|
|
#include "common/msg.h"
|
|
#include "common/common.h"
|
|
#include "input/input.h"
|
|
#include "input/event.h"
|
|
#include "osdep/io.h"
|
|
#include "osdep/windows_utils.h"
|
|
#include "mpv_talloc.h"
|
|
|
|
#include "droptarget.h"
|
|
|
|
struct droptarget {
|
|
IDropTarget iface;
|
|
atomic_int ref_cnt;
|
|
struct mp_log *log;
|
|
struct input_ctx *input_ctx;
|
|
struct mp_vo_opts *opts;
|
|
DWORD last_effect;
|
|
IDataObject *data_obj;
|
|
};
|
|
|
|
static FORMATETC fmtetc_file = {
|
|
.cfFormat = CF_HDROP,
|
|
.dwAspect = DVASPECT_CONTENT,
|
|
.lindex = -1,
|
|
.tymed = TYMED_HGLOBAL,
|
|
};
|
|
|
|
static FORMATETC fmtetc_url = {
|
|
.dwAspect = DVASPECT_CONTENT,
|
|
.lindex = -1,
|
|
.tymed = TYMED_HGLOBAL,
|
|
};
|
|
|
|
static void DropTarget_Destroy(struct droptarget *t)
|
|
{
|
|
SAFE_RELEASE(t->data_obj);
|
|
talloc_free(t);
|
|
}
|
|
|
|
static STDMETHODIMP DropTarget_QueryInterface(IDropTarget *self, REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget)) {
|
|
*ppvObject = self;
|
|
IDropTarget_AddRef(self);
|
|
return S_OK;
|
|
}
|
|
|
|
*ppvObject = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static STDMETHODIMP_(ULONG) DropTarget_AddRef(IDropTarget *self)
|
|
{
|
|
struct droptarget *t = (struct droptarget *)self;
|
|
return atomic_fetch_add(&t->ref_cnt, 1) + 1;
|
|
}
|
|
|
|
static STDMETHODIMP_(ULONG) DropTarget_Release(IDropTarget *self)
|
|
{
|
|
struct droptarget *t = (struct droptarget *)self;
|
|
|
|
ULONG ref_cnt = atomic_fetch_add(&t->ref_cnt, -1) - 1;
|
|
if (ref_cnt == 0)
|
|
DropTarget_Destroy(t);
|
|
return ref_cnt;
|
|
}
|
|
|
|
static STDMETHODIMP DropTarget_DragEnter(IDropTarget *self,
|
|
IDataObject *pDataObj,
|
|
DWORD grfKeyState, POINTL pt,
|
|
DWORD *pdwEffect)
|
|
{
|
|
struct droptarget *t = (struct droptarget *)self;
|
|
|
|
IDataObject_AddRef(pDataObj);
|
|
if (FAILED(IDataObject_QueryGetData(pDataObj, &fmtetc_file)) &&
|
|
FAILED(IDataObject_QueryGetData(pDataObj, &fmtetc_url)))
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
}
|
|
|
|
SAFE_RELEASE(t->data_obj);
|
|
t->data_obj = pDataObj;
|
|
t->last_effect = *pdwEffect;
|
|
return S_OK;
|
|
}
|
|
|
|
static STDMETHODIMP DropTarget_DragOver(IDropTarget *self, DWORD grfKeyState,
|
|
POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
struct droptarget *t = (struct droptarget *)self;
|
|
|
|
*pdwEffect = t->last_effect;
|
|
return S_OK;
|
|
}
|
|
|
|
static STDMETHODIMP DropTarget_DragLeave(IDropTarget *self)
|
|
{
|
|
struct droptarget *t = (struct droptarget *)self;
|
|
|
|
SAFE_RELEASE(t->data_obj);
|
|
return S_OK;
|
|
}
|
|
|
|
static STDMETHODIMP DropTarget_Drop(IDropTarget *self, IDataObject *pDataObj,
|
|
DWORD grfKeyState, POINTL pt,
|
|
DWORD *pdwEffect)
|
|
{
|
|
struct droptarget *t = (struct droptarget *)self;
|
|
|
|
enum mp_dnd_action action;
|
|
if (t->opts->drag_and_drop >= 0) {
|
|
action = t->opts->drag_and_drop;
|
|
} else {
|
|
action = (grfKeyState & MK_SHIFT) ? DND_APPEND : DND_REPLACE;
|
|
}
|
|
|
|
SAFE_RELEASE(t->data_obj);
|
|
|
|
STGMEDIUM medium;
|
|
if (t->opts->drag_and_drop == -2) {
|
|
t->last_effect = DROPEFFECT_NONE;
|
|
} else if (SUCCEEDED(IDataObject_GetData(pDataObj, &fmtetc_file, &medium))) {
|
|
if (GlobalLock(medium.hGlobal)) {
|
|
HDROP drop = medium.hGlobal;
|
|
|
|
UINT files_num = DragQueryFileW(drop, 0xFFFFFFFF, NULL, 0);
|
|
char **files = talloc_zero_array(NULL, char*, files_num);
|
|
|
|
UINT recvd_files = 0;
|
|
for (UINT i = 0; i < files_num; i++) {
|
|
UINT len = DragQueryFileW(drop, i, NULL, 0);
|
|
wchar_t *buf = talloc_array(NULL, wchar_t, len + 1);
|
|
|
|
if (DragQueryFileW(drop, i, buf, len + 1) == len) {
|
|
wchar_t *target = mp_w32_get_shell_link_target(buf);
|
|
char *fname = mp_to_utf8(files, target ? target : buf);
|
|
files[recvd_files++] = fname;
|
|
talloc_free(target);
|
|
|
|
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, recvd_files, files, action);
|
|
talloc_free(files);
|
|
}
|
|
|
|
ReleaseStgMedium(&medium);
|
|
} else if (SUCCEEDED(IDataObject_GetData(pDataObj, &fmtetc_url, &medium))) {
|
|
wchar_t *wurl = GlobalLock(medium.hGlobal);
|
|
if (wurl) {
|
|
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->last_effect = DROPEFFECT_NONE;
|
|
}
|
|
|
|
*pdwEffect = t->last_effect;
|
|
return S_OK;
|
|
}
|
|
|
|
static IDropTargetVtbl idroptarget_vtbl = {
|
|
.QueryInterface = DropTarget_QueryInterface,
|
|
.AddRef = DropTarget_AddRef,
|
|
.Release = DropTarget_Release,
|
|
.DragEnter = DropTarget_DragEnter,
|
|
.DragOver = DropTarget_DragOver,
|
|
.DragLeave = DropTarget_DragLeave,
|
|
.Drop = DropTarget_Drop,
|
|
};
|
|
|
|
IDropTarget *mp_w32_droptarget_create(struct mp_log *log,
|
|
struct mp_vo_opts *opts,
|
|
struct input_ctx *input_ctx)
|
|
{
|
|
fmtetc_url.cfFormat = RegisterClipboardFormatW(L"UniformResourceLocatorW");
|
|
|
|
struct droptarget *dt = talloc(NULL, struct droptarget);
|
|
dt->iface.lpVtbl = &idroptarget_vtbl;
|
|
atomic_store(&dt->ref_cnt, 0);
|
|
dt->last_effect = 0;
|
|
dt->data_obj = NULL;
|
|
dt->log = mp_log_new(dt, log, "droptarget");
|
|
dt->opts = opts;
|
|
dt->input_ctx = input_ctx;
|
|
|
|
return &dt->iface;
|
|
}
|