summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorJames Ross-Gowan <rossymiles@gmail.com>2016-08-25 23:07:42 +1000
committerJames Ross-Gowan <rossymiles@gmail.com>2016-08-26 20:02:58 +1000
commit31f28da0f32f1fd37925b2c30a53c83916583ef7 (patch)
treeced1b30bb1eaadc5f6d3d936da5759809784a868 /video
parentef0b2e33b1cc8d064f64b864443bf1c233691508 (diff)
downloadmpv-31f28da0f32f1fd37925b2c30a53c83916583ef7.tar.bz2
mpv-31f28da0f32f1fd37925b2c30a53c83916583ef7.tar.xz
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.
Diffstat (limited to 'video')
-rw-r--r--video/out/w32_common.c152
1 files changed, 117 insertions, 35 deletions
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index b1c95f78d1..dea0061b71 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -48,8 +48,6 @@
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
-static const wchar_t classname[] = L"mpv";
-
static __thread struct vo_w32_state *w32_thread_context;
struct vo_w32_state {
@@ -64,6 +62,8 @@ struct vo_w32_state {
HWND window;
HWND parent; // 0 normally, set in embedding mode
+ HHOOK parent_win_hook;
+ HWINEVENTHOOK parent_evt_hook;
HMONITOR monitor; // Handle of the current screen
struct mp_rect screenrc; // Size and virtual position of the current screen
@@ -747,11 +747,19 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
// is that will make us lose WM_USER wakeups.
mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
return 0;
+ case WM_NCDESTROY: // Sometimes only WM_NCDESTROY is received in --wid mode
case WM_DESTROY:
+ 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);
w32->destroyed = true;
w32->window = NULL;
PostQuitMessage(0);
- return 0;
+ break;
case WM_SYSCOMMAND:
switch (wParam) {
case SC_SCREENSAVE:
@@ -901,27 +909,109 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
return DefWindowProcW(hWnd, message, wParam, lParam);
}
+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);
+}
+
// Dispatch incoming window events and handle them.
// This returns only when the thread is asked to terminate.
static void run_message_loop(struct vo_w32_state *w32)
{
MSG msg;
- while (GetMessageW(&msg, 0, 0, 0) > 0) {
+ while (GetMessageW(&msg, 0, 0, 0) > 0)
DispatchMessageW(&msg);
- if (w32->parent) {
- RECT r, rp;
- BOOL res = GetClientRect(w32->window, &r);
- res = res && GetClientRect(w32->parent, &rp);
- if (res && (r.right != rp.right || r.bottom != rp.bottom))
- MoveWindow(w32->window, 0, 0, rp.right, rp.bottom, FALSE);
-
- // Window has probably been closed, e.g. due to parent program crash
- if (!IsWindow(w32->parent))
- mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN);
- }
- }
-
// Even if the message loop somehow exits, we still have to respond to
// external requests until termination is requested.
while (!w32->terminate)
@@ -1222,33 +1312,26 @@ static void *gui_thread(void *ptr)
thread_disable_ime();
- WNDCLASSEXW wcex = {
- .cbSize = sizeof wcex,
- .style = CS_HREDRAW | CS_VREDRAW,
- .lpfnWndProc = WndProc,
- .hInstance = HINST_THISCOMPONENT,
- .hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"),
- .hCursor = LoadCursor(NULL, IDC_ARROW),
- .lpszClassName = classname,
- };
- RegisterClassExW(&wcex);
-
w32_thread_context = w32;
if (w32->opts->WinID >= 0)
w32->parent = (HWND)(intptr_t)(w32->opts->WinID);
+ ATOM cls = get_window_class();
if (w32->parent) {
RECT r;
GetClientRect(w32->parent, &r);
- w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, classname,
- classname,
+ w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY,
+ (LPWSTR)MAKEINTATOM(cls), L"mpv",
WS_CHILD | WS_VISIBLE,
0, 0, r.right, r.bottom,
w32->parent, 0, HINST_THISCOMPONENT, NULL);
+
+ // Install a hook to get notifications when the parent changes size
+ if (w32->window)
+ install_parent_hook(w32);
} else {
- w32->window = CreateWindowExW(0, classname,
- classname,
+ w32->window = CreateWindowExW(0, (LPWSTR)MAKEINTATOM(cls), L"mpv",
update_style(w32, 0),
CW_USEDEFAULT, SW_HIDE, 100, 100,
0, 0, HINST_THISCOMPONENT, NULL);
@@ -1323,10 +1406,9 @@ done:
MP_VERBOSE(w32, "uninit\n");
- if (w32->window) {
- RevokeDragDrop(w32->window);
+ remove_parent_hook(w32);
+ if (w32->window && !w32->destroyed)
DestroyWindow(w32->window);
- }
if (w32->taskbar_list)
ITaskbarList2_Release(w32->taskbar_list);
if (w32->taskbar_list3)