2009-02-08 03:27:30 +00:00
|
|
|
/*
|
2015-04-13 07:36:54 +00:00
|
|
|
* This file is part of mpv.
|
2009-02-08 03:27:30 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or modify
|
2009-02-08 03:27:30 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2009-02-08 03:27:30 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
2015-04-13 07:36:54 +00:00
|
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2009-02-08 03:27:30 +00:00
|
|
|
*/
|
|
|
|
|
2007-03-04 13:20:51 +00:00
|
|
|
#include <stdio.h>
|
2003-09-19 14:33:51 +00:00
|
|
|
#include <limits.h>
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
#include <pthread.h>
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
#include <assert.h>
|
2003-09-19 14:33:51 +00:00
|
|
|
#include <windows.h>
|
2007-03-04 13:20:51 +00:00
|
|
|
#include <windowsx.h>
|
2014-01-06 04:21:54 +00:00
|
|
|
#include <ole2.h>
|
2014-12-08 06:06:14 +00:00
|
|
|
#include <shobjidl.h>
|
2015-11-08 18:31:09 +00:00
|
|
|
#include <avrt.h>
|
2003-09-19 14:33:51 +00:00
|
|
|
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/options.h"
|
2013-12-17 00:23:09 +00:00
|
|
|
#include "input/keycodes.h"
|
|
|
|
#include "input/input.h"
|
2014-01-06 04:21:54 +00:00
|
|
|
#include "input/event.h"
|
2015-11-26 11:11:45 +00:00
|
|
|
#include "stream/stream.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
|
|
|
#include "common/common.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "vo.h"
|
2014-05-06 21:01:19 +00:00
|
|
|
#include "win_state.h"
|
2003-09-19 14:33:51 +00:00
|
|
|
#include "w32_common.h"
|
2015-02-03 04:25:50 +00:00
|
|
|
#include "win32/displayconfig.h"
|
2011-12-07 00:36:19 +00:00
|
|
|
#include "osdep/io.h"
|
2014-10-19 21:32:34 +00:00
|
|
|
#include "osdep/threads.h"
|
2014-01-19 13:20:02 +00:00
|
|
|
#include "osdep/w32_keyboard.h"
|
2016-09-07 09:26:25 +00:00
|
|
|
#include "osdep/atomic.h"
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
#include "misc/dispatch.h"
|
|
|
|
#include "misc/rendezvous.h"
|
2016-01-11 18:03:40 +00:00
|
|
|
#include "mpv_talloc.h"
|
2003-09-19 14:33:51 +00:00
|
|
|
|
2016-06-10 12:37:29 +00:00
|
|
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
|
|
|
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
|
|
|
|
|
2014-07-26 18:30:52 +00:00
|
|
|
static __thread struct vo_w32_state *w32_thread_context;
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
struct vo_w32_state {
|
|
|
|
struct mp_log *log;
|
|
|
|
struct vo *vo;
|
|
|
|
struct mp_vo_opts *opts;
|
|
|
|
struct input_ctx *input_ctx;
|
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
pthread_t thread;
|
|
|
|
bool terminate;
|
|
|
|
struct mp_dispatch_queue *dispatch; // used to run stuff on the GUI thread
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
HWND window;
|
2014-12-09 20:28:35 +00:00
|
|
|
HWND parent; // 0 normally, set in embedding mode
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
HHOOK parent_win_hook;
|
|
|
|
HWINEVENTHOOK parent_evt_hook;
|
2014-07-26 18:27:03 +00:00
|
|
|
|
2015-02-03 04:25:50 +00:00
|
|
|
HMONITOR monitor; // Handle of the current screen
|
|
|
|
struct mp_rect screenrc; // Size and virtual position of the current screen
|
2015-11-26 11:11:45 +00:00
|
|
|
char *color_profile; // Path of the current screen's color profile
|
2014-07-26 18:27:03 +00:00
|
|
|
|
|
|
|
// last non-fullscreen extends (updated only on fullscreen or on initialization)
|
|
|
|
int prev_width;
|
|
|
|
int prev_height;
|
|
|
|
int prev_x;
|
|
|
|
int prev_y;
|
|
|
|
|
2014-09-30 02:21:35 +00:00
|
|
|
// Has the window seen a WM_DESTROY? If so, don't call DestroyWindow again.
|
|
|
|
bool destroyed;
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
// whether the window position and size were intialized
|
|
|
|
bool window_bounds_initialized;
|
|
|
|
|
|
|
|
bool current_fs;
|
|
|
|
|
|
|
|
// currently known window state
|
|
|
|
int window_x;
|
|
|
|
int window_y;
|
|
|
|
int dw;
|
|
|
|
int dh;
|
|
|
|
|
|
|
|
// video size
|
|
|
|
uint32_t o_dwidth;
|
|
|
|
uint32_t o_dheight;
|
|
|
|
|
|
|
|
bool disable_screensaver;
|
|
|
|
bool cursor_visible;
|
2016-08-05 14:01:26 +00:00
|
|
|
atomic_uint event_flags;
|
2014-07-26 18:27:03 +00:00
|
|
|
|
|
|
|
BOOL tracking;
|
|
|
|
TRACKMOUSEEVENT trackEvent;
|
|
|
|
|
|
|
|
int mouse_x;
|
|
|
|
int mouse_y;
|
|
|
|
|
2015-03-13 08:46:54 +00:00
|
|
|
// Should SetCursor be called when handling VOCTRL_SET_CURSOR_VISIBILITY?
|
|
|
|
bool can_set_cursor;
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
// UTF-16 decoding state for WM_CHAR and VK_PACKET
|
|
|
|
int high_surrogate;
|
2014-12-08 06:06:14 +00:00
|
|
|
|
|
|
|
ITaskbarList2 *taskbar_list;
|
2015-11-15 22:03:48 +00:00
|
|
|
ITaskbarList3 *taskbar_list3;
|
|
|
|
UINT tbtnCreatedMsg;
|
|
|
|
bool tbtnCreated;
|
2015-03-10 18:25:30 +00:00
|
|
|
|
2016-09-02 16:08:49 +00:00
|
|
|
struct voctrl_playback_state current_pstate;
|
|
|
|
|
2015-03-10 18:25:30 +00:00
|
|
|
// updates on move/resize/displaychange
|
|
|
|
double display_fps;
|
2015-11-08 18:31:09 +00:00
|
|
|
|
|
|
|
HANDLE avrt_handle;
|
2014-07-26 18:27:03 +00:00
|
|
|
};
|
|
|
|
|
2014-01-06 04:21:54 +00:00
|
|
|
typedef struct tagDropTarget {
|
|
|
|
IDropTarget iface;
|
2015-06-14 15:51:59 +00:00
|
|
|
atomic_int refCnt;
|
2014-01-06 04:21:54 +00:00
|
|
|
DWORD lastEffect;
|
|
|
|
IDataObject* dataObj;
|
2014-07-26 18:27:03 +00:00
|
|
|
struct vo_w32_state *w32;
|
2014-01-06 04:21:54 +00:00
|
|
|
} 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;
|
2015-06-14 15:51:59 +00:00
|
|
|
return atomic_fetch_add(&t->refCnt, 1) + 1;
|
2014-01-06 04:21:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG STDMETHODCALLTYPE DropTarget_Release(IDropTarget* This)
|
|
|
|
{
|
|
|
|
DropTarget* t = (DropTarget*)This;
|
2015-06-14 15:51:59 +00:00
|
|
|
ULONG cRef = atomic_fetch_add(&t->refCnt, -1) - 1;
|
2014-01-06 04:21:54 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-30 12:55:16 +00:00
|
|
|
enum mp_dnd_action action = (grfKeyState & MK_SHIFT) ? DND_APPEND : DND_REPLACE;
|
|
|
|
|
2014-01-06 04:21:54 +00:00
|
|
|
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);
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t* buf = talloc_array(NULL, wchar_t, len + 1);
|
2014-01-06 04:21:54 +00:00
|
|
|
|
|
|
|
if (DragQueryFileW(hDrop, i, buf, len + 1) == len) {
|
|
|
|
char* fname = mp_to_utf8(files, buf);
|
|
|
|
files[nrecvd_files++] = fname;
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_VERBOSE(t->w32, "received dropped file: %s\n",
|
2014-01-06 04:21:54 +00:00
|
|
|
fname);
|
|
|
|
} else {
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_ERR(t->w32, "error getting dropped file name\n");
|
2014-01-06 04:21:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
talloc_free(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
2015-08-29 07:42:04 +00:00
|
|
|
mp_event_drop_files(t->w32->input_ctx, nrecvd_files, files,
|
2015-08-30 12:55:16 +00:00
|
|
|
action);
|
2014-01-06 04:21:54 +00:00
|
|
|
|
|
|
|
talloc_free(files);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
} else if (pDataObj->lpVtbl->GetData(pDataObj,
|
|
|
|
&fmtetc_url, &medium) == S_OK) {
|
|
|
|
// get the URL encoded in US-ASCII
|
2016-02-02 20:43:16 +00:00
|
|
|
wchar_t* wurl = GlobalLock(medium.hGlobal);
|
|
|
|
if (wurl != NULL) {
|
|
|
|
char *url = mp_to_utf8(NULL, wurl);
|
2014-07-26 18:27:03 +00:00
|
|
|
if (mp_event_drop_mime_data(t->w32->input_ctx, "text/uri-list",
|
2015-08-30 12:55:16 +00:00
|
|
|
bstr0(url), action) > 0) {
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_VERBOSE(t->w32, "received dropped URL: %s\n", url);
|
2014-01-06 04:21:54 +00:00
|
|
|
} else {
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_ERR(t->w32, "error getting dropped URL\n");
|
2014-01-06 04:21:54 +00:00
|
|
|
}
|
|
|
|
|
2016-02-02 20:43:16 +00:00
|
|
|
talloc_free(url);
|
2014-01-06 04:21:54 +00:00
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
t->lastEffect = DROPEFFECT_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDataObj->lpVtbl->Release(pDataObj);
|
|
|
|
*pdwEffect = t->lastEffect;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
static void DropTarget_Init(DropTarget* This, struct vo_w32_state *w32)
|
2014-01-06 04:21:54 +00:00
|
|
|
{
|
|
|
|
IDropTargetVtbl* vtbl = talloc(This, IDropTargetVtbl);
|
|
|
|
*vtbl = (IDropTargetVtbl){
|
|
|
|
DropTarget_QueryInterface, DropTarget_AddRef, DropTarget_Release,
|
|
|
|
DropTarget_DragEnter, DropTarget_DragOver, DropTarget_DragLeave,
|
|
|
|
DropTarget_Drop
|
|
|
|
};
|
|
|
|
|
|
|
|
This->iface.lpVtbl = vtbl;
|
2015-06-14 15:51:59 +00:00
|
|
|
atomic_store(&This->refCnt, 0);
|
2014-01-06 04:21:54 +00:00
|
|
|
This->lastEffect = 0;
|
|
|
|
This->dataObj = NULL;
|
2014-07-26 18:27:03 +00:00
|
|
|
This->w32 = w32;
|
2014-01-06 04:21:54 +00:00
|
|
|
}
|
|
|
|
|
2012-04-14 11:39:53 +00:00
|
|
|
static void add_window_borders(HWND hwnd, RECT *rc)
|
2011-10-28 13:19:51 +00:00
|
|
|
{
|
2016-12-10 21:50:11 +00:00
|
|
|
AdjustWindowRect(rc, GetWindowLongPtrW(hwnd, GWL_STYLE), 0);
|
2011-10-28 13:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// basically a reverse AdjustWindowRect (win32 doesn't appear to have this)
|
2012-04-14 11:39:53 +00:00
|
|
|
static void subtract_window_borders(HWND hwnd, RECT *rc)
|
2011-10-28 13:19:51 +00:00
|
|
|
{
|
|
|
|
RECT b = { 0, 0, 0, 0 };
|
2012-04-14 11:39:53 +00:00
|
|
|
add_window_borders(hwnd, &b);
|
2011-10-28 13:19:51 +00:00
|
|
|
rc->left -= b.left;
|
|
|
|
rc->top -= b.top;
|
|
|
|
rc->right -= b.right;
|
|
|
|
rc->bottom -= b.bottom;
|
|
|
|
}
|
|
|
|
|
2014-12-29 13:52:34 +00:00
|
|
|
static LRESULT borderless_nchittest(struct vo_w32_state *w32, int x, int y)
|
|
|
|
{
|
2015-03-09 11:15:01 +00:00
|
|
|
if (IsMaximized(w32->window))
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTCLIENT;
|
|
|
|
|
|
|
|
POINT mouse = { x, y };
|
|
|
|
ScreenToClient(w32->window, &mouse);
|
|
|
|
|
2016-07-03 06:56:52 +00:00
|
|
|
// The horizontal frame should be the same size as the vertical frame,
|
|
|
|
// since the NONCLIENTMETRICS structure does not distinguish between them
|
|
|
|
int frame_size = GetSystemMetrics(SM_CXFRAME) +
|
|
|
|
GetSystemMetrics(SM_CXPADDEDBORDER);
|
2014-12-29 13:52:34 +00:00
|
|
|
// The diagonal size handles are slightly wider than the side borders
|
2016-07-03 06:56:52 +00:00
|
|
|
int diagonal_width = frame_size * 2 + GetSystemMetrics(SM_CXBORDER);
|
2014-12-29 13:52:34 +00:00
|
|
|
|
|
|
|
// Hit-test top border
|
2016-07-03 06:56:52 +00:00
|
|
|
if (mouse.y < frame_size) {
|
|
|
|
if (mouse.x < diagonal_width)
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTTOPLEFT;
|
2016-07-03 06:56:52 +00:00
|
|
|
if (mouse.x >= w32->dw - diagonal_width)
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTTOPRIGHT;
|
|
|
|
return HTTOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hit-test bottom border
|
2016-07-03 06:56:52 +00:00
|
|
|
if (mouse.y >= w32->dh - frame_size) {
|
|
|
|
if (mouse.x < diagonal_width)
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTBOTTOMLEFT;
|
2016-07-03 06:56:52 +00:00
|
|
|
if (mouse.x >= w32->dw - diagonal_width)
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTBOTTOMRIGHT;
|
|
|
|
return HTBOTTOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hit-test side borders
|
2016-07-03 06:56:52 +00:00
|
|
|
if (mouse.x < frame_size)
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTLEFT;
|
2016-07-03 06:56:52 +00:00
|
|
|
if (mouse.x >= w32->dw - frame_size)
|
2014-12-29 13:52:34 +00:00
|
|
|
return HTRIGHT;
|
|
|
|
return HTCLIENT;
|
|
|
|
}
|
|
|
|
|
2011-10-28 13:19:51 +00:00
|
|
|
// turn a WMSZ_* input value in v into the border that should be resized
|
|
|
|
// returns: 0=left, 1=top, 2=right, 3=bottom, -1=undefined
|
2014-01-06 11:26:42 +00:00
|
|
|
static int get_resize_border(int v)
|
|
|
|
{
|
2011-10-28 13:19:51 +00:00
|
|
|
switch (v) {
|
|
|
|
case WMSZ_LEFT: return 3;
|
|
|
|
case WMSZ_TOP: return 2;
|
|
|
|
case WMSZ_RIGHT: return 3;
|
|
|
|
case WMSZ_BOTTOM: return 2;
|
|
|
|
case WMSZ_TOPLEFT: return 1;
|
|
|
|
case WMSZ_TOPRIGHT: return 1;
|
|
|
|
case WMSZ_BOTTOMLEFT: return 3;
|
|
|
|
case WMSZ_BOTTOMRIGHT: return 3;
|
|
|
|
default: return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
static bool key_state(int vk)
|
2012-04-14 11:27:34 +00:00
|
|
|
{
|
|
|
|
return GetKeyState(vk) & 0x8000;
|
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
static int mod_state(struct vo_w32_state *w32)
|
2012-04-06 21:14:21 +00:00
|
|
|
{
|
|
|
|
int res = 0;
|
2014-04-17 15:25:45 +00:00
|
|
|
|
|
|
|
// AltGr is represented as LCONTROL+RMENU on Windows
|
2014-07-26 18:27:03 +00:00
|
|
|
bool alt_gr = mp_input_use_alt_gr(w32->input_ctx) &&
|
|
|
|
key_state(VK_RMENU) && key_state(VK_LCONTROL);
|
2014-04-17 15:25:45 +00:00
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
if (key_state(VK_RCONTROL) || (key_state(VK_LCONTROL) && !alt_gr))
|
2013-02-12 00:34:04 +00:00
|
|
|
res |= MP_KEY_MODIFIER_CTRL;
|
2014-07-26 18:27:03 +00:00
|
|
|
if (key_state(VK_SHIFT))
|
2013-02-12 00:34:04 +00:00
|
|
|
res |= MP_KEY_MODIFIER_SHIFT;
|
2014-07-26 18:27:03 +00:00
|
|
|
if (key_state(VK_LMENU) || (key_state(VK_RMENU) && !alt_gr))
|
2013-02-12 00:34:04 +00:00
|
|
|
res |= MP_KEY_MODIFIER_ALT;
|
2012-04-06 21:14:21 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-08-01 18:51:52 +00:00
|
|
|
static int decode_surrogate_pair(wchar_t lead, wchar_t trail)
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
2015-03-04 10:43:02 +00:00
|
|
|
return 0x10000 + (((lead & 0x3ff) << 10) | (trail & 0x3ff));
|
2014-04-17 15:25:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-01 18:51:52 +00:00
|
|
|
static int decode_utf16(struct vo_w32_state *w32, wchar_t c)
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
|
|
|
// Decode UTF-16, keeping state in w32->high_surrogate
|
|
|
|
if (IS_HIGH_SURROGATE(c)) {
|
|
|
|
w32->high_surrogate = c;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (IS_LOW_SURROGATE(c)) {
|
|
|
|
if (!w32->high_surrogate) {
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_ERR(w32, "Invalid UTF-16 input\n");
|
2014-04-17 15:25:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int codepoint = decode_surrogate_pair(w32->high_surrogate, c);
|
|
|
|
w32->high_surrogate = 0;
|
|
|
|
return codepoint;
|
|
|
|
}
|
|
|
|
if (w32->high_surrogate != 0) {
|
|
|
|
w32->high_surrogate = 0;
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_ERR(w32, "Invalid UTF-16 input\n");
|
2014-04-17 15:25:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_keyboard_buffer(void)
|
|
|
|
{
|
|
|
|
static const UINT vkey = VK_DECIMAL;
|
|
|
|
static const BYTE keys[256] = { 0 };
|
|
|
|
UINT scancode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC);
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t buf[10];
|
2014-04-17 15:25:45 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
// Use the method suggested by Michael Kaplan to clear any pending dead
|
|
|
|
// keys from the current keyboard layout. See:
|
|
|
|
// https://web.archive.org/web/20101004154432/http://blogs.msdn.com/b/michkap/archive/2006/04/06/569632.aspx
|
|
|
|
// https://web.archive.org/web/20100820152419/http://blogs.msdn.com/b/michkap/archive/2007/10/27/5717859.aspx
|
|
|
|
do {
|
|
|
|
ret = ToUnicode(vkey, scancode, keys, buf, MP_ARRAY_SIZE(buf), 0);
|
|
|
|
} while (ret < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int to_unicode(UINT vkey, UINT scancode, const BYTE keys[256])
|
|
|
|
{
|
|
|
|
// This wraps ToUnicode to be stateless and to return only one character
|
|
|
|
|
|
|
|
// Make the buffer 10 code units long to be safe, same as here:
|
|
|
|
// https://web.archive.org/web/20101013215215/http://blogs.msdn.com/b/michkap/archive/2006/03/24/559169.aspx
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t buf[10] = { 0 };
|
2014-04-17 15:25:45 +00:00
|
|
|
|
|
|
|
// Dead keys aren't useful for key shortcuts, so clear the keyboard state
|
|
|
|
clear_keyboard_buffer();
|
|
|
|
|
|
|
|
int len = ToUnicode(vkey, scancode, keys, buf, MP_ARRAY_SIZE(buf), 0);
|
|
|
|
|
|
|
|
// Return the last complete UTF-16 code point. A negative return value
|
|
|
|
// indicates a dead key, however there should still be a non-combining
|
|
|
|
// version of the key in the buffer.
|
|
|
|
if (len < 0)
|
|
|
|
len = -len;
|
|
|
|
if (len >= 2 && IS_SURROGATE_PAIR(buf[len - 2], buf[len - 1]))
|
|
|
|
return decode_surrogate_pair(buf[len - 2], buf[len - 1]);
|
|
|
|
if (len >= 1)
|
|
|
|
return buf[len - 1];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
static int decode_key(struct vo_w32_state *w32, UINT vkey, UINT scancode)
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
|
|
|
BYTE keys[256];
|
|
|
|
GetKeyboardState(keys);
|
|
|
|
|
|
|
|
// If mp_input_use_alt_gr is false, detect and remove AltGr so normal
|
|
|
|
// characters are generated. Note that AltGr is represented as
|
|
|
|
// LCONTROL+RMENU on Windows.
|
|
|
|
if ((keys[VK_RMENU] & 0x80) && (keys[VK_LCONTROL] & 0x80) &&
|
2014-07-26 18:27:03 +00:00
|
|
|
!mp_input_use_alt_gr(w32->input_ctx))
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
|
|
|
keys[VK_RMENU] = keys[VK_LCONTROL] = 0;
|
|
|
|
keys[VK_MENU] = keys[VK_LMENU];
|
|
|
|
keys[VK_CONTROL] = keys[VK_RCONTROL];
|
|
|
|
}
|
|
|
|
|
|
|
|
int c = to_unicode(vkey, scancode, keys);
|
|
|
|
|
|
|
|
// Some shift states prevent ToUnicode from working or cause it to produce
|
|
|
|
// control characters. If this is detected, remove modifiers until it
|
|
|
|
// starts producing normal characters.
|
|
|
|
if (c < 0x20 && (keys[VK_MENU] & 0x80)) {
|
|
|
|
keys[VK_LMENU] = keys[VK_RMENU] = keys[VK_MENU] = 0;
|
|
|
|
c = to_unicode(vkey, scancode, keys);
|
|
|
|
}
|
|
|
|
if (c < 0x20 && (keys[VK_CONTROL] & 0x80)) {
|
|
|
|
keys[VK_LCONTROL] = keys[VK_RCONTROL] = keys[VK_CONTROL] = 0;
|
|
|
|
c = to_unicode(vkey, scancode, keys);
|
|
|
|
}
|
|
|
|
if (c < 0x20)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Decode lone UTF-16 surrogates (VK_PACKET can generate these)
|
|
|
|
if (c < 0x10000)
|
2014-07-26 18:27:03 +00:00
|
|
|
return decode_utf16(w32, c);
|
2014-04-17 15:25:45 +00:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2014-11-08 11:14:29 +00:00
|
|
|
static void handle_key_down(struct vo_w32_state *w32, UINT vkey, UINT scancode)
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
|
|
|
// Ignore key repeat
|
|
|
|
if (scancode & KF_REPEAT)
|
2014-11-08 11:14:29 +00:00
|
|
|
return;
|
2014-04-17 15:25:45 +00:00
|
|
|
|
|
|
|
int mpkey = mp_w32_vkey_to_mpkey(vkey, scancode & KF_EXTENDED);
|
|
|
|
if (!mpkey) {
|
2014-07-26 18:27:03 +00:00
|
|
|
mpkey = decode_key(w32, vkey, scancode & (0xff | KF_EXTENDED));
|
2014-04-17 15:25:45 +00:00
|
|
|
if (!mpkey)
|
2014-11-08 11:14:29 +00:00
|
|
|
return;
|
2014-04-17 15:25:45 +00:00
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, mpkey | mod_state(w32) | MP_KEY_STATE_DOWN);
|
2014-04-17 15:25:45 +00:00
|
|
|
}
|
|
|
|
|
2014-11-08 11:14:29 +00:00
|
|
|
static void handle_key_up(struct vo_w32_state *w32, UINT vkey, UINT scancode)
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
|
|
|
switch (vkey) {
|
|
|
|
case VK_MENU:
|
|
|
|
case VK_CONTROL:
|
|
|
|
case VK_SHIFT:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Releasing all keys on key-up is simpler and ensures no keys can be
|
|
|
|
// get "stuck." This matches the behaviour of other VOs.
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, MP_INPUT_RELEASE_ALL);
|
2014-04-17 15:25:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-01 18:51:52 +00:00
|
|
|
static bool handle_char(struct vo_w32_state *w32, wchar_t wc)
|
2014-04-17 15:25:45 +00:00
|
|
|
{
|
2014-07-26 18:27:03 +00:00
|
|
|
int c = decode_utf16(w32, wc);
|
2014-04-17 15:25:45 +00:00
|
|
|
|
|
|
|
if (c == 0)
|
|
|
|
return true;
|
|
|
|
if (c < 0x20)
|
|
|
|
return false;
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, c | mod_state(w32));
|
2014-04-17 15:25:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
static void signal_events(struct vo_w32_state *w32, int events)
|
|
|
|
{
|
2016-08-05 14:01:26 +00:00
|
|
|
atomic_fetch_or(&w32->event_flags, events);
|
video: move display and timing to a separate thread
The VO is run inside its own thread. It also does most of video timing.
The playloop hands the image data and a realtime timestamp to the VO,
and the VO does the rest.
In particular, this allows the playloop to do other things, instead of
blocking for video redraw. But if anything accesses the VO during video
timing, it will block.
This also fixes vo_sdl.c event handling; but that is only a side-effect,
since reimplementing the broken way would require more effort.
Also drop --softsleep. In theory, this option helps if the kernel's
sleeping mechanism is too inaccurate for video timing. In practice, I
haven't ever encountered a situation where it helps, and it just burns
CPU cycles. On the other hand it's probably actively harmful, because
it prevents the libavcodec decoder threads from doing real work.
Side note:
Originally, I intended that multiple frames can be queued to the VO. But
this is not done, due to problems with OSD and other certain features.
OSD in particular is simply designed in a way that it can be neither
timed nor copied, so you do have to render it into the video frame
before you can draw the next frame. (Subtitles have no such restriction.
sd_lavc was even updated to fix this.) It seems the right solution to
queuing multiple VO frames is rendering on VO-backed framebuffers, like
vo_vdpau.c does. This requires VO driver support, and is out of scope
of this commit.
As consequence, the VO has a queue size of 1. The existing video queue
is just needed to compute frame duration, and will be moved out in the
next commit.
2014-08-12 21:02:08 +00:00
|
|
|
vo_wakeup(w32->vo);
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wakeup_gui_thread(void *ctx)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = ctx;
|
|
|
|
PostMessage(w32->window, WM_USER, 0, 0);
|
|
|
|
}
|
|
|
|
|
2015-02-03 04:25:50 +00:00
|
|
|
static double get_refresh_rate_from_gdi(const wchar_t *device)
|
2015-03-10 18:25:30 +00:00
|
|
|
{
|
2015-02-03 04:25:50 +00:00
|
|
|
DEVMODEW dm = { .dmSize = sizeof dm };
|
|
|
|
if (!EnumDisplaySettingsW(device, ENUM_CURRENT_SETTINGS, &dm))
|
|
|
|
return 0.0;
|
2015-03-10 18:25:30 +00:00
|
|
|
|
|
|
|
// May return 0 or 1 which "represent the display hardware's default refresh rate"
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565%28v=vs.85%29.aspx
|
|
|
|
// mpv validates this value with a threshold of 1, so don't return exactly 1
|
|
|
|
if (dm.dmDisplayFrequency == 1)
|
2015-02-03 04:25:50 +00:00
|
|
|
return 0.0;
|
2015-03-10 18:25:30 +00:00
|
|
|
|
|
|
|
// dm.dmDisplayFrequency is an integer which is rounded down, so it's
|
|
|
|
// highly likely that 23 represents 24/1.001, 59 represents 60/1.001, etc.
|
|
|
|
// A caller can always reproduce the original value by using floor.
|
|
|
|
double rv = dm.dmDisplayFrequency;
|
|
|
|
switch (dm.dmDisplayFrequency) {
|
|
|
|
case 23:
|
|
|
|
case 29:
|
2015-03-21 05:47:59 +00:00
|
|
|
case 47:
|
2015-03-10 18:25:30 +00:00
|
|
|
case 59:
|
|
|
|
case 71:
|
2015-03-21 05:47:59 +00:00
|
|
|
case 89:
|
|
|
|
case 95:
|
2015-03-10 18:25:30 +00:00
|
|
|
case 119:
|
2015-03-21 05:47:59 +00:00
|
|
|
case 143:
|
2015-03-10 18:25:30 +00:00
|
|
|
rv = (rv + 1) / 1.001;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2015-11-26 11:11:45 +00:00
|
|
|
static char *get_color_profile(void *ctx, const wchar_t *device)
|
|
|
|
{
|
|
|
|
char *name = NULL;
|
|
|
|
|
|
|
|
HDC ic = CreateICW(device, NULL, NULL, NULL);
|
|
|
|
if (!ic)
|
|
|
|
goto done;
|
|
|
|
wchar_t wname[MAX_PATH + 1];
|
|
|
|
if (!GetICMProfileW(ic, &(DWORD){ MAX_PATH }, wname))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
name = mp_to_utf8(ctx, wname);
|
|
|
|
done:
|
|
|
|
if (ic)
|
|
|
|
DeleteDC(ic);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_display_info(struct vo_w32_state *w32)
|
2015-03-10 18:25:30 +00:00
|
|
|
{
|
2015-02-03 04:25:50 +00:00
|
|
|
HMONITOR monitor = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
if (w32->monitor == monitor)
|
|
|
|
return;
|
|
|
|
w32->monitor = monitor;
|
|
|
|
|
|
|
|
MONITORINFOEXW mi = { .cbSize = sizeof mi };
|
|
|
|
GetMonitorInfoW(monitor, (MONITORINFO*)&mi);
|
|
|
|
|
|
|
|
// Try to get the monitor refresh rate.
|
|
|
|
double freq = 0.0;
|
|
|
|
|
|
|
|
if (freq == 0.0)
|
|
|
|
freq = mp_w32_displayconfig_get_refresh_rate(mi.szDevice);
|
|
|
|
if (freq == 0.0)
|
|
|
|
freq = get_refresh_rate_from_gdi(mi.szDevice);
|
|
|
|
|
|
|
|
if (freq != w32->display_fps) {
|
|
|
|
MP_VERBOSE(w32, "display-fps: %f\n", freq);
|
|
|
|
if (freq == 0.0)
|
|
|
|
MP_WARN(w32, "Couldn't determine monitor refresh rate\n");
|
|
|
|
w32->display_fps = freq;
|
2015-03-10 18:25:30 +00:00
|
|
|
signal_events(w32, VO_EVENT_WIN_STATE);
|
|
|
|
}
|
2015-11-26 11:11:45 +00:00
|
|
|
|
|
|
|
char *color_profile = get_color_profile(w32, mi.szDevice);
|
|
|
|
if ((color_profile == NULL) != (w32->color_profile == NULL) ||
|
|
|
|
(color_profile && strcmp(color_profile, w32->color_profile)))
|
|
|
|
{
|
|
|
|
if (color_profile)
|
|
|
|
MP_VERBOSE(w32, "color-profile: %s\n", color_profile);
|
|
|
|
talloc_free(w32->color_profile);
|
|
|
|
w32->color_profile = color_profile;
|
|
|
|
color_profile = NULL;
|
|
|
|
signal_events(w32, VO_EVENT_ICC_PROFILE_CHANGED);
|
|
|
|
}
|
|
|
|
|
|
|
|
talloc_free(color_profile);
|
2015-03-10 18:25:30 +00:00
|
|
|
}
|
|
|
|
|
2015-11-26 11:11:45 +00:00
|
|
|
static void force_update_display_info(struct vo_w32_state *w32)
|
2015-02-03 04:25:50 +00:00
|
|
|
{
|
|
|
|
w32->monitor = 0;
|
2015-11-26 11:11:45 +00:00
|
|
|
update_display_info(w32);
|
2015-02-03 04:25:50 +00:00
|
|
|
}
|
|
|
|
|
2016-09-02 16:08:49 +00:00
|
|
|
static void update_playback_state(struct vo_w32_state *w32)
|
|
|
|
{
|
|
|
|
struct voctrl_playback_state *pstate = &w32->current_pstate;
|
|
|
|
|
|
|
|
if (!w32->taskbar_list3 || !w32->tbtnCreated)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!pstate->playing || !pstate->taskbar_progress) {
|
|
|
|
ITaskbarList3_SetProgressState(w32->taskbar_list3, w32->window,
|
|
|
|
TBPF_NOPROGRESS);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ITaskbarList3_SetProgressValue(w32->taskbar_list3, w32->window,
|
|
|
|
pstate->percent_pos, 100);
|
|
|
|
ITaskbarList3_SetProgressState(w32->taskbar_list3, w32->window,
|
|
|
|
pstate->paused ? TBPF_PAUSED :
|
|
|
|
TBPF_NORMAL);
|
|
|
|
}
|
|
|
|
|
2012-04-14 11:39:53 +00:00
|
|
|
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
|
|
|
LPARAM lParam)
|
|
|
|
{
|
2014-07-26 18:30:52 +00:00
|
|
|
assert(w32_thread_context);
|
|
|
|
struct vo_w32_state *w32 = w32_thread_context;
|
2014-08-06 18:00:26 +00:00
|
|
|
if (!w32->window)
|
|
|
|
w32->window = hWnd; // can happen during CreateWindow*!
|
|
|
|
assert(w32->window == hWnd);
|
2013-06-20 21:39:53 +00:00
|
|
|
int mouse_button = 0;
|
2012-04-14 11:39:53 +00:00
|
|
|
|
2003-09-19 14:33:51 +00:00
|
|
|
switch (message) {
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
case WM_USER:
|
|
|
|
// This message is used to wakeup the GUI thread, see wakeup_gui_thread.
|
|
|
|
mp_dispatch_queue_process(w32->dispatch, 0);
|
|
|
|
break;
|
2014-08-06 18:00:26 +00:00
|
|
|
case WM_ERASEBKGND: // no need to erase background separately
|
2014-01-06 14:35:27 +00:00
|
|
|
return 1;
|
|
|
|
case WM_PAINT:
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
signal_events(w32, VO_EVENT_EXPOSE);
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
|
|
|
case WM_MOVE: {
|
|
|
|
POINT p = {0};
|
|
|
|
ClientToScreen(w32->window, &p);
|
|
|
|
w32->window_x = p.x;
|
|
|
|
w32->window_y = p.y;
|
2016-09-29 13:21:23 +00:00
|
|
|
|
|
|
|
// Window may intersect with new monitors (see VOCTRL_GET_DISPLAY_NAMES)
|
|
|
|
signal_events(w32, VO_EVENT_WIN_STATE);
|
|
|
|
|
2015-11-26 11:11:45 +00:00
|
|
|
update_display_info(w32); // if we moved between monitors
|
2016-12-12 16:39:00 +00:00
|
|
|
MP_DBG(w32, "move window: %d:%d\n", w32->window_x, w32->window_y);
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WM_SIZE: {
|
|
|
|
RECT r;
|
2015-02-02 21:52:13 +00:00
|
|
|
if (GetClientRect(w32->window, &r) && r.right > 0 && r.bottom > 0) {
|
|
|
|
w32->dw = r.right;
|
|
|
|
w32->dh = r.bottom;
|
|
|
|
signal_events(w32, VO_EVENT_RESIZE);
|
|
|
|
MP_VERBOSE(w32, "resize window: %d:%d\n", w32->dw, w32->dh);
|
|
|
|
}
|
2015-03-09 09:30:33 +00:00
|
|
|
|
|
|
|
// Window may have been minimized or restored
|
|
|
|
signal_events(w32, VO_EVENT_WIN_STATE);
|
2015-02-03 04:25:50 +00:00
|
|
|
|
2015-11-26 11:11:45 +00:00
|
|
|
update_display_info(w32);
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WM_SIZING:
|
2014-10-04 20:17:36 +00:00
|
|
|
if (w32->opts->keepaspect && w32->opts->keepaspect_window &&
|
2015-06-20 11:33:23 +00:00
|
|
|
!w32->current_fs && !w32->parent)
|
2014-01-06 14:35:27 +00:00
|
|
|
{
|
|
|
|
RECT *rc = (RECT*)lParam;
|
|
|
|
// get client area of the windows if it had the rect rc
|
|
|
|
// (subtracting the window borders)
|
|
|
|
RECT r = *rc;
|
|
|
|
subtract_window_borders(w32->window, &r);
|
|
|
|
int c_w = r.right - r.left, c_h = r.bottom - r.top;
|
|
|
|
float aspect = w32->o_dwidth / (float) MPMAX(w32->o_dheight, 1);
|
|
|
|
int d_w = c_h * aspect - c_w;
|
|
|
|
int d_h = c_w / aspect - c_h;
|
|
|
|
int d_corners[4] = { d_w, d_h, -d_w, -d_h };
|
|
|
|
int corners[4] = { rc->left, rc->top, rc->right, rc->bottom };
|
|
|
|
int corner = get_resize_border(wParam);
|
|
|
|
if (corner >= 0)
|
|
|
|
corners[corner] -= d_corners[corner];
|
|
|
|
*rc = (RECT) { corners[0], corners[1], corners[2], corners[3] };
|
|
|
|
return TRUE;
|
2012-04-06 21:14:21 +00:00
|
|
|
}
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
|
|
|
case WM_CLOSE:
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
// Don't actually allow it to destroy the window, or whatever else it
|
|
|
|
// is that will make us lose WM_USER wakeups.
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
|
2014-09-30 02:21:35 +00:00
|
|
|
return 0;
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
case WM_NCDESTROY: // Sometimes only WM_NCDESTROY is received in --wid mode
|
2014-09-30 02:21:35 +00:00
|
|
|
case WM_DESTROY:
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
if (w32->destroyed)
|
|
|
|
break;
|
|
|
|
// If terminate is not set, something else destroyed the window. This
|
|
|
|
// can also happen in --wid mode when the parent window is destroyed.
|
|
|
|
if (!w32->terminate)
|
|
|
|
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
|
|
|
|
RevokeDragDrop(w32->window);
|
2014-09-30 02:21:35 +00:00
|
|
|
w32->destroyed = true;
|
2014-10-17 20:22:10 +00:00
|
|
|
w32->window = NULL;
|
2014-09-30 02:21:35 +00:00
|
|
|
PostQuitMessage(0);
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
break;
|
2014-01-06 14:35:27 +00:00
|
|
|
case WM_SYSCOMMAND:
|
2016-12-16 15:53:09 +00:00
|
|
|
switch (wParam & 0xFFF0) {
|
2014-01-06 14:35:27 +00:00
|
|
|
case SC_SCREENSAVE:
|
|
|
|
case SC_MONITORPOWER:
|
|
|
|
if (w32->disable_screensaver) {
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_VERBOSE(w32, "killing screensaver\n");
|
2012-04-06 21:14:21 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-04-07 12:47:06 +00:00
|
|
|
break;
|
2012-04-06 21:14:21 +00:00
|
|
|
}
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
2014-12-29 13:52:34 +00:00
|
|
|
case WM_NCHITTEST:
|
|
|
|
// Provide sizing handles for borderless windows
|
2015-06-20 11:33:23 +00:00
|
|
|
if (!w32->opts->border && !w32->current_fs) {
|
2014-12-29 13:52:34 +00:00
|
|
|
return borderless_nchittest(w32, GET_X_LPARAM(lParam),
|
|
|
|
GET_Y_LPARAM(lParam));
|
|
|
|
}
|
|
|
|
break;
|
2014-01-14 11:27:42 +00:00
|
|
|
case WM_SYSKEYDOWN:
|
2014-11-08 10:43:14 +00:00
|
|
|
// Open the window menu on Alt+Space. Normally DefWindowProc opens the
|
|
|
|
// window menu in response to WM_SYSCHAR, but since mpv translates its
|
|
|
|
// own keyboard input, WM_SYSCHAR isn't generated, so the window menu
|
|
|
|
// must be opened manually.
|
|
|
|
if (wParam == VK_SPACE) {
|
|
|
|
SendMessage(w32->window, WM_SYSCOMMAND, SC_KEYMENU, ' ');
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle all other WM_SYSKEYDOWN messages as WM_KEYDOWN
|
2014-04-17 15:25:45 +00:00
|
|
|
case WM_KEYDOWN:
|
2014-11-08 11:14:29 +00:00
|
|
|
handle_key_down(w32, wParam, HIWORD(lParam));
|
|
|
|
if (wParam == VK_F10)
|
2014-04-17 15:25:45 +00:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case WM_SYSKEYUP:
|
|
|
|
case WM_KEYUP:
|
2014-11-08 11:14:29 +00:00
|
|
|
handle_key_up(w32, wParam, HIWORD(lParam));
|
|
|
|
if (wParam == VK_F10)
|
2014-01-06 14:35:27 +00:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case WM_CHAR:
|
2014-04-17 15:25:45 +00:00
|
|
|
case WM_SYSCHAR:
|
2014-07-26 18:27:03 +00:00
|
|
|
if (handle_char(w32, wParam))
|
2014-01-06 14:35:27 +00:00
|
|
|
return 0;
|
|
|
|
break;
|
2014-04-17 15:25:45 +00:00
|
|
|
case WM_KILLFOCUS:
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, MP_INPUT_RELEASE_ALL);
|
2014-04-17 15:25:45 +00:00
|
|
|
break;
|
2014-01-06 14:35:27 +00:00
|
|
|
case WM_SETCURSOR:
|
2015-03-13 08:46:54 +00:00
|
|
|
// The cursor should only be hidden if the mouse is in the client area
|
|
|
|
// and if the window isn't in menu mode (HIWORD(lParam) is non-zero)
|
|
|
|
w32->can_set_cursor = LOWORD(lParam) == HTCLIENT && HIWORD(lParam);
|
|
|
|
if (w32->can_set_cursor && !w32->cursor_visible) {
|
2014-01-06 14:35:27 +00:00
|
|
|
SetCursor(NULL);
|
|
|
|
return TRUE;
|
2013-06-20 21:39:53 +00:00
|
|
|
}
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
|
|
|
case WM_MOUSELEAVE:
|
|
|
|
w32->tracking = FALSE;
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_LEAVE);
|
2014-01-06 14:35:27 +00:00
|
|
|
break;
|
|
|
|
case WM_MOUSEMOVE: {
|
2015-02-17 05:50:57 +00:00
|
|
|
if (!w32->tracking) {
|
2014-01-06 14:35:27 +00:00
|
|
|
w32->tracking = TrackMouseEvent(&w32->trackEvent);
|
2015-02-17 05:50:57 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_ENTER);
|
|
|
|
}
|
2014-01-06 14:35:27 +00:00
|
|
|
// Windows can send spurious mouse events, which would make the mpv
|
|
|
|
// core unhide the mouse cursor on completely unrelated events. See:
|
|
|
|
// https://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
|
|
|
|
int x = GET_X_LPARAM(lParam);
|
|
|
|
int y = GET_Y_LPARAM(lParam);
|
|
|
|
if (x != w32->mouse_x || y != w32->mouse_y) {
|
|
|
|
w32->mouse_x = x;
|
|
|
|
w32->mouse_y = y;
|
2014-07-27 19:53:29 +00:00
|
|
|
mp_input_set_mouse_pos(w32->input_ctx, x, y);
|
2014-01-06 14:35:27 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
mouse_button = MP_MOUSE_BTN0 | MP_KEY_STATE_DOWN;
|
|
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
mouse_button = MP_MOUSE_BTN0 | MP_KEY_STATE_UP;
|
|
|
|
break;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
|
|
mouse_button = MP_MOUSE_BTN1 | MP_KEY_STATE_DOWN;
|
|
|
|
break;
|
|
|
|
case WM_MBUTTONUP:
|
|
|
|
mouse_button = MP_MOUSE_BTN1 | MP_KEY_STATE_UP;
|
|
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
|
|
mouse_button = MP_MOUSE_BTN2 | MP_KEY_STATE_DOWN;
|
|
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
|
|
mouse_button = MP_MOUSE_BTN2 | MP_KEY_STATE_UP;
|
|
|
|
break;
|
|
|
|
case WM_MOUSEWHEEL: {
|
|
|
|
int x = GET_WHEEL_DELTA_WPARAM(wParam);
|
|
|
|
mouse_button = x > 0 ? MP_MOUSE_BTN3 : MP_MOUSE_BTN4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WM_XBUTTONDOWN:
|
|
|
|
mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
|
|
|
|
mouse_button |= MP_KEY_STATE_DOWN;
|
|
|
|
break;
|
|
|
|
case WM_XBUTTONUP:
|
|
|
|
mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
|
|
|
|
mouse_button |= MP_KEY_STATE_UP;
|
|
|
|
break;
|
2015-03-10 18:25:30 +00:00
|
|
|
case WM_DISPLAYCHANGE:
|
2015-11-26 11:11:45 +00:00
|
|
|
force_update_display_info(w32);
|
2015-03-10 18:25:30 +00:00
|
|
|
break;
|
2013-06-20 21:39:53 +00:00
|
|
|
}
|
|
|
|
|
2015-11-15 22:03:48 +00:00
|
|
|
if (message == w32->tbtnCreatedMsg) {
|
|
|
|
w32->tbtnCreated = true;
|
2016-09-02 16:08:49 +00:00
|
|
|
update_playback_state(w32);
|
2015-11-15 22:03:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-19 19:56:42 +00:00
|
|
|
if (mouse_button) {
|
2014-07-26 18:27:03 +00:00
|
|
|
mouse_button |= mod_state(w32);
|
|
|
|
mp_input_put_key(w32->input_ctx, mouse_button);
|
2013-12-19 19:56:42 +00:00
|
|
|
|
2014-07-27 19:53:29 +00:00
|
|
|
if (mp_input_mouse_enabled(w32->input_ctx)) {
|
2013-12-19 19:56:42 +00:00
|
|
|
int x = GET_X_LPARAM(lParam);
|
|
|
|
int y = GET_Y_LPARAM(lParam);
|
|
|
|
|
|
|
|
if (mouse_button == (MP_MOUSE_BTN0 | MP_KEY_STATE_DOWN) &&
|
2015-06-20 11:33:23 +00:00
|
|
|
!w32->current_fs &&
|
2014-07-26 18:27:03 +00:00
|
|
|
!mp_input_test_dragging(w32->input_ctx, x, y))
|
2013-12-19 19:56:42 +00:00
|
|
|
{
|
|
|
|
// Window dragging hack
|
|
|
|
ReleaseCapture();
|
|
|
|
SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
|
2014-07-26 18:27:03 +00:00
|
|
|
mp_input_put_key(w32->input_ctx, MP_MOUSE_BTN0 |
|
|
|
|
MP_KEY_STATE_UP);
|
2013-12-19 19:56:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2014-02-20 14:21:17 +00:00
|
|
|
|
|
|
|
if (mouse_button & MP_KEY_STATE_DOWN)
|
|
|
|
SetCapture(w32->window);
|
|
|
|
else
|
|
|
|
ReleaseCapture();
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2012-04-06 21:42:02 +00:00
|
|
|
return DefWindowProcW(hWnd, message, wParam, lParam);
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
|
|
|
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
static pthread_once_t window_class_init_once = PTHREAD_ONCE_INIT;
|
|
|
|
static ATOM window_class;
|
|
|
|
static void register_window_class(void)
|
|
|
|
{
|
|
|
|
window_class = RegisterClassExW(&(WNDCLASSEXW) {
|
|
|
|
.cbSize = sizeof(WNDCLASSEXW),
|
|
|
|
.style = CS_HREDRAW | CS_VREDRAW,
|
|
|
|
.lpfnWndProc = WndProc,
|
|
|
|
.hInstance = HINST_THISCOMPONENT,
|
|
|
|
.hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"),
|
|
|
|
.hCursor = LoadCursor(NULL, IDC_ARROW),
|
|
|
|
.lpszClassName = L"mpv",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static ATOM get_window_class(void)
|
|
|
|
{
|
|
|
|
pthread_once(&window_class_init_once, register_window_class);
|
|
|
|
return window_class;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void resize_child_win(HWND parent)
|
|
|
|
{
|
|
|
|
// Check if an mpv window is a child of this window. This will not
|
|
|
|
// necessarily be the case because the hook functions will run for all
|
|
|
|
// windows on the parent window's thread.
|
|
|
|
ATOM cls = get_window_class();
|
|
|
|
HWND child = FindWindowExW(parent, NULL, (LPWSTR)MAKEINTATOM(cls), NULL);
|
|
|
|
if (!child)
|
|
|
|
return;
|
|
|
|
// Make sure the window was created by this instance
|
|
|
|
if (GetWindowLongPtrW(child, GWLP_HINSTANCE) != (LONG_PTR)HINST_THISCOMPONENT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Resize the mpv window to match its parent window's size
|
|
|
|
RECT rm, rp;
|
|
|
|
if (!GetClientRect(child, &rm))
|
|
|
|
return;
|
|
|
|
if (!GetClientRect(parent, &rp))
|
|
|
|
return;
|
|
|
|
if (EqualRect(&rm, &rp))
|
|
|
|
return;
|
|
|
|
SetWindowPos(child, NULL, 0, 0, rp.right, rp.bottom, SWP_ASYNCWINDOWPOS |
|
|
|
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static LRESULT CALLBACK parent_win_hook(int nCode, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
if (nCode != HC_ACTION)
|
|
|
|
goto done;
|
|
|
|
CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
|
|
|
|
if (cwp->message != WM_WINDOWPOSCHANGED)
|
|
|
|
goto done;
|
|
|
|
resize_child_win(cwp->hwnd);
|
|
|
|
done:
|
|
|
|
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CALLBACK parent_evt_hook(HWINEVENTHOOK hWinEventHook, DWORD event,
|
|
|
|
HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread,
|
|
|
|
DWORD dwmsEventTime)
|
|
|
|
{
|
|
|
|
if (event != EVENT_OBJECT_LOCATIONCHANGE)
|
|
|
|
return;
|
|
|
|
if (!hwnd || idObject != OBJID_WINDOW || idChild != CHILDID_SELF)
|
|
|
|
return;
|
|
|
|
resize_child_win(hwnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void install_parent_hook(struct vo_w32_state *w32)
|
|
|
|
{
|
|
|
|
DWORD pid;
|
|
|
|
DWORD tid = GetWindowThreadProcessId(w32->parent, &pid);
|
|
|
|
|
|
|
|
// If the parent lives inside the current process, install a Windows hook
|
|
|
|
if (pid == GetCurrentProcessId()) {
|
|
|
|
w32->parent_win_hook = SetWindowsHookExW(WH_CALLWNDPROC,
|
|
|
|
parent_win_hook, NULL, tid);
|
|
|
|
} else {
|
|
|
|
// Otherwise, use a WinEvent hook. These don't seem to be as smooth as
|
|
|
|
// Windows hooks, but they can be delivered across process boundaries.
|
|
|
|
w32->parent_evt_hook = SetWinEventHook(
|
|
|
|
EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE,
|
|
|
|
NULL, parent_evt_hook, pid, tid, WINEVENT_OUTOFCONTEXT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_parent_hook(struct vo_w32_state *w32)
|
|
|
|
{
|
|
|
|
if (w32->parent_win_hook)
|
|
|
|
UnhookWindowsHookEx(w32->parent_win_hook);
|
|
|
|
if (w32->parent_evt_hook)
|
|
|
|
UnhookWinEvent(w32->parent_evt_hook);
|
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:57 +00:00
|
|
|
// Dispatch incoming window events and handle them.
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
// This returns only when the thread is asked to terminate.
|
|
|
|
static void run_message_loop(struct vo_w32_state *w32)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
2003-09-19 14:33:51 +00:00
|
|
|
MSG msg;
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
while (GetMessageW(&msg, 0, 0, 0) > 0)
|
2012-04-06 21:42:02 +00:00
|
|
|
DispatchMessageW(&msg);
|
2014-01-06 11:26:42 +00:00
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
// Even if the message loop somehow exits, we still have to respond to
|
|
|
|
// external requests until termination is requested.
|
|
|
|
while (!w32->terminate)
|
|
|
|
mp_dispatch_queue_process(w32->dispatch, 1000);
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 08:31:00 +00:00
|
|
|
struct get_monitor_data {
|
|
|
|
int i;
|
|
|
|
int target;
|
|
|
|
HMONITOR mon;
|
|
|
|
};
|
|
|
|
|
|
|
|
static BOOL CALLBACK get_monitor_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
2014-09-17 08:31:00 +00:00
|
|
|
struct get_monitor_data *data = (struct get_monitor_data*)p;
|
2014-01-06 11:26:42 +00:00
|
|
|
|
2014-09-17 08:31:00 +00:00
|
|
|
if (data->i == data->target) {
|
|
|
|
data->mon = mon;
|
2006-04-16 13:38:28 +00:00
|
|
|
return FALSE;
|
2014-09-17 08:31:00 +00:00
|
|
|
}
|
|
|
|
data->i++;
|
2006-04-16 13:38:28 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-09-17 08:31:00 +00:00
|
|
|
static HMONITOR get_monitor(int id)
|
|
|
|
{
|
|
|
|
struct get_monitor_data data = { .target = id };
|
|
|
|
EnumDisplayMonitors(NULL, NULL, get_monitor_proc, (LPARAM)&data);
|
|
|
|
return data.mon;
|
|
|
|
}
|
|
|
|
|
2014-09-17 05:59:44 +00:00
|
|
|
static void update_screen_rect(struct vo_w32_state *w32)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
2014-07-26 18:27:03 +00:00
|
|
|
struct mp_vo_opts *opts = w32->opts;
|
2015-06-20 11:33:23 +00:00
|
|
|
int screen = w32->current_fs ? opts->fsscreen_id : opts->screen_id;
|
2014-01-06 11:26:42 +00:00
|
|
|
|
2014-09-17 08:31:00 +00:00
|
|
|
// Handle --fs-screen=all
|
2015-06-20 11:33:23 +00:00
|
|
|
if (w32->current_fs && screen == -2) {
|
2014-05-06 21:01:19 +00:00
|
|
|
struct mp_rect rc = {
|
|
|
|
GetSystemMetrics(SM_XVIRTUALSCREEN),
|
|
|
|
GetSystemMetrics(SM_YVIRTUALSCREEN),
|
|
|
|
GetSystemMetrics(SM_CXVIRTUALSCREEN),
|
|
|
|
GetSystemMetrics(SM_CYVIRTUALSCREEN),
|
|
|
|
};
|
|
|
|
rc.x1 += rc.x0;
|
|
|
|
rc.y1 += rc.y0;
|
|
|
|
w32->screenrc = rc;
|
2014-09-17 08:31:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// When not using --fs-screen=all, mpv belongs to a specific HMONITOR
|
|
|
|
HMONITOR mon;
|
|
|
|
if (screen == -1) {
|
|
|
|
// Handle --fs-screen=current and --screen=default
|
|
|
|
mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
|
2014-09-17 05:59:44 +00:00
|
|
|
} else {
|
2014-09-17 08:31:00 +00:00
|
|
|
mon = get_monitor(screen);
|
|
|
|
if (!mon) {
|
|
|
|
MP_INFO(w32, "Screen %d does not exist, falling back to primary\n",
|
|
|
|
screen);
|
|
|
|
mon = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
}
|
2006-04-16 13:38:28 +00:00
|
|
|
}
|
2014-09-17 08:31:00 +00:00
|
|
|
|
|
|
|
MONITORINFO mi = { .cbSize = sizeof(mi) };
|
|
|
|
GetMonitorInfoW(mon, &mi);
|
|
|
|
w32->screenrc = (struct mp_rect){
|
|
|
|
mi.rcMonitor.left, mi.rcMonitor.top,
|
|
|
|
mi.rcMonitor.right, mi.rcMonitor.bottom,
|
|
|
|
};
|
2006-04-16 13:38:28 +00:00
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
static DWORD update_style(struct vo_w32_state *w32, DWORD style)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
2016-12-10 21:01:39 +00:00
|
|
|
const DWORD NO_FRAME = WS_OVERLAPPED | WS_MINIMIZEBOX;
|
|
|
|
const DWORD FRAME = WS_OVERLAPPEDWINDOW;
|
|
|
|
const DWORD FULLSCREEN = NO_FRAME | WS_SYSMENU;
|
|
|
|
style &= ~(NO_FRAME | FRAME | FULLSCREEN);
|
|
|
|
if (w32->current_fs) {
|
|
|
|
style |= FULLSCREEN;
|
|
|
|
} else {
|
|
|
|
style |= w32->opts->border ? FRAME : NO_FRAME;
|
|
|
|
}
|
2012-04-14 11:39:53 +00:00
|
|
|
return style;
|
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
// Update the window title, position, size, and border style.
|
2015-10-02 16:13:24 +00:00
|
|
|
static void reinit_window_state(struct vo_w32_state *w32)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
2003-11-30 16:36:10 +00:00
|
|
|
HWND layer = HWND_NOTOPMOST;
|
2005-11-17 20:49:46 +00:00
|
|
|
RECT r;
|
2003-09-19 14:33:51 +00:00
|
|
|
|
2014-12-09 20:28:35 +00:00
|
|
|
if (w32->parent)
|
2015-10-02 16:13:24 +00:00
|
|
|
return;
|
2010-11-09 22:11:08 +00:00
|
|
|
|
2015-06-20 11:33:23 +00:00
|
|
|
bool new_fs = w32->opts->fullscreen;
|
|
|
|
bool toggle_fs = w32->current_fs != new_fs;
|
|
|
|
w32->current_fs = new_fs;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2014-12-08 06:06:14 +00:00
|
|
|
if (w32->taskbar_list) {
|
|
|
|
ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
|
2015-06-20 11:33:23 +00:00
|
|
|
w32->window, w32->current_fs);
|
2014-12-08 06:06:14 +00:00
|
|
|
}
|
|
|
|
|
2016-12-10 21:50:11 +00:00
|
|
|
DWORD style = update_style(w32, GetWindowLongPtrW(w32->window, GWL_STYLE));
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
if (w32->opts->ontop)
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
layer = HWND_TOPMOST;
|
|
|
|
|
|
|
|
// xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
|
2014-09-17 05:59:44 +00:00
|
|
|
update_screen_rect(w32);
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2014-09-09 16:54:44 +00:00
|
|
|
int screen_w = w32->screenrc.x1 - w32->screenrc.x0;
|
|
|
|
int screen_h = w32->screenrc.y1 - w32->screenrc.y0;
|
|
|
|
|
2015-06-20 11:33:23 +00:00
|
|
|
if (w32->current_fs) {
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
// Save window position and size when switching to fullscreen.
|
|
|
|
if (toggle_fs) {
|
2014-07-26 18:27:03 +00:00
|
|
|
w32->prev_width = w32->dw;
|
|
|
|
w32->prev_height = w32->dh;
|
2012-04-14 11:39:53 +00:00
|
|
|
w32->prev_x = w32->window_x;
|
|
|
|
w32->prev_y = w32->window_y;
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_VERBOSE(w32, "save window bounds: %d:%d:%d:%d\n",
|
2012-04-14 11:39:53 +00:00
|
|
|
w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
}
|
2014-01-06 11:26:42 +00:00
|
|
|
|
2014-05-06 21:01:19 +00:00
|
|
|
w32->window_x = w32->screenrc.x0;
|
|
|
|
w32->window_y = w32->screenrc.y0;
|
2014-09-09 16:54:44 +00:00
|
|
|
w32->dw = screen_w;
|
|
|
|
w32->dh = screen_h;
|
2006-04-12 14:11:26 +00:00
|
|
|
} else {
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
if (toggle_fs) {
|
|
|
|
// Restore window position and size when switching from fullscreen.
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_VERBOSE(w32, "restore window bounds: %d:%d:%d:%d\n",
|
2012-04-14 11:39:53 +00:00
|
|
|
w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
|
2014-07-26 18:27:03 +00:00
|
|
|
w32->dw = w32->prev_width;
|
|
|
|
w32->dh = w32->prev_height;
|
2012-04-14 11:39:53 +00:00
|
|
|
w32->window_x = w32->prev_x;
|
|
|
|
w32->window_y = w32->prev_y;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
}
|
2006-04-12 14:11:26 +00:00
|
|
|
}
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2012-04-14 11:39:53 +00:00
|
|
|
r.left = w32->window_x;
|
2014-07-26 18:27:03 +00:00
|
|
|
r.right = r.left + w32->dw;
|
2012-04-14 11:39:53 +00:00
|
|
|
r.top = w32->window_y;
|
2014-07-26 18:27:03 +00:00
|
|
|
r.bottom = r.top + w32->dh;
|
2011-11-17 23:59:48 +00:00
|
|
|
|
2016-12-10 21:50:11 +00:00
|
|
|
SetWindowLongPtrW(w32->window, GWL_STYLE, style);
|
2014-09-09 16:54:44 +00:00
|
|
|
|
|
|
|
RECT cr = r;
|
2012-04-14 11:39:53 +00:00
|
|
|
add_window_borders(w32->window, &r);
|
2016-04-26 01:47:14 +00:00
|
|
|
// Check on client area size instead of window size on --fit-border=no
|
|
|
|
long o_w;
|
|
|
|
long o_h;
|
|
|
|
if( w32->opts->fit_border ) {
|
|
|
|
o_w = r.right - r.left;
|
|
|
|
o_h = r.bottom - r.top;
|
|
|
|
} else {
|
|
|
|
o_w = cr.right - cr.left;
|
|
|
|
o_h = cr.bottom - cr.top;
|
|
|
|
}
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2016-04-26 01:47:14 +00:00
|
|
|
if ( !w32->current_fs && ( o_w > screen_w || o_h > screen_h ) )
|
2014-09-09 16:54:44 +00:00
|
|
|
{
|
|
|
|
MP_VERBOSE(w32, "requested window size larger than the screen\n");
|
|
|
|
// Use the aspect of the client area, not the full window size.
|
|
|
|
// Basically, try to compute the maximum window size.
|
2016-04-26 01:47:14 +00:00
|
|
|
long n_w;
|
|
|
|
long n_h;
|
|
|
|
if( w32->opts->fit_border ) {
|
|
|
|
n_w = screen_w - (r.right - cr.right) - (cr.left - r.left);
|
|
|
|
n_h = screen_h - (r.bottom - cr.bottom) - (cr.top - r.top);
|
|
|
|
} else {
|
|
|
|
n_w = screen_w;
|
|
|
|
n_h = screen_h;
|
|
|
|
}
|
2014-09-09 16:54:44 +00:00
|
|
|
// Letterbox
|
|
|
|
double asp = (cr.right - cr.left) / (double)(cr.bottom - cr.top);
|
|
|
|
double s_asp = n_w / (double)n_h;
|
|
|
|
if (asp > s_asp) {
|
|
|
|
n_h = n_w / asp;
|
|
|
|
} else {
|
|
|
|
n_w = n_h * asp;
|
|
|
|
}
|
2016-05-21 19:33:06 +00:00
|
|
|
// Save new size
|
|
|
|
w32->dw = n_w;
|
|
|
|
w32->dh = n_h;
|
2016-05-21 19:34:53 +00:00
|
|
|
// Get old window center
|
|
|
|
long o_cx = r.left + (r.right - r.left) / 2;
|
|
|
|
long o_cy = r.top + (r.bottom - r.top) / 2;
|
2016-05-21 19:33:06 +00:00
|
|
|
// Add window borders to the new window size
|
2014-09-09 16:54:44 +00:00
|
|
|
r = (RECT){.right = n_w, .bottom = n_h};
|
|
|
|
add_window_borders(w32->window, &r);
|
2016-05-21 19:33:06 +00:00
|
|
|
// Get top and left border size for client area position calculation
|
|
|
|
long b_top = -r.top;
|
|
|
|
long b_left = -r.left;
|
2016-05-21 19:34:53 +00:00
|
|
|
// Center the final window around the old window center
|
2014-09-09 16:54:44 +00:00
|
|
|
n_w = r.right - r.left;
|
|
|
|
n_h = r.bottom - r.top;
|
2016-05-21 19:34:53 +00:00
|
|
|
r.left = o_cx - n_w / 2;
|
|
|
|
r.top = o_cy - n_h / 2;
|
2014-09-09 16:54:44 +00:00
|
|
|
r.right = r.left + n_w;
|
|
|
|
r.bottom = r.top + n_h;
|
2016-05-21 19:33:06 +00:00
|
|
|
// Save new client area position
|
|
|
|
w32->window_x = r.left + b_left;
|
|
|
|
w32->window_y = r.top + b_top;
|
2014-09-09 16:54:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_VERBOSE(w32, "reset window bounds: %d:%d:%d:%d\n",
|
|
|
|
(int) r.left, (int) r.top, (int)(r.right - r.left),
|
|
|
|
(int)(r.bottom - r.top));
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2012-04-14 11:39:53 +00:00
|
|
|
SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
|
2015-06-20 12:32:14 +00:00
|
|
|
r.bottom - r.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
signal_events(w32, VO_EVENT_RESIZE);
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
static void gui_thread_reconfig(void *ptr)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
2015-10-02 16:13:24 +00:00
|
|
|
struct vo_w32_state *w32 = ptr;
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
|
|
|
|
struct vo *vo = w32->vo;
|
2010-11-10 22:10:30 +00:00
|
|
|
|
2014-05-06 21:01:19 +00:00
|
|
|
struct vo_win_geometry geo;
|
|
|
|
vo_calc_window_geometry(vo, &w32->screenrc, &geo);
|
|
|
|
vo_apply_window_geometry(vo, &geo);
|
|
|
|
|
|
|
|
bool reset_size = w32->o_dwidth != vo->dwidth || w32->o_dheight != vo->dheight;
|
2016-05-30 08:09:43 +00:00
|
|
|
bool pos_init = false;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
|
2014-05-06 21:01:19 +00:00
|
|
|
w32->o_dwidth = vo->dwidth;
|
|
|
|
w32->o_dheight = vo->dheight;
|
2006-04-12 14:11:26 +00:00
|
|
|
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
// the desired size is ignored in wid mode, it always matches the window size.
|
2014-12-09 20:28:35 +00:00
|
|
|
if (!w32->parent) {
|
2012-04-14 11:39:53 +00:00
|
|
|
if (w32->window_bounds_initialized) {
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
// restore vo_dwidth/vo_dheight, which are reset against our will
|
|
|
|
// in vo_config()
|
|
|
|
RECT r;
|
2012-04-14 11:39:53 +00:00
|
|
|
GetClientRect(w32->window, &r);
|
|
|
|
vo->dwidth = r.right;
|
|
|
|
vo->dheight = r.bottom;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
} else {
|
2012-04-14 11:39:53 +00:00
|
|
|
w32->window_bounds_initialized = true;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
reset_size = true;
|
2016-05-30 08:09:43 +00:00
|
|
|
pos_init = true;
|
2014-05-06 21:01:19 +00:00
|
|
|
w32->window_x = w32->prev_x = geo.win.x0;
|
|
|
|
w32->window_y = w32->prev_y = geo.win.y0;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
}
|
2014-01-06 11:26:42 +00:00
|
|
|
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
if (reset_size) {
|
2014-05-06 21:01:19 +00:00
|
|
|
w32->prev_width = vo->dwidth = w32->o_dwidth;
|
|
|
|
w32->prev_height = vo->dheight = w32->o_dheight;
|
win32: fix window creation and size handling
This commit fixes various issues with the way the window position and
size is setup. Most importantly, it fixes some bugs with restoring from
fullscreen state.
Rename create_rendering_context() to reinit_window_state(). This function
doesn't create anything, it just sets the window bounds and styles.
Do not use vo_dx/dy for the window position, as video_out.c overwrites it
with each vo_config() call. Use private variables window_x/y instead.
A big cause for issues was that reinit_window_state() accidentally cleared
the WS_VISIBLE style. I suspect that the API call to temporarily hide the
window was a hack to deal with this. Another bug was that the window style
was changed without calling SetWindowPos with SWP_FRAMECHANGED (as the
MSDN documentation says).
Properly initialize window position and size on vo_config following the
same rules as the x11 backend:
- Never change the window position. The window position should be kept, as
the user might move the window, and resetting the window position e.g.
during ordered chapter playback is not desired.
- Never change the window size, unless the size of the video changes.
These rules don't apply to fullscreen. When switching from fullscreen to
windowed mode, the backend should restore the previous windowed size and
position. When the VO was reconfigured during playback (vo_config() etc.),
the saved window position and size should be changed according to the
rules above, even if the window was in fullscreen mode during
reconfiguring.
Note that these rules might be perceived as awkward by some users: if you
play multiple files with different resolutions, the window won't be
centered when playing the files after the first. This is not a bug.
2011-10-22 00:11:14 +00:00
|
|
|
}
|
2013-03-19 22:36:43 +00:00
|
|
|
} else {
|
|
|
|
RECT r;
|
|
|
|
GetClientRect(w32->window, &r);
|
|
|
|
vo->dwidth = r.right;
|
|
|
|
vo->dheight = r.bottom;
|
2008-12-03 10:38:50 +00:00
|
|
|
}
|
2006-04-12 14:11:26 +00:00
|
|
|
|
2016-06-10 12:37:29 +00:00
|
|
|
// Recenter window around old position on new video size
|
2016-06-25 18:07:38 +00:00
|
|
|
// excluding the case when initial position handled by win_state.
|
2016-05-30 08:09:43 +00:00
|
|
|
if (!pos_init) {
|
|
|
|
w32->window_x += w32->dw / 2 - vo->dwidth / 2;
|
|
|
|
w32->window_y += w32->dh / 2 - vo->dheight / 2;
|
|
|
|
}
|
2015-05-31 15:54:14 +00:00
|
|
|
w32->dw = vo->dwidth;
|
|
|
|
w32->dh = vo->dheight;
|
|
|
|
|
2015-10-02 16:13:24 +00:00
|
|
|
reinit_window_state(w32);
|
2006-04-12 14:11:26 +00:00
|
|
|
}
|
|
|
|
|
2015-10-02 16:09:13 +00:00
|
|
|
// Resize the window. On the first call, it's also made visible.
|
2015-10-31 11:52:02 +00:00
|
|
|
void vo_w32_config(struct vo *vo)
|
2012-04-14 11:39:53 +00:00
|
|
|
{
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
struct vo_w32_state *w32 = vo->w32;
|
2015-10-02 16:13:24 +00:00
|
|
|
mp_dispatch_run(w32->dispatch, gui_thread_reconfig, w32);
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
}
|
2003-09-19 14:33:51 +00:00
|
|
|
|
2015-10-22 08:36:26 +00:00
|
|
|
static void thread_disable_ime(void)
|
|
|
|
{
|
|
|
|
// Disables the IME for windows on this thread. imm32.dll must be loaded
|
|
|
|
// dynamically to account for machines without East Asian language support.
|
|
|
|
HMODULE imm32 = LoadLibraryW(L"imm32.dll");
|
|
|
|
if (!imm32)
|
|
|
|
return;
|
|
|
|
BOOL (WINAPI *pImmDisableIME)(DWORD) = (BOOL (WINAPI*)(DWORD))
|
|
|
|
GetProcAddress(imm32, "ImmDisableIME");
|
|
|
|
if (pImmDisableIME)
|
|
|
|
pImmDisableIME(0);
|
|
|
|
FreeLibrary(imm32);
|
|
|
|
}
|
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
static void *gui_thread(void *ptr)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = ptr;
|
|
|
|
bool ole_ok = false;
|
|
|
|
int res = 0;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2014-10-19 21:32:34 +00:00
|
|
|
mpthread_set_name("win32 window");
|
|
|
|
|
2015-10-22 08:36:26 +00:00
|
|
|
thread_disable_ime();
|
|
|
|
|
2014-07-26 18:30:52 +00:00
|
|
|
w32_thread_context = w32;
|
|
|
|
|
2014-12-09 20:28:35 +00:00
|
|
|
if (w32->opts->WinID >= 0)
|
|
|
|
w32->parent = (HWND)(intptr_t)(w32->opts->WinID);
|
|
|
|
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
ATOM cls = get_window_class();
|
2014-12-09 20:28:35 +00:00
|
|
|
if (w32->parent) {
|
2008-02-09 14:14:35 +00:00
|
|
|
RECT r;
|
2014-12-09 20:28:35 +00:00
|
|
|
GetClientRect(w32->parent, &r);
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY,
|
|
|
|
(LPWSTR)MAKEINTATOM(cls), L"mpv",
|
2012-04-14 11:39:53 +00:00
|
|
|
WS_CHILD | WS_VISIBLE,
|
2014-07-26 18:27:03 +00:00
|
|
|
0, 0, r.right, r.bottom,
|
2016-06-10 12:37:29 +00:00
|
|
|
w32->parent, 0, HINST_THISCOMPONENT, NULL);
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
|
|
|
|
// Install a hook to get notifications when the parent changes size
|
|
|
|
if (w32->window)
|
|
|
|
install_parent_hook(w32);
|
2012-04-14 11:39:53 +00:00
|
|
|
} else {
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
w32->window = CreateWindowExW(0, (LPWSTR)MAKEINTATOM(cls), L"mpv",
|
2014-07-26 18:27:03 +00:00
|
|
|
update_style(w32, 0),
|
2014-07-26 18:29:18 +00:00
|
|
|
CW_USEDEFAULT, SW_HIDE, 100, 100,
|
2016-06-10 12:37:29 +00:00
|
|
|
0, 0, HINST_THISCOMPONENT, NULL);
|
2012-04-14 11:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!w32->window) {
|
2014-07-26 18:27:03 +00:00
|
|
|
MP_ERR(w32, "unable to create window!\n");
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
goto done;
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 06:06:14 +00:00
|
|
|
if (SUCCEEDED(OleInitialize(NULL))) {
|
|
|
|
ole_ok = true;
|
|
|
|
|
2016-02-02 20:43:16 +00:00
|
|
|
fmtetc_url.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocatorW"));
|
2014-01-06 04:21:54 +00:00
|
|
|
DropTarget* dropTarget = talloc(NULL, DropTarget);
|
2014-07-26 18:27:03 +00:00
|
|
|
DropTarget_Init(dropTarget, w32);
|
2014-01-06 04:21:54 +00:00
|
|
|
RegisterDragDrop(w32->window, &dropTarget->iface);
|
2014-12-08 06:06:14 +00:00
|
|
|
|
|
|
|
// ITaskbarList2 has the MarkFullscreenWindow method, which is used to
|
|
|
|
// make sure the taskbar is hidden when mpv goes fullscreen
|
|
|
|
if (SUCCEEDED(CoCreateInstance(&CLSID_TaskbarList, NULL,
|
2014-12-16 10:19:33 +00:00
|
|
|
CLSCTX_INPROC_SERVER, &IID_ITaskbarList2,
|
2014-12-08 06:06:14 +00:00
|
|
|
(void**)&w32->taskbar_list)))
|
|
|
|
{
|
|
|
|
if (FAILED(ITaskbarList2_HrInit(w32->taskbar_list))) {
|
|
|
|
ITaskbarList2_Release(w32->taskbar_list);
|
|
|
|
w32->taskbar_list = NULL;
|
|
|
|
}
|
|
|
|
}
|
2015-11-15 22:03:48 +00:00
|
|
|
|
|
|
|
// ITaskbarList3 has methods for status indication on taskbar buttons,
|
|
|
|
// however that interface is only available on Win7/2008 R2 or newer
|
|
|
|
if (SUCCEEDED(CoCreateInstance(&CLSID_TaskbarList, NULL,
|
|
|
|
CLSCTX_INPROC_SERVER, &IID_ITaskbarList3,
|
|
|
|
(void**)&w32->taskbar_list3)))
|
|
|
|
{
|
|
|
|
if (FAILED(ITaskbarList3_HrInit(w32->taskbar_list3))) {
|
|
|
|
ITaskbarList3_Release(w32->taskbar_list3);
|
|
|
|
w32->taskbar_list3 = NULL;
|
|
|
|
} else {
|
|
|
|
w32->tbtnCreatedMsg = RegisterWindowMessage(L"TaskbarButtonCreated");
|
|
|
|
}
|
|
|
|
}
|
2014-12-08 06:06:14 +00:00
|
|
|
} else {
|
|
|
|
MP_ERR(w32, "Failed to initialize OLE/COM\n");
|
2014-01-06 04:21:54 +00:00
|
|
|
}
|
2014-01-06 11:26:42 +00:00
|
|
|
|
2013-07-25 17:47:49 +00:00
|
|
|
w32->tracking = FALSE;
|
|
|
|
w32->trackEvent = (TRACKMOUSEEVENT){
|
|
|
|
.cbSize = sizeof(TRACKMOUSEEVENT),
|
|
|
|
.dwFlags = TME_LEAVE,
|
|
|
|
.hwndTrack = w32->window,
|
|
|
|
};
|
2013-07-25 17:19:12 +00:00
|
|
|
|
2014-12-09 20:28:35 +00:00
|
|
|
if (w32->parent)
|
2012-04-14 11:39:53 +00:00
|
|
|
EnableWindow(w32->window, 0);
|
2014-01-06 11:26:42 +00:00
|
|
|
|
2013-07-14 12:57:39 +00:00
|
|
|
w32->cursor_visible = true;
|
2012-04-14 11:39:53 +00:00
|
|
|
|
2014-09-17 05:59:44 +00:00
|
|
|
update_screen_rect(w32);
|
2003-09-19 14:33:51 +00:00
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
mp_dispatch_set_wakeup_fn(w32->dispatch, wakeup_gui_thread, w32);
|
|
|
|
|
|
|
|
res = 1;
|
|
|
|
done:
|
|
|
|
|
|
|
|
mp_rendezvous(w32, res); // init barrier
|
|
|
|
|
|
|
|
// This blocks until the GUI thread is to be exited.
|
|
|
|
if (res)
|
|
|
|
run_message_loop(w32);
|
|
|
|
|
|
|
|
MP_VERBOSE(w32, "uninit\n");
|
|
|
|
|
w32_common: use hooks to detect parent window resize
Because VOCTRL_CHECK_EVENTS is processed asynchronously (as of 088a007,)
the GUI thread no longer gets regular wakeups, so the old check that
made sure the video window matched the parent window's size in --wid
embedding mode did not run very often. This made --wid embedding not
very usable.
Instead of polling for window size changes, use Windows hooks to react
to them when they happen. When the parent window is owned by the same
process as the video window, use a WH_CALLWNDPROC hook. When the parent
window is not owned by the same process, WinEvents must be used, which
are not as smooth, but still work for this purpose.
Since neither SetWindowsHookEx nor SetWinEventHook take a context
parameter to send data to the hook function, the hook functions must
find the child window by its class instead, so there are a few changes
to ensure this is fast and the class is unique.
This also fixes up the logic to handle window destruction. When a parent
window is destroyed, its children are also destroyed, so this gives us a
way to react to parent window destruction without polling.
2016-08-25 13:07:42 +00:00
|
|
|
remove_parent_hook(w32);
|
|
|
|
if (w32->window && !w32->destroyed)
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
DestroyWindow(w32->window);
|
2014-12-08 06:06:14 +00:00
|
|
|
if (w32->taskbar_list)
|
|
|
|
ITaskbarList2_Release(w32->taskbar_list);
|
2015-11-15 22:03:48 +00:00
|
|
|
if (w32->taskbar_list3)
|
|
|
|
ITaskbarList3_Release(w32->taskbar_list3);
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
if (ole_ok)
|
|
|
|
OleUninitialize();
|
|
|
|
SetThreadExecutionState(ES_CONTINUOUS);
|
|
|
|
|
|
|
|
w32_thread_context = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns: 1 = Success, 0 = Failure
|
|
|
|
int vo_w32_init(struct vo *vo)
|
|
|
|
{
|
|
|
|
assert(!vo->w32);
|
|
|
|
|
|
|
|
struct vo_w32_state *w32 = talloc_ptrtype(vo, w32);
|
|
|
|
*w32 = (struct vo_w32_state){
|
|
|
|
.log = mp_log_new(w32, vo->log, "win32"),
|
|
|
|
.vo = vo,
|
|
|
|
.opts = vo->opts,
|
|
|
|
.input_ctx = vo->input_ctx,
|
|
|
|
.dispatch = mp_dispatch_create(w32),
|
|
|
|
};
|
|
|
|
vo->w32 = w32;
|
|
|
|
|
|
|
|
if (pthread_create(&w32->thread, NULL, gui_thread, w32))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (!mp_rendezvous(w32, 0)) { // init barrier
|
|
|
|
pthread_join(w32->thread, NULL);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-11-08 18:31:09 +00:00
|
|
|
// While the UI runs in its own thread, the thread in which this function
|
|
|
|
// runs in will be the renderer thread. Apply magic MMCSS cargo-cult,
|
|
|
|
// which might stop Windows from throttling clock rate and so on.
|
2015-12-06 18:20:23 +00:00
|
|
|
if (vo->opts->mmcss_profile[0]) {
|
|
|
|
wchar_t *profile = mp_from_utf8(NULL, vo->opts->mmcss_profile);
|
|
|
|
w32->avrt_handle = AvSetMmThreadCharacteristicsW(profile, &(DWORD){0});
|
|
|
|
talloc_free(profile);
|
|
|
|
}
|
2015-11-08 18:31:09 +00:00
|
|
|
|
2003-09-19 14:33:51 +00:00
|
|
|
return 1;
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
fail:
|
|
|
|
talloc_free(w32);
|
|
|
|
vo->w32 = NULL;
|
|
|
|
return 0;
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
|
|
|
|
2016-09-29 13:21:23 +00:00
|
|
|
struct disp_names_data {
|
|
|
|
HMONITOR assoc;
|
|
|
|
int count;
|
|
|
|
char **names;
|
|
|
|
};
|
|
|
|
|
|
|
|
static BOOL CALLBACK disp_names_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
|
|
|
|
{
|
|
|
|
struct disp_names_data *data = (struct disp_names_data*)p;
|
|
|
|
|
|
|
|
// get_disp_names() adds data->assoc to the list, so skip it here
|
|
|
|
if (mon == data->assoc)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
MONITORINFOEXW mi = { .cbSize = sizeof mi };
|
|
|
|
if (GetMonitorInfoW(mon, (MONITORINFO*)&mi)) {
|
|
|
|
MP_TARRAY_APPEND(NULL, data->names, data->count,
|
|
|
|
mp_to_utf8(NULL, mi.szDevice));
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char **get_disp_names(struct vo_w32_state *w32)
|
|
|
|
{
|
|
|
|
// Get the client area of the window in screen space
|
|
|
|
RECT rect = { 0 };
|
|
|
|
GetClientRect(w32->window, &rect);
|
|
|
|
MapWindowPoints(w32->window, NULL, (POINT*)&rect, 2);
|
|
|
|
|
|
|
|
struct disp_names_data data = { .assoc = w32->monitor };
|
|
|
|
|
|
|
|
// Make sure the monitor that Windows considers to be associated with the
|
|
|
|
// window is first in the list
|
|
|
|
MONITORINFOEXW mi = { .cbSize = sizeof mi };
|
|
|
|
if (GetMonitorInfoW(data.assoc, (MONITORINFO*)&mi)) {
|
|
|
|
MP_TARRAY_APPEND(NULL, data.names, data.count,
|
|
|
|
mp_to_utf8(NULL, mi.szDevice));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the names of the other monitors that intersect the client rect
|
|
|
|
EnumDisplayMonitors(NULL, &rect, disp_names_proc, (LPARAM)&data);
|
|
|
|
MP_TARRAY_APPEND(NULL, data.names, data.count, NULL);
|
|
|
|
return data.names;
|
|
|
|
}
|
|
|
|
|
2015-01-08 17:32:23 +00:00
|
|
|
static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
|
2013-05-15 16:17:18 +00:00
|
|
|
{
|
|
|
|
switch (request) {
|
2014-01-06 14:35:27 +00:00
|
|
|
case VOCTRL_FULLSCREEN:
|
2014-07-26 18:27:03 +00:00
|
|
|
if (w32->opts->fullscreen != w32->current_fs)
|
|
|
|
reinit_window_state(w32);
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_ONTOP:
|
2014-07-26 18:27:03 +00:00
|
|
|
reinit_window_state(w32);
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_BORDER:
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
reinit_window_state(w32);
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
2014-09-04 20:53:50 +00:00
|
|
|
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
|
2014-01-06 14:35:27 +00:00
|
|
|
int *s = arg;
|
|
|
|
|
|
|
|
if (!w32->window_bounds_initialized)
|
|
|
|
return VO_FALSE;
|
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
s[0] = w32->current_fs ? w32->prev_width : w32->dw;
|
|
|
|
s[1] = w32->current_fs ? w32->prev_height : w32->dh;
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2014-09-04 20:53:50 +00:00
|
|
|
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
|
2014-01-06 14:35:27 +00:00
|
|
|
int *s = arg;
|
|
|
|
|
|
|
|
if (!w32->window_bounds_initialized)
|
|
|
|
return VO_FALSE;
|
|
|
|
if (w32->current_fs) {
|
2016-05-21 19:43:25 +00:00
|
|
|
w32->prev_x += w32->prev_width / 2 - s[0] / 2;
|
|
|
|
w32->prev_y += w32->prev_height / 2 - s[1] / 2;
|
2014-01-06 14:35:27 +00:00
|
|
|
w32->prev_width = s[0];
|
|
|
|
w32->prev_height = s[1];
|
|
|
|
} else {
|
2016-05-21 19:43:25 +00:00
|
|
|
w32->window_x += w32->dw / 2 - s[0] / 2;
|
|
|
|
w32->window_y += w32->dh / 2 - s[1] / 2;
|
2014-07-26 18:27:03 +00:00
|
|
|
w32->dw = s[0];
|
|
|
|
w32->dh = s[1];
|
2013-11-02 16:50:09 +00:00
|
|
|
}
|
2013-07-14 12:57:39 +00:00
|
|
|
|
2014-07-26 18:27:03 +00:00
|
|
|
reinit_window_state(w32);
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2015-03-09 09:30:33 +00:00
|
|
|
case VOCTRL_GET_WIN_STATE:
|
|
|
|
*(int *)arg = IsIconic(w32->window) ? VO_WIN_STATE_MINIMIZED : 0;
|
|
|
|
return VO_TRUE;
|
2014-01-06 14:35:27 +00:00
|
|
|
case VOCTRL_SET_CURSOR_VISIBILITY:
|
|
|
|
w32->cursor_visible = *(bool *)arg;
|
|
|
|
|
2015-03-13 08:46:54 +00:00
|
|
|
if (w32->can_set_cursor && w32->tracking) {
|
2014-01-06 14:35:27 +00:00
|
|
|
if (w32->cursor_visible)
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
else
|
|
|
|
SetCursor(NULL);
|
2013-05-16 22:10:46 +00:00
|
|
|
}
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_KILL_SCREENSAVER:
|
|
|
|
w32->disable_screensaver = true;
|
2015-04-20 10:24:19 +00:00
|
|
|
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED |
|
|
|
|
ES_DISPLAY_REQUIRED);
|
2014-01-06 14:35:27 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_RESTORE_SCREENSAVER:
|
|
|
|
w32->disable_screensaver = false;
|
|
|
|
SetThreadExecutionState(ES_CONTINUOUS);
|
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_UPDATE_WINDOW_TITLE: {
|
2015-08-01 18:51:52 +00:00
|
|
|
wchar_t *title = mp_from_utf8(NULL, (char *)arg);
|
2014-01-06 14:35:27 +00:00
|
|
|
SetWindowTextW(w32->window, title);
|
|
|
|
talloc_free(title);
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2015-11-15 22:03:48 +00:00
|
|
|
case VOCTRL_UPDATE_PLAYBACK_STATE: {
|
2016-09-02 16:08:49 +00:00
|
|
|
w32->current_pstate = *(struct voctrl_playback_state *)arg;
|
2015-11-22 10:40:20 +00:00
|
|
|
|
2016-09-02 16:08:49 +00:00
|
|
|
update_playback_state(w32);
|
2015-11-15 22:03:48 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2015-03-10 18:25:30 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_FPS:
|
2015-11-26 11:11:45 +00:00
|
|
|
update_display_info(w32);
|
2015-03-10 18:25:30 +00:00
|
|
|
*(double*) arg = w32->display_fps;
|
|
|
|
return VO_TRUE;
|
2016-09-29 13:21:23 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_NAMES:
|
|
|
|
*(char ***)arg = get_disp_names(w32);
|
|
|
|
return VO_TRUE;
|
2015-11-26 11:11:45 +00:00
|
|
|
case VOCTRL_GET_ICC_PROFILE:
|
|
|
|
update_display_info(w32);
|
|
|
|
if (w32->color_profile) {
|
|
|
|
bstr *p = arg;
|
|
|
|
*p = stream_read_file(w32->color_profile, NULL,
|
|
|
|
w32->vo->global, 100000000); // 100 MB
|
|
|
|
return p->len ? VO_TRUE : VO_FALSE;
|
|
|
|
}
|
|
|
|
return VO_FALSE;
|
2013-05-15 16:17:18 +00:00
|
|
|
}
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
static void do_control(void *ptr)
|
|
|
|
{
|
|
|
|
void **p = ptr;
|
|
|
|
struct vo_w32_state *w32 = p[0];
|
|
|
|
int *events = p[1];
|
|
|
|
int request = *(int *)p[2];
|
|
|
|
void *arg = p[3];
|
|
|
|
int *ret = p[4];
|
2015-01-08 17:32:23 +00:00
|
|
|
*ret = gui_thread_control(w32, request, arg);
|
2016-08-05 14:01:26 +00:00
|
|
|
*events |= atomic_fetch_and(&w32->event_flags, 0);
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
// Safe access, since caller (owner of vo) is blocked.
|
|
|
|
if (*events & VO_EVENT_RESIZE) {
|
|
|
|
w32->vo->dwidth = w32->dw;
|
|
|
|
w32->vo->dheight = w32->dh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = vo->w32;
|
2016-08-05 14:01:26 +00:00
|
|
|
if (request == VOCTRL_CHECK_EVENTS) {
|
|
|
|
*events |= atomic_fetch_and(&w32->event_flags, 0);
|
2016-08-07 14:33:57 +00:00
|
|
|
if (*events & VO_EVENT_RESIZE) {
|
|
|
|
mp_dispatch_lock(w32->dispatch);
|
|
|
|
vo->dwidth = w32->dw;
|
|
|
|
vo->dheight = w32->dh;
|
|
|
|
mp_dispatch_unlock(w32->dispatch);
|
|
|
|
}
|
2016-08-05 14:01:26 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
} else {
|
|
|
|
int r;
|
|
|
|
void *p[] = {w32, events, &request, arg, &r};
|
|
|
|
mp_dispatch_run(w32->dispatch, do_control, p);
|
|
|
|
return r;
|
|
|
|
}
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void do_terminate(void *ptr)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = ptr;
|
|
|
|
w32->terminate = true;
|
2014-09-30 02:21:35 +00:00
|
|
|
|
|
|
|
if (!w32->destroyed)
|
|
|
|
DestroyWindow(w32->window);
|
2016-09-16 12:25:50 +00:00
|
|
|
|
|
|
|
mp_dispatch_interrupt(w32->dispatch);
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
}
|
|
|
|
|
2012-04-14 11:39:53 +00:00
|
|
|
void vo_w32_uninit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = vo->w32;
|
2012-04-23 22:41:12 +00:00
|
|
|
if (!w32)
|
|
|
|
return;
|
2014-01-06 11:26:42 +00:00
|
|
|
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
mp_dispatch_run(w32->dispatch, do_terminate, w32);
|
|
|
|
pthread_join(w32->thread, NULL);
|
2014-07-26 18:27:03 +00:00
|
|
|
|
2015-11-08 18:31:09 +00:00
|
|
|
AvRevertMmThreadCharacteristics(w32->avrt_handle);
|
|
|
|
|
2012-04-14 11:39:53 +00:00
|
|
|
talloc_free(w32);
|
|
|
|
vo->w32 = NULL;
|
2003-09-19 14:33:51 +00:00
|
|
|
}
|
2014-07-26 18:27:03 +00:00
|
|
|
|
|
|
|
HWND vo_w32_hwnd(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = vo->w32;
|
win32: move window handling to a separate thread
The windows message loop now runs in a separate thread. Rendering,
such as with Direct3D or OpenGL, still happens in the main thread.
In particular, this should prevent the video from freezing if the
window is dragged. (The reason was that the message dispatcher won't
return while the dragging is active, so mpv couldn't update the
video at all.)
This is pretty "rough" and just hacked in, and there's no API yet to
make this easier for other backends. It will be cleaned up later
once we're sure that it works, or when we know how exactly it should
work. One oddity is that OpenGL is actually completely managed in the
renderer thread, while e.g. Cocoa (which has its own threading code)
creates the context in the GUI thread, and then lets the renderer
thread access it.
One strange issue is that we now have to stop WM_CLOSE from actually
closing the window. Instead, we wait until the playloop handles the
close command, and requests the VO to shutdown. This is done mainly
because closing the window apparently destroys it, and then WM_USER
can't be handled anymore - which means the playloop has no way to
wakeup the GUI thread. It seems you can't really win here... maybe
there's a better way to have a thread receive messages with and
without a window, but I didn't find one yet.
Dragging the window (by clicking into the middle of it) behaves
strangely in wine, but didn't before the change. Reason unknown.
2014-07-26 18:31:31 +00:00
|
|
|
return w32->window; // immutable, so no synchronization needed
|
2014-07-26 18:27:03 +00:00
|
|
|
}
|
2014-08-05 17:40:57 +00:00
|
|
|
|
|
|
|
void vo_w32_run_on_thread(struct vo *vo, void (*cb)(void *ctx), void *ctx)
|
|
|
|
{
|
|
|
|
struct vo_w32_state *w32 = vo->w32;
|
|
|
|
mp_dispatch_run(w32->dispatch, cb, ctx);
|
|
|
|
}
|