vlc | branch: master | Steve Lhomme <[email protected]> | 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 <[email protected]> + * 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 _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
