[vlc-devel] [PATCH v2 5/5] vout: win32: drawable: use an internal HWND to detect size changes

Steve Lhomme robux4 at ycbcr.xyz
Thu May 7 16:11:19 CEST 2020


In the past the display module was checking if the size of the host HWND
changed via the Manage callback. Now this callback doesn't exist anymore and
the window module is responsible for reporting its size.

In the case of embedded-window we need to read the size of the parent as we
don't get any events telling us it's resized, nor that we should be resized
(since apps using set_hwnd have no way to know the HWND we use internally to
resize it).
---
 modules/video_output/drawable.c | 182 +++++++++++++++++++++++++++++++-
 1 file changed, 178 insertions(+), 4 deletions(-)

diff --git a/modules/video_output/drawable.c b/modules/video_output/drawable.c
index ad82afadd9e9..9e1d098d0576 100644
--- a/modules/video_output/drawable.c
+++ b/modules/video_output/drawable.c
@@ -64,6 +64,125 @@ static const struct vout_window_operations ops = {
     .destroy = Close,
 };
 
+#define RECTWidth(r)   (LONG)((r).right - (r).left)
+#define RECTHeight(r)  (LONG)((r).bottom - (r).top)
+
+static const TCHAR *EMBED_HWND_CLASS = TEXT("VLC embeded HWND");
+
+struct drawable_sys
+{
+    vlc_mutex_t hwnd_lock;
+    vlc_cond_t hwnd_ready;
+    bool hwnd_is_ready;
+
+    vout_window_t *wnd;
+    HWND hWnd;
+    HWND embed_hwnd;
+    RECT rect_parent;
+};
+
+static LRESULT CALLBACK WinVoutEventProc(HWND hwnd, UINT message,
+                                         WPARAM wParam, LPARAM lParam )
+{
+    if( message == WM_CREATE /*WM_NCCREATE*/ )
+    {
+        /* Store our internal structure for future use */
+        CREATESTRUCT *c = (CREATESTRUCT *)lParam;
+        SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)c->lpCreateParams );
+        return 0;
+    }
+
+    LONG_PTR p_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA );
+    if( unlikely(p_user_data == 0) )
+        return DefWindowProc(hwnd, message, wParam, lParam);
+    struct drawable_sys *sys = (struct drawable_sys *)p_user_data;
+
+    vout_window_t *wnd = sys->wnd;
+
+    RECT clientRect;
+    GetClientRect(sys->embed_hwnd, &clientRect);
+    if (RECTWidth(sys->rect_parent)  != RECTWidth(clientRect) ||
+        RECTHeight(sys->rect_parent) != RECTHeight(clientRect)) {
+        sys->rect_parent = clientRect;
+
+        SetWindowPos(hwnd, 0, 0, 0,
+                     RECTWidth(sys->rect_parent),
+                     RECTHeight(sys->rect_parent),
+                     SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
+    }
+
+    switch( message )
+    {
+    case WM_ERASEBKGND:
+        /* nothing to erase */
+        return 1;
+
+    case WM_PAINT:
+        /* nothing to repaint */
+        ValidateRect(hwnd, NULL);
+        break;
+
+    case WM_CLOSE:
+        vout_window_ReportClose(wnd);
+        return 0;
+
+    /* the window has been closed so shut down everything now */
+    case WM_DESTROY:
+        /* just destroy the window */
+        PostQuitMessage( 0 );
+        return 0;
+
+    case WM_SIZE:
+        vout_window_ReportSize(wnd, LOWORD(lParam), HIWORD(lParam));
+        return 0;
+
+    default:
+        break;
+    }
+
+    /* Let windows handle the message */
+    return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+static DWORD WINAPI WindowLoopThread(LPVOID lpParameter)
+{
+    struct drawable_sys *sys = lpParameter;
+
+    /* Get this module's instance */
+    HMODULE hInstance = GetModuleHandle(NULL);
+
+    vlc_mutex_lock(&sys->hwnd_lock);
+    sys->hWnd =
+        CreateWindowEx( 0,
+                    EMBED_HWND_CLASS,              /* name of window class */
+                    TEXT("Embedded HWND"),                 /* window title */
+                    WS_CHILD|WS_VISIBLE|WS_DISABLED,       /* window style */
+                    0,                             /* default X coordinate */
+                    0,                             /* default Y coordinate */
+                    RECTWidth(sys->rect_parent),           /* window width */
+                    RECTHeight(sys->rect_parent),         /* window height */
+                    sys->embed_hwnd,                      /* parent window */
+                    NULL,                        /* no menu in this window */
+                    hInstance,          /* handle of this program instance */
+                    sys );                            /* send to WM_CREATE */
+    sys->hwnd_is_ready = true;
+    vlc_cond_signal(&sys->hwnd_ready);
+    vlc_mutex_unlock(&sys->hwnd_lock);
+
+    if (sys->hWnd == NULL)
+        return 1;
+
+    /* Main loop */
+    /* GetMessage will sleep if there's no message in the queue */
+    MSG msg;
+    while( GetMessage( &msg, 0, 0, 0 ) )
+    {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+    return 0;
+}
+
 static void RemoveDrawable(HWND val)
 {
     size_t n = 0;
@@ -84,6 +203,9 @@ static void RemoveDrawable(HWND val)
     {
         free (used);
         used = NULL;
+
+        HINSTANCE hInstance = GetModuleHandle(NULL);
+        UnregisterClass( EMBED_HWND_CLASS, hInstance );
     }
     vlc_mutex_unlock (&serializer);
 }
@@ -102,6 +224,7 @@ static int Open(vout_window_t *wnd)
     size_t n = 0;
 
     vlc_mutex_lock (&serializer);
+    bool first_hwnd = used == NULL;
     if (used != NULL)
         for (/*n = 0*/; used[n]; n++)
             if (used[n] == val)
@@ -122,11 +245,62 @@ static int Open(vout_window_t *wnd)
 
     vlc_mutex_unlock (&serializer);
 
+    struct drawable_sys *sys = vlc_obj_calloc(VLC_OBJECT(wnd), 1, sizeof(*sys));
+    if (unlikely(sys == NULL)) {
+        RemoveDrawable(val);
+        return VLC_ENOMEM;
+    }
+
+    sys->embed_hwnd = (HWND)val;
+    sys->wnd = wnd;
+    GetClientRect(sys->embed_hwnd, &sys->rect_parent);
+    vlc_mutex_init(&sys->hwnd_lock);
+    vlc_cond_init(&sys->hwnd_ready);
+
+    if (first_hwnd)
+    {
+        /* Get this module's instance */
+        HMODULE hInstance = GetModuleHandle(NULL);
+
+        WNDCLASS wc = { 0 };                      /* window class components */
+        wc.lpfnWndProc   = WinVoutEventProc;                /* event handler */
+        wc.hInstance     = hInstance;                            /* instance */
+        wc.lpszClassName = EMBED_HWND_CLASS;
+        if( !RegisterClass(&wc) )
+        {
+            msg_Err( sys->wnd, "RegisterClass failed (err=%lu)", GetLastError() );
+            goto error;
+        }
+    }
+
+    // Create a Thread for the window event loop
+    if (CreateThread(NULL, 0, WindowLoopThread, sys, 0, NULL) == NULL)
+    {
+        msg_Err( sys->wnd, "CreateThread failed (err=%lu)", GetLastError() );
+        goto error;
+    }
+
+    vlc_mutex_lock(&sys->hwnd_lock);
+    while (!sys->hwnd_is_ready)
+        vlc_cond_wait(&sys->hwnd_ready, &sys->hwnd_lock);
+    vlc_mutex_unlock(&sys->hwnd_lock);
+
+    if (sys->hWnd == NULL)
+    {
+        msg_Err( sys->wnd, "Failed to create a window (err=%lu)", GetLastError() );
+        goto error;
+    }
+
     wnd->type = VOUT_WINDOW_TYPE_HWND;
-    wnd->handle.hwnd = (void *)val;
+    wnd->handle.hwnd = (void *)sys->hWnd;
     wnd->ops = &ops;
-    wnd->sys = (void *)val;
+    wnd->sys = (void *)sys;
     return VLC_SUCCESS;
+
+error:
+    RemoveDrawable(sys->embed_hwnd);
+
+    return VLC_EGENERIC;
 }
 
 /**
@@ -134,7 +308,7 @@ static int Open(vout_window_t *wnd)
  */
 static void Close (vout_window_t *wnd)
 {
-    HWND val = (HWND) wnd->handle.hwnd;
+    struct drawable_sys *sys = wnd->sys;
 
-    RemoveDrawable(val);
+    RemoveDrawable(sys->embed_hwnd);
 }
-- 
2.17.1



More information about the vlc-devel mailing list