diff options
Diffstat (limited to 'video/out/win32')
-rw-r--r-- | video/out/win32/displayconfig.c | 140 | ||||
-rw-r--r-- | video/out/win32/displayconfig.h | 27 | ||||
-rw-r--r-- | video/out/win32/droptarget.c | 227 | ||||
-rw-r--r-- | video/out/win32/droptarget.h | 35 |
4 files changed, 429 insertions, 0 deletions
diff --git a/video/out/win32/displayconfig.c b/video/out/win32/displayconfig.c new file mode 100644 index 0000000..9844afd --- /dev/null +++ b/video/out/win32/displayconfig.c @@ -0,0 +1,140 @@ +/* + * 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 <stdbool.h> +#include <string.h> + +#include "displayconfig.h" + +#include "mpv_talloc.h" + +static bool is_valid_refresh_rate(DISPLAYCONFIG_RATIONAL rr) +{ + // DisplayConfig sometimes reports a rate of 1 when the rate is not known + return rr.Denominator != 0 && rr.Numerator / rr.Denominator > 1; +} + +static int get_config(void *ctx, + UINT32 *num_paths, DISPLAYCONFIG_PATH_INFO** paths, + UINT32 *num_modes, DISPLAYCONFIG_MODE_INFO** modes) +{ + LONG res; + *paths = NULL; + *modes = NULL; + + // The display configuration could change between the call to + // GetDisplayConfigBufferSizes and the call to QueryDisplayConfig, so call + // them in a loop until the correct buffer size is chosen + do { + res = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, num_paths, + num_modes); + if (res != ERROR_SUCCESS) + goto fail; + + // Free old buffers if they exist and allocate new ones + talloc_free(*paths); + talloc_free(*modes); + *paths = talloc_array(ctx, DISPLAYCONFIG_PATH_INFO, *num_paths); + *modes = talloc_array(ctx, DISPLAYCONFIG_MODE_INFO, *num_modes); + + res = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, num_paths, *paths, + num_modes, *modes, NULL); + } while (res == ERROR_INSUFFICIENT_BUFFER); + if (res != ERROR_SUCCESS) + goto fail; + + return 0; +fail: + talloc_free(*paths); + talloc_free(*modes); + return -1; +} + +static DISPLAYCONFIG_PATH_INFO *get_path(UINT32 num_paths, + DISPLAYCONFIG_PATH_INFO* paths, + const wchar_t *device) +{ + // Search for a path with a matching device name + for (UINT32 i = 0; i < num_paths; i++) { + // Send a GET_SOURCE_NAME request + DISPLAYCONFIG_SOURCE_DEVICE_NAME source = { + .header = { + .size = sizeof source, + .type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, + .adapterId = paths[i].sourceInfo.adapterId, + .id = paths[i].sourceInfo.id, + } + }; + if (DisplayConfigGetDeviceInfo(&source.header) != ERROR_SUCCESS) + return NULL; + + // Check if the device name matches + if (!wcscmp(device, source.viewGdiDeviceName)) + return &paths[i]; + } + + return NULL; +} + +static double get_refresh_rate_from_mode(DISPLAYCONFIG_MODE_INFO *mode) +{ + if (mode->infoType != DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) + return 0.0; + + DISPLAYCONFIG_VIDEO_SIGNAL_INFO *info = + &mode->targetMode.targetVideoSignalInfo; + if (!is_valid_refresh_rate(info->vSyncFreq)) + return 0.0; + + return ((double)info->vSyncFreq.Numerator) / + ((double)info->vSyncFreq.Denominator); +} + +double mp_w32_displayconfig_get_refresh_rate(const wchar_t *device) +{ + void *ctx = talloc_new(NULL); + double freq = 0.0; + + // Get the current display configuration + UINT32 num_paths; + DISPLAYCONFIG_PATH_INFO* paths; + UINT32 num_modes; + DISPLAYCONFIG_MODE_INFO* modes; + if (get_config(ctx, &num_paths, &paths, &num_modes, &modes)) + goto end; + + // Get the path for the specified monitor + DISPLAYCONFIG_PATH_INFO* path; + if (!(path = get_path(num_paths, paths, device))) + goto end; + + // Try getting the refresh rate from the mode first. The value in the mode + // overrides the value in the path. + if (path->targetInfo.modeInfoIdx != DISPLAYCONFIG_PATH_MODE_IDX_INVALID) + freq = get_refresh_rate_from_mode(&modes[path->targetInfo.modeInfoIdx]); + + // If the mode didn't contain a valid refresh rate, try the path + if (freq == 0.0 && is_valid_refresh_rate(path->targetInfo.refreshRate)) { + freq = ((double)path->targetInfo.refreshRate.Numerator) / + ((double)path->targetInfo.refreshRate.Denominator); + } + +end: + talloc_free(ctx); + return freq; +} diff --git a/video/out/win32/displayconfig.h b/video/out/win32/displayconfig.h new file mode 100644 index 0000000..ee6cd03 --- /dev/null +++ b/video/out/win32/displayconfig.h @@ -0,0 +1,27 @@ +/* + * 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/>. + */ + +#ifndef MP_WIN32_DISPLAYCONFIG_H_ +#define MP_WIN32_DISPLAYCONFIG_H_ + +#include <wchar.h> + +// Given a GDI monitor device name, get the precise refresh rate using the +// Windows 7 DisplayConfig API. Returns 0.0 on failure. +double mp_w32_displayconfig_get_refresh_rate(const wchar_t *device); + +#endif diff --git a/video/out/win32/droptarget.c b/video/out/win32/droptarget.c new file mode 100644 index 0000000..8a33522 --- /dev/null +++ b/video/out/win32/droptarget.c @@ -0,0 +1,227 @@ +/* + * 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 <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) { + char *fname = mp_to_utf8(files, buf); + files[recvd_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, 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; +} diff --git a/video/out/win32/droptarget.h b/video/out/win32/droptarget.h new file mode 100644 index 0000000..1c18c06 --- /dev/null +++ b/video/out/win32/droptarget.h @@ -0,0 +1,35 @@ +/* + * 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/>. + */ + +#ifndef MP_WIN32_DROPTARGET_H_ +#define MP_WIN32_DROPTARGET_H_ + +#include <windows.h> +#include <ole2.h> +#include <shobjidl.h> + +#include "input/input.h" +#include "common/msg.h" +#include "common/common.h" +#include "options/options.h" + +// Create a IDropTarget implementation that sends dropped files to input_ctx +IDropTarget *mp_w32_droptarget_create(struct mp_log *log, + struct mp_vo_opts *opts, + struct input_ctx *input_ctx); + +#endif |