summaryrefslogtreecommitdiffstats
path: root/video/out/win32
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/win32')
-rw-r--r--video/out/win32/displayconfig.c140
-rw-r--r--video/out/win32/displayconfig.h27
-rw-r--r--video/out/win32/droptarget.c227
-rw-r--r--video/out/win32/droptarget.h35
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