mirror of
https://github.com/mpv-player/mpv
synced 2024-12-11 17:37:23 +00:00
e0b517985b
When a shell link is dropped onto the mpv window, the file name will be replaced by the file name of its target so that the linked file is played.
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;
|
|
}
|