[vlc-commits] modules:vout: add a specific vout window module for Windows

Steve Lhomme git at videolan.org
Tue Apr 2 16:33:08 CEST 2019


vlc | branch: master | Steve Lhomme <robux4 at ycbcr.xyz> | Tue Mar 26 11:17:45 2019 +0100| [8daa8aef720eb0ca207e960876e06482a4c6a450] | committer: Steve Lhomme

modules:vout: add a specific vout window module for Windows

The video-wallpaper/desktop mode is not supported yet.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=8daa8aef720eb0ca207e960876e06482a4c6a450
---

 modules/video_output/Makefile.am    |   5 +
 modules/video_output/win32/window.c | 425 ++++++++++++++++++++++++++++++++++++
 po/POTFILES.in                      |   1 +
 3 files changed, 431 insertions(+)

diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index bb307bb9b8..3081e9c14e 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -386,6 +386,11 @@ if HAVE_WIN32
 vout_LTLIBRARIES += libdrawable_plugin.la
 endif
 
+libwin32_window_plugin_la_SOURCES = video_output/win32/window.c
+if HAVE_WIN32_DESKTOP
+vout_LTLIBRARIES += libwin32_window_plugin.la
+endif
+
 
 ### OS/2 ###
 if HAVE_OS2
diff --git a/modules/video_output/win32/window.c b/modules/video_output/win32/window.c
new file mode 100644
index 0000000000..eb7b34d881
--- /dev/null
+++ b/modules/video_output/win32/window.c
@@ -0,0 +1,425 @@
+/**
+ * @file window.c
+ * @brief Win32 non-embedded video window provider
+ */
+/*****************************************************************************
+ * Copyright © 2007-2010 VLC authors and VideoLAN
+ *             2016-2019 VideoLabs, VLC authors and VideoLAN
+ *
+ * Authors: Filippo Carone <filippo at carone.org>
+ *          Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdarg.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_window.h>
+
+#include <shellapi.h>                                         /* ExtractIcon */
+
+#define RECTWidth(r)   (LONG)((r).right - (r).left)
+#define RECTHeight(r)  (LONG)((r).bottom - (r).top)
+
+#define WM_VLC_SET_ON_TOP    (WM_APP + 2)
+
+#define IDM_TOGGLE_ON_TOP  (WM_USER + 1)
+
+typedef struct vout_window_sys_t
+{
+    vlc_thread_t thread;
+    vlc_mutex_t  lock;
+    vlc_cond_t   wait;
+    bool         b_ready;
+    bool         b_done;
+
+    HWND hwnd;
+
+    WCHAR class_main[256];
+    HICON vlc_icon;
+
+    /* state before fullscreen */
+    WINDOWPLACEMENT window_placement;
+    LONG            i_window_style;
+} vout_window_sys_t;
+
+
+static void Resize(vout_window_t *wnd, unsigned width, unsigned height)
+{
+    vout_window_sys_t *sys = wnd->sys;
+
+    /* When you create a window you give the dimensions you wish it to
+     * have. Unfortunatly these dimensions will include the borders and
+     * titlebar. We use the following function to find out the size of
+     * the window corresponding to the useable surface we want */
+    RECT decorated_window = {
+        .right = width,
+        .bottom = height,
+    };
+    LONG i_window_style = GetWindowLong(sys->hwnd, GWL_STYLE);
+    AdjustWindowRect( &decorated_window, i_window_style, 0 );
+    SetWindowPos(sys->hwnd, 0, 0, 0,
+                 RECTWidth(decorated_window), RECTHeight(decorated_window),
+                 SWP_NOZORDER|SWP_NOMOVE);
+}
+
+static int Enable(vout_window_t *wnd, const vout_window_cfg_t *cfg)
+{
+    vout_window_sys_t *sys = wnd->sys;
+
+    LONG i_window_style;
+    if (cfg->is_decorated)
+        i_window_style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
+    else
+        i_window_style = WS_POPUP;
+    i_window_style |= WS_CLIPCHILDREN;
+
+    /* allow user to regain control over input events if requested */
+    bool b_mouse_support = var_InheritBool( wnd, "mouse-events" );
+    bool b_key_support = var_InheritBool( wnd, "keyboard-events" );
+    if( !b_mouse_support && !b_key_support )
+        i_window_style |= WS_DISABLED;
+    SetWindowLong(sys->hwnd, GWL_STYLE, i_window_style);
+
+    Resize(wnd, cfg->width, cfg->height);
+    ShowWindow( sys->hwnd, SW_SHOW );
+    return VLC_SUCCESS;
+}
+
+static void Disable(struct vout_window_t *wnd)
+{
+    vout_window_sys_t *sys = wnd->sys;
+    ShowWindow( sys->hwnd, SW_HIDE );
+}
+
+static void SetState(vout_window_t *wnd, unsigned state)
+{
+    enum vout_window_state wstate = state;
+    vout_window_sys_t *sys = wnd->sys;
+    switch (wstate)
+    {
+    case VOUT_WINDOW_STATE_ABOVE:
+        PostMessage( sys->hwnd, WM_VLC_SET_ON_TOP, TRUE, 0);
+        break;
+    case VOUT_WINDOW_STATE_NORMAL:
+        PostMessage( sys->hwnd, WM_VLC_SET_ON_TOP, FALSE, 0);
+        break;
+    }
+}
+
+static void SetFullscreen(vout_window_t *wnd, const char *id)
+{
+    VLC_UNUSED(id);
+    msg_Dbg(wnd, "entering fullscreen mode");
+    vout_window_sys_t *sys = wnd->sys;
+
+    sys->window_placement.length = sizeof(WINDOWPLACEMENT);
+    GetWindowPlacement(sys->hwnd, &sys->window_placement);
+
+    sys->i_window_style = GetWindowLong(sys->hwnd, GWL_STYLE);
+
+    /* Change window style, no borders and no title bar */
+    SetWindowLong(sys->hwnd, GWL_STYLE, WS_CLIPCHILDREN | WS_VISIBLE);
+
+    /* Retrieve current window position so fullscreen will happen
+     * on the right screen */
+    HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
+    MONITORINFO mi;
+    mi.cbSize = sizeof(MONITORINFO);
+    if (GetMonitorInfo(hmon, &mi))
+        SetWindowPos(sys->hwnd, 0,
+                     mi.rcMonitor.left,
+                     mi.rcMonitor.top,
+                     RECTWidth(mi.rcMonitor),
+                     RECTHeight(mi.rcMonitor),
+                     SWP_NOZORDER|SWP_FRAMECHANGED);
+}
+
+static void UnsetFullscreen(vout_window_t *wnd)
+{
+    msg_Dbg(wnd, "leaving fullscreen mode");
+    vout_window_sys_t *sys = wnd->sys;
+
+    /* return to normal window for non embedded vout */
+    if (sys->window_placement.length)
+    {
+        SetWindowLong(sys->hwnd, GWL_STYLE, sys->i_window_style);
+        SetWindowPlacement(sys->hwnd, &sys->window_placement);
+    }
+    ShowWindow(sys->hwnd, SW_SHOWNORMAL);
+}
+
+static void SetAbove( HWND hwnd, bool is_on_top )
+{
+    HMENU hMenu = GetSystemMenu(hwnd, FALSE);
+    if (is_on_top && !(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) {
+        CheckMenuItem(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND | MFS_CHECKED);
+        SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+    } else if (!is_on_top && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) {
+        CheckMenuItem(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND | MFS_UNCHECKED);
+        SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
+    }
+}
+
+/*****************************************************************************
+ * WinVoutEventProc: This is the window event processing function.
+ *****************************************************************************
+ * On Windows, when you create a window you have to attach an event processing
+ * function to it. The aim of this function is to manage "Queued Messages" and
+ * "Nonqueued Messages".
+ * Queued Messages are those picked up and retransmitted by vout_Manage
+ * (using the GetMessage and DispatchMessage functions).
+ * Nonqueued Messages are those that Windows will send directly to this
+ * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
+ *****************************************************************************/
+static long FAR PASCAL WinVoutEventProc( HWND hwnd, UINT message,
+                                         WPARAM wParam, LPARAM lParam )
+{
+    if( message == WM_CREATE )
+    {
+        /* Store wnd 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( p_user_data == 0 )
+        return DefWindowProc(hwnd, message, wParam, lParam);
+    vout_window_t *wnd = (vout_window_t *)p_user_data;
+
+    switch( message )
+    {
+    case WM_CLOSE:
+        vout_window_ReportClose(wnd);
+        break;
+
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+
+    case WM_SIZE:
+        vout_window_ReportSize(wnd, LOWORD(lParam), HIWORD(lParam));
+        return 0;
+
+    case WM_SYSCOMMAND:
+        switch (wParam)
+        {
+        case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
+        {
+            msg_Dbg(wnd, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
+            HMENU hMenu = GetSystemMenu(hwnd, FALSE);
+            const bool is_on_top = (GetMenuState(hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND) & MF_CHECKED) == 0;
+            SetAbove( wnd, is_on_top ? VOUT_WINDOW_STATE_ABOVE : VOUT_WINDOW_STATE_NORMAL );
+            return 0;
+        }
+        default:
+            break;
+        }
+        break;
+
+    case WM_VLC_SET_ON_TOP:
+        SetAbove( hwnd, wParam != 0);
+        return 0;
+    }
+
+    return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+static void Close(vout_window_t *wnd)
+{
+    vout_window_sys_t *sys = wnd->sys;
+
+    if (sys->hwnd)
+    {
+        PostMessage( sys->hwnd, WM_CLOSE, 0, 0 );
+        /* wait until the thread is done */
+        vlc_mutex_lock( &sys->lock );
+        while( !sys->b_done )
+        {
+            vlc_cond_wait( &sys->wait, &sys->lock );
+        }
+        vlc_mutex_unlock( &sys->lock );
+
+        DestroyWindow( sys->hwnd );
+    }
+    vlc_join(sys->thread, NULL);
+    vlc_mutex_destroy( &sys->lock );
+    vlc_cond_destroy( &sys->wait );
+
+    HINSTANCE hInstance = GetModuleHandle(NULL);
+    UnregisterClass( sys->class_main, hInstance );
+    if( sys->vlc_icon )
+        DestroyIcon( sys->vlc_icon );
+    wnd->sys = NULL;
+}
+
+static void *EventThread( void *p_this )
+{
+    vout_window_t *wnd = (vout_window_t *)p_this;
+    vout_window_sys_t *sys = wnd->sys;
+
+    int canc = vlc_savecancel ();
+
+    HINSTANCE hInstance = GetModuleHandle(NULL);
+
+    LONG i_window_style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_CLIPCHILDREN;
+
+    /* allow user to regain control over input events if requested */
+    bool b_mouse_support = var_InheritBool( wnd, "mouse-events" );
+    bool b_key_support = var_InheritBool( wnd, "keyboard-events" );
+    if( !b_mouse_support && !b_key_support )
+        i_window_style |= WS_DISABLED;
+
+    /* Create the window */
+    sys->hwnd =
+        CreateWindowEx( WS_EX_NOPARENTNOTIFY,
+                    sys->class_main,                 /* name of window class */
+                    _T(VOUT_TITLE) _T(" (VLC Video Output)"),/* window title */
+                    i_window_style,                          /* window style */
+                    CW_USEDEFAULT,                   /* default X coordinate */
+                    CW_USEDEFAULT,                   /* default Y coordinate */
+                    CW_USEDEFAULT,                           /* window width */
+                    CW_USEDEFAULT,                          /* window height */
+                    0,                                      /* parent window */
+                    NULL,                          /* no menu in this window */
+                    hInstance,            /* handle of this program instance */
+                    wnd );                           /* send vd to WM_CREATE */
+
+    vlc_mutex_lock( &sys->lock );
+    sys->b_ready = true;
+    vlc_cond_signal( &sys->wait );
+    vlc_mutex_unlock( &sys->lock );
+
+    if (sys->hwnd == NULL)
+    {
+        vlc_restorecancel( canc );
+        return NULL;
+    }
+
+    /* Append a "Always On Top" entry in the system menu */
+    HMENU hMenu = GetSystemMenu( sys->hwnd, FALSE );
+    AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") );
+    AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
+                       IDM_TOGGLE_ON_TOP, _T("Always on &Top") );
+
+    for( ;; )
+    {
+        MSG msg;
+        if( !GetMessage( &msg, 0, 0, 0 ) )
+        {
+            break;
+        }
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+    sys->b_done = true;
+    vlc_cond_signal( &sys->wait );
+    vlc_restorecancel(canc);
+    return NULL;
+}
+
+static const struct vout_window_operations ops = {
+    .enable  = Enable,
+    .disable = Disable,
+    .resize = Resize,
+    .set_state = SetState,
+    .set_fullscreen = SetFullscreen,
+    .unset_fullscreen = UnsetFullscreen,
+    .destroy = Close,
+};
+
+static int Open(vout_window_t *wnd)
+{
+    if (var_InheritBool( wnd, "video-wallpaper" ))
+        return VLC_EGENERIC;
+
+    vout_window_sys_t *sys = vlc_obj_calloc(VLC_OBJECT(wnd), 1, sizeof(vout_window_sys_t));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    _snwprintf( sys->class_main, ARRAYSIZE(sys->class_main),
+               TEXT("VLC standalone window %p"), (void *)sys );
+
+    HINSTANCE hInstance = GetModuleHandle(NULL);
+
+    WCHAR app_path[MAX_PATH];
+    if( GetModuleFileName( NULL, app_path, MAX_PATH ) )
+        sys->vlc_icon = ExtractIcon( hInstance, app_path    , 0 );
+
+    WNDCLASS wc = { 0 };
+    /* Fill in the window class structure */
+    wc.style         = CS_OWNDC|CS_DBLCLKS;           /* style: dbl click */
+    wc.lpfnWndProc   = (WNDPROC)WinVoutEventProc;        /* event handler */
+    wc.hInstance     = hInstance;                             /* instance */
+    wc.hIcon         = sys->vlc_icon;            /* load the vlc big icon */
+    wc.lpszClassName = sys->class_main;            /* use a special class */
+
+    /* Register the window class */
+    if( !RegisterClass(&wc) )
+    {
+        if( sys->vlc_icon )
+            DestroyIcon( sys->vlc_icon );
+
+        msg_Err( wnd, "RegisterClass FAILED (err=%lu)", GetLastError() );
+        return VLC_EGENERIC;
+    }
+    vlc_mutex_init( &sys->lock );
+    vlc_cond_init( &sys->wait );
+    sys->b_ready = false;
+    sys->b_done = false;
+
+    wnd->sys = sys;
+    if( vlc_clone( &sys->thread, EventThread, wnd, VLC_THREAD_PRIORITY_LOW ) )
+    {
+        Close(wnd);
+        return VLC_EGENERIC;
+    }
+
+    vlc_mutex_lock( &sys->lock );
+    while( !sys->b_ready )
+    {
+        vlc_cond_wait( &sys->wait, &sys->lock );
+    }
+    if (sys->hwnd == NULL)
+    {
+        vlc_mutex_unlock( &sys->lock );
+        Close(wnd);
+        return VLC_EGENERIC;
+    }
+    vlc_mutex_unlock( &sys->lock );
+
+    wnd->sys = sys;
+    wnd->type = VOUT_WINDOW_TYPE_HWND;
+    wnd->handle.hwnd = sys->hwnd;
+    wnd->ops = &ops;
+    wnd->info.has_double_click = true;
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+    set_shortname(N_("Win32 window"))
+    set_description(N_("Win32 window"))
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_capability("vout window", 10)
+    set_callbacks(Open, NULL)
+vlc_module_end()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b0e290f832..aa0a88492a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1231,6 +1231,7 @@ modules/video_output/win32/direct3d9.c
 modules/video_output/win32/direct3d11.c
 modules/video_output/win32/events.c
 modules/video_output/win32/glwin32.c
+modules/video_output/win32/window.c
 modules/video_output/win32/wingdi.c
 modules/video_output/win32/wgl.c
 modules/video_output/vdummy.c



More information about the vlc-commits mailing list