win32_fullscreen.cpp 31.9 KB
Newer Older
1 2 3
/*****************************************************************************
 * vlc_win32_fullscreen.h: a VLC plugin for Mozilla
 *****************************************************************************
4
 * Copyright © 2002-2011 VideoLAN and VLC authors
5 6 7 8
 * $Id$
 *
 * Authors: Sergey Radionov <rsatom@gmail.com>
 *
9 10 11
 * 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
12 13 14 15 16
 * (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
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20 21 22
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
23 24 25
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
26 27

#include <windows.h>
28
#include <windowsx.h>
29 30 31
#include <commctrl.h>
#include <uxtheme.h>

32
#include "win32_fullscreen.h"
33

34 35 36 37 38 39
////////////////////////////////////////////////////////////////////////////////
//VLCControlsWnd members
////////////////////////////////////////////////////////////////////////////////
VLCControlsWnd*
VLCControlsWnd::CreateControlsWindow(HINSTANCE hInstance,
                                     VLCWindowsManager* wm, HWND hWndParent)
40
{
41 42 43
    VLCControlsWnd* wnd = new VLCControlsWnd(hInstance, wm);
    if( wnd && wnd->Create(hWndParent) ) {
        return wnd;
44
    }
45
    delete wnd;
46 47 48
    return 0;
}

49 50 51 52
VLCControlsWnd::VLCControlsWnd(HINSTANCE hInstance, VLCWindowsManager* wm)
    :VLCWnd(hInstance), _wm(wm),
     hToolTipWnd(0), hFSButton(0), hPlayPauseButton(0),
     hVideoPosScroll(0), hMuteButton(0), hVolumeSlider(0)
53
{
54 55
}

56
VLCControlsWnd::~VLCControlsWnd()
57
{
58 59 60
    if(hToolTipWnd){
        ::DestroyWindow(hToolTipWnd);
        hToolTipWnd = 0;
61 62 63
    }
}

64
bool VLCControlsWnd::Create(HWND hWndParent)
65
{
66 67 68
    return VLCWnd::CreateEx(WS_EX_TOPMOST, TEXT("VLC Controls Window"),
                            WS_CHILD|WS_CLIPSIBLINGS,
                            0, 0, 0, 0, hWndParent, 0);
69 70
}

71
void VLCControlsWnd::PreRegisterWindowClass(WNDCLASS* wc)
72
{
73 74 75
    wc->lpszClassName = TEXT("VLC Controls Class");
    wc->hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
};
76

77
void VLCControlsWnd::CreateToolTip()
78
{
79 80 81 82 83 84 85 86 87 88 89 90
    hToolTipWnd = CreateWindowEx(WS_EX_TOPMOST,
            TOOLTIPS_CLASS,
            NULL,
            WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            hWnd(),
            NULL,
            hInstance(),
            NULL);
91

92 93 94 95
    SetWindowPos(hToolTipWnd,
            HWND_TOPMOST,
            0, 0, 0, 0,
            SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
96 97


98 99 100 101 102
    TOOLINFO ti;
    ti.cbSize = sizeof(TOOLINFO);
    ti.uFlags = TTF_SUBCLASS|TTF_IDISHWND;
    ti.hwnd   = hWnd();
    ti.hinst  = hInstance();
103

104 105
    TCHAR HintText[100];
    RECT ActivateTTRect;
106

107 108 109 110 111 112 113
    //end fullscreen button tooltip
    GetWindowRect(hFSButton, &ActivateTTRect);
    GetWindowText(hFSButton, HintText, sizeof(HintText));
    ti.uId = (UINT_PTR)hFSButton;
    ti.rect = ActivateTTRect;
    ti.lpszText = HintText;
    SendMessage(hToolTipWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
114

115 116 117 118 119 120 121
    //play/pause button tooltip
    GetWindowRect(hPlayPauseButton, &ActivateTTRect);
    GetWindowText(hPlayPauseButton, HintText, sizeof(HintText));
    ti.uId = (UINT_PTR)hPlayPauseButton;
    ti.rect = ActivateTTRect;
    ti.lpszText = HintText;
    SendMessage(hToolTipWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
122

123 124 125 126 127 128 129
    //mute button tooltip
    GetWindowRect(hMuteButton, &ActivateTTRect);
    GetWindowText(hMuteButton, HintText, sizeof(HintText));
    ti.uId = (UINT_PTR)hMuteButton;
    ti.rect = ActivateTTRect;
    ti.lpszText = HintText;
    SendMessage(hToolTipWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
130 131
}

132
LRESULT VLCControlsWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
133
{
134
    switch(uMsg){
135
        case WM_CREATE:{
136
            const int ControlsHeight = 21+3;
Cheng Sun's avatar
Cheng Sun committed
137
            const int ButtonsWidth = ControlsHeight;
138

139
            int HorizontalOffset = xControlsSpace;
140
            int ControlWidth = ButtonsWidth;
141 142 143 144 145 146 147
            hPlayPauseButton =
                CreateWindow(TEXT("BUTTON"), TEXT("Play/Pause"),
                             WS_CHILD|WS_VISIBLE|BS_BITMAP|BS_FLAT,
                             HorizontalOffset, xControlsSpace,
                             ControlWidth, ControlsHeight, hWnd(),
                             (HMENU)ID_FS_PLAY_PAUSE, 0, 0);
            SendMessage(hPlayPauseButton, BM_SETIMAGE,
148
                        (WPARAM)IMAGE_BITMAP, (LPARAM)RC().hPlayBitmap);
149
            HorizontalOffset+=ControlWidth+xControlsSpace;
150 151

            ControlWidth = 200;
152 153 154 155 156 157 158
            int VideoPosControlHeight = 10;
            hVideoPosScroll =
                CreateWindow(PROGRESS_CLASS, TEXT("Video Position"),
                             WS_CHILD|WS_DISABLED|WS_VISIBLE|SBS_HORZ|SBS_TOPALIGN|PBS_SMOOTH,
                             HorizontalOffset, xControlsSpace+(ControlsHeight-VideoPosControlHeight)/2,
                             ControlWidth, VideoPosControlHeight, hWnd(),
                             (HMENU)ID_FS_VIDEO_POS_SCROLL, 0, 0);
159 160 161 162 163
            HMODULE hThModule = LoadLibrary(TEXT("UxTheme.dll"));
            if(hThModule){
                FARPROC proc = GetProcAddress(hThModule, "SetWindowTheme");
                typedef HRESULT (WINAPI* SetWindowThemeProc)(HWND, LPCWSTR, LPCWSTR);
                if(proc){
164
                    ((SetWindowThemeProc)proc)(hVideoPosScroll, L"", L"");
165 166 167
                }
                FreeLibrary(hThModule);
            }
168
            HorizontalOffset+=ControlWidth+xControlsSpace;
169
            SendMessage(hVideoPosScroll, (UINT)PBM_SETRANGE, 0, MAKELPARAM(0, 1000));
170 171

            ControlWidth = ButtonsWidth;
172 173 174 175 176 177 178 179 180
            hMuteButton =
                CreateWindow(TEXT("BUTTON"), TEXT("Mute"),
                             WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX|BS_PUSHLIKE|BS_BITMAP, //BS_FLAT
                             HorizontalOffset, xControlsSpace,
                             ControlWidth, ControlsHeight,
                             hWnd(), (HMENU)ID_FS_MUTE, 0, 0);
            SendMessage(hMuteButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,
                        (LPARAM)RC().hVolumeBitmap);
            HorizontalOffset+=ControlWidth+xControlsSpace;
181 182

            ControlWidth = 100;
183 184 185 186 187 188 189 190 191 192 193
            hVolumeSlider =
                CreateWindow(TRACKBAR_CLASS, TEXT("Volume"),
                             WS_CHILD|WS_VISIBLE|TBS_HORZ|TBS_BOTTOM|TBS_AUTOTICKS,
                             HorizontalOffset, xControlsSpace,
                             ControlWidth, ControlsHeight - 4, hWnd(),
                             (HMENU)ID_FS_VOLUME, 0, 0);
            HorizontalOffset+=ControlWidth+xControlsSpace;
            SendMessage(hVolumeSlider, TBM_SETRANGE, FALSE, (LPARAM) MAKELONG (0, 100));
            SendMessage(hVolumeSlider, TBM_SETTICFREQ, (WPARAM) 10, 0);

            ControlWidth = ButtonsWidth;
194 195 196 197
            DWORD dwFSBtnStyle = WS_CHILD|BS_BITMAP|BS_FLAT;
            if( !PO() || PO()->get_enable_fs() ){
                dwFSBtnStyle |= WS_VISIBLE;
            }
198 199
            hFSButton =
                CreateWindow(TEXT("BUTTON"), TEXT("Toggle fullscreen"),
200
                             dwFSBtnStyle,
201 202 203 204
                             HorizontalOffset, xControlsSpace,
                             ControlWidth, ControlsHeight, hWnd(),
                             (HMENU)ID_FS_SWITCH_FS, 0, 0);
            SendMessage(hFSButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,
205
                        (LPARAM)RC().hFullscreenBitmap);
206 207 208 209
            HorizontalOffset+=ControlWidth+xControlsSpace;

            RECT rect;
            GetClientRect(GetParent(hWnd()), &rect);
210 211

            int ControlWndWidth = HorizontalOffset;
212 213 214 215 216
            int ControlWndHeight = xControlsSpace+ControlsHeight+xControlsSpace;
            SetWindowPos(hWnd(), 0,
                         0, (rect.bottom - rect.top) - ControlWndWidth,
                         rect.right-rect.left, ControlWndHeight,
                         SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOACTIVATE);
217

218
            CreateToolTip();
219
            RegisterToVLCEvents();
220
            SyncVolumeSliderWithVLCVolume();
221 222
            break;
        }
223 224 225
        case WM_LBUTTONUP:{
            POINT BtnUpPoint = {LOWORD(lParam), HIWORD(lParam)};
            RECT VideoPosRect;
226 227
            GetWindowRect(hVideoPosScroll, &VideoPosRect);
            ClientToScreen(hWnd(), &BtnUpPoint);
228
            if(PtInRect(&VideoPosRect, BtnUpPoint)){
229
                SetVideoPos(float(BtnUpPoint.x-VideoPosRect.left)/(VideoPosRect.right-VideoPosRect.left));
230 231 232 233
            }
            break;
        }
        case WM_TIMER:{
234
            NeedHideControls();
235 236 237 238
            break;
        }
        case WM_SETCURSOR:{
            RECT VideoPosRect;
239
            GetWindowRect(hVideoPosScroll, &VideoPosRect);
240 241 242 243 244 245 246
            DWORD dwMsgPos = GetMessagePos();
            POINT MsgPosPoint = {LOWORD(dwMsgPos), HIWORD(dwMsgPos)};
            if(PtInRect(&VideoPosRect, MsgPosPoint)){
                SetCursor(LoadCursor(NULL, IDC_HAND));
                return TRUE;
            }
            else{
247
                return VLCWnd::WindowProc(uMsg, wParam, lParam);
248 249 250 251 252 253 254 255 256 257 258 259
            }
            break;
        }
        case WM_NCDESTROY:
            break;
        case WM_COMMAND:{
            WORD NCode = HIWORD(wParam);
            WORD Control = LOWORD(wParam);
            switch(NCode){
                case BN_CLICKED:{
                    switch(Control){
                        case ID_FS_SWITCH_FS:
260
                            WM().ToggleFullScreen();
261 262
                            break;
                        case ID_FS_PLAY_PAUSE:{
263
                            if( VP() ){
264
                                if( IsPlaying() )
265
                                    VP()->mlp().pause();
266
                                else
267
                                    VP()->play();
268 269 270 271
                            }
                            break;
                        }
                        case ID_FS_MUTE:{
272
                            if( VP() ){
273
                                VP()->get_mp().setMute( IsDlgButtonChecked(hWnd(), ID_FS_MUTE) != FALSE );
274
                                SyncVolumeSliderWithVLCVolume();
275 276 277 278 279 280 281 282 283
                            }
                            break;
                        }
                    }
                    break;
                }
            }
            break;
        }
284
        case WM_SIZE:{
285 286 287 288 289 290 291 292
            if( GetWindowLong(hWnd(), GWL_STYLE) & WS_VISIBLE &&
                !WM().IsFullScreen() &&
                ( !PO() || !PO()->get_show_toolbar() ) )
            {
                //hide controls when they are not allowed
                NeedHideControls();
            }

293 294
            const int new_client_width = LOWORD(lParam);

295 296 297
            bool isFSBtnVisible =
                (GetWindowLong(hFSButton, GWL_STYLE) & WS_VISIBLE) != 0;

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
            HDWP hDwp = BeginDeferWindowPos(4);

            int VideoScrollWidth = new_client_width;

            POINT pt = {0, 0};
            RECT rect;
            GetWindowRect(hPlayPauseButton, &rect);
            pt.x = rect.right;
            ScreenToClient(hWnd(), &pt);
            VideoScrollWidth -= pt.x;
            VideoScrollWidth -= xControlsSpace;

            RECT VideoSrcollRect;
            GetWindowRect(hVideoPosScroll, &VideoSrcollRect);

            RECT MuteRect;
            GetWindowRect(hMuteButton, &MuteRect);
            VideoScrollWidth -= xControlsSpace;
            VideoScrollWidth -= (MuteRect.right - MuteRect.left);

            RECT VolumeRect;
            GetWindowRect(hVolumeSlider, &VolumeRect);
            VideoScrollWidth -= xControlsSpace;
            VideoScrollWidth -= (VolumeRect.right - VolumeRect.left);

323 324 325 326 327 328 329
            RECT FSRect = {0, 0, 0, 0};
            if( isFSBtnVisible ) {
                GetWindowRect(hFSButton, &FSRect);
                VideoScrollWidth -= xControlsSpace;
                VideoScrollWidth -= (FSRect.right - FSRect.left);
                VideoScrollWidth -= xControlsSpace;
            }
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358

            pt.x = VideoSrcollRect.left;
            pt.y = VideoSrcollRect.top;
            ScreenToClient(hWnd(), &pt);
            hDwp = DeferWindowPos(hDwp, hVideoPosScroll, 0, pt.x, pt.y,
                                  VideoScrollWidth,
                                  VideoSrcollRect.bottom - VideoSrcollRect.top,
                                  SWP_NOACTIVATE|SWP_NOOWNERZORDER);

            int HorizontalOffset =
                pt.x + VideoScrollWidth + xControlsSpace;
            pt.x = 0;
            pt.y = MuteRect.top;
            ScreenToClient(hWnd(), &pt);
            hDwp = DeferWindowPos(hDwp, hMuteButton, 0,
                                  HorizontalOffset, pt.y, 0, 0,
                                  SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOOWNERZORDER);
            HorizontalOffset +=
                MuteRect.right - MuteRect.left + xControlsSpace;

            pt.x = 0;
            pt.y = VolumeRect.top;
            ScreenToClient(hWnd(), &pt);
            hDwp = DeferWindowPos(hDwp, hVolumeSlider, 0,
                                  HorizontalOffset, pt.y, 0, 0,
                                  SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOOWNERZORDER);
            HorizontalOffset +=
                VolumeRect.right - VolumeRect.left + xControlsSpace;

359 360 361 362 363 364 365 366
            if( isFSBtnVisible ) {
                pt.x = 0;
                pt.y = FSRect.top;
                ScreenToClient(hWnd(), &pt);
                hDwp = DeferWindowPos(hDwp, hFSButton, 0,
                                      HorizontalOffset, pt.y, 0, 0,
                                      SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOOWNERZORDER);
            }
367 368 369 370

            EndDeferWindowPos(hDwp);
            break;
        }
371 372
        case WM_HSCROLL:
        case WM_VSCROLL: {
373
            if( VP() ){
374 375 376
                if(hVolumeSlider==(HWND)lParam){
                    LRESULT SliderPos = SendMessage(hVolumeSlider, (UINT) TBM_GETPOS, 0, 0);
                    SetVLCVolumeBySliderPos(SliderPos);
377 378 379 380 381
                }
            }
            break;
        }
        default:
382
            return VLCWnd::WindowProc(uMsg, wParam, lParam);
383 384
    }
    return 0L;
385 386
}

387
void VLCControlsWnd::RegisterToVLCEvents()
388
{
389
    VP()->get_mp().eventManager().onPositionChanged([this](float pos) {
390
        PostMessage(hVideoPosScroll, (UINT) PBM_SETPOS, (WPARAM)(pos * 1000), 0);
391
    });
392

393 394 395
    VP()->get_mp().eventManager().onPlaying([this] {
        PostMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM) IMAGE_BITMAP, (LPARAM) RC().hPauseBitmap);
    });
396

397 398 399
    VP()->get_mp().eventManager().onPaused([this] {
        PostMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM) IMAGE_BITMAP, (LPARAM) RC().hPlayBitmap);
    });
400 401 402

    VP()->get_mp().eventManager().onStopped([this] {
        PostMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM) IMAGE_BITMAP, (LPARAM) RC().hPlayBitmap);
403 404 405 406 407 408
        PostMessage(hVideoPosScroll, (UINT) PBM_SETPOS, (WPARAM)0, 0);
    });

    VP()->get_mp().eventManager().onEndReached([this] {
        PostMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM) IMAGE_BITMAP, (LPARAM) RC().hPlayBitmap);
        PostMessage(hVideoPosScroll, (UINT) PBM_SETPOS, (WPARAM)0, 0);
409
    });
410 411 412 413
}

void VLCControlsWnd::NeedShowControls()
{
414
    if( !(GetWindowLong(hWnd(), GWL_STYLE) & WS_VISIBLE) ) {
415 416
        if(WM().IsFullScreen() || (PO() && PO()->get_show_toolbar() ) )
            ShowWindow( hWnd(), SW_SHOW );
417
    }
418
    SetTimer(hWnd(), 1, 2 * 1000, nullptr);
419 420 421 422 423
}

void VLCControlsWnd::NeedHideControls()
{
    ShowWindow( hWnd(), SW_HIDE );
424
    KillTimer(hWnd(), 1);
425 426 427 428
}

void VLCControlsWnd::SetVideoPos(float Pos) //0-start, 1-end
{
429
    if( VP() ){
430
        VP()->get_mp().setPosition( Pos );
431 432

        if( VP()->get_mp().length() > 0 )
433
            PostMessage(hVideoPosScroll, (UINT)PBM_SETPOS, (WPARAM) (Pos * 1000), 0);
434 435 436 437 438
    }
}

void VLCControlsWnd::SyncVolumeSliderWithVLCVolume()
{
439 440
    if( VP() ){
        vlc_player& vp = *VP();
441
        unsigned int vol = vp.get_mp().volume();
442
        const LRESULT SliderPos = SendMessage(hVolumeSlider, (UINT) TBM_GETPOS, 0, 0);
443
        if((UINT)SliderPos!=vol)
444
            PostMessage(hVolumeSlider, (UINT) TBM_SETPOS, (WPARAM) TRUE, (LPARAM) vol);
445

446
        bool muted = vp.get_mp().mute();
447 448
        int MuteButtonState = SendMessage(hMuteButton, (UINT) BM_GETCHECK, 0, 0);
        if((muted&&(BST_UNCHECKED==MuteButtonState))||(!muted&&(BST_CHECKED==MuteButtonState))){
449
            PostMessage(hMuteButton, BM_SETCHECK, (WPARAM)(muted?BST_CHECKED:BST_UNCHECKED), 0);
450 451 452 453 454 455
        }
        LRESULT lResult = SendMessage(hMuteButton, BM_GETIMAGE, (WPARAM)IMAGE_BITMAP, 0);
        if( (muted && ((HANDLE)lResult == RC().hVolumeBitmap)) ||
            (!muted&&((HANDLE)lResult == RC().hVolumeMutedBitmap)) )
        {
            HANDLE hBmp = muted ? RC().hVolumeMutedBitmap : RC().hVolumeBitmap ;
456
            PostMessage(hMuteButton, BM_SETIMAGE,
457 458 459 460 461 462 463
                        (WPARAM)IMAGE_BITMAP, (LPARAM)hBmp);
        }
    }
}

void VLCControlsWnd::SetVLCVolumeBySliderPos(int CurPos)
{
464 465
    if( VP() ){
        vlc_player& vp = *VP();
466
        vp.get_mp().setVolume( CurPos );
467
        if(0==CurPos){
468
            vp.get_mp().setMute( IsDlgButtonChecked( hWnd(), ID_FS_MUTE) != FALSE );
469 470 471 472 473 474
        }
        SyncVolumeSliderWithVLCVolume();
    }
}


475
void VLCControlsWnd::UpdateFullscreenButton(bool fullscreen)
476
{
477 478 479 480
    if (fullscreen)
        PostMessage( hFSButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM) RC().hDeFullscreenBitmap );
    else
        PostMessage( hFSButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM) RC().hFullscreenBitmap );
481 482
}

483 484 485
////////////////////////////////////////////////////////////////////////////////
//VLCHolderWnd members
////////////////////////////////////////////////////////////////////////////////
486

487 488 489
VLCHolderWnd*
VLCHolderWnd::CreateHolderWindow(HINSTANCE hInstance,
                                 HWND hParentWnd, VLCWindowsManager* WM)
490
{
491 492 493
    VLCHolderWnd* wnd = new VLCHolderWnd(hInstance, WM);
    if( wnd && wnd->Create(hParentWnd) ) {
        return wnd;
494
    }
495 496
    delete wnd;
    return 0;
497 498
}

499
VLCHolderWnd::~VLCHolderWnd()
500
{
501 502 503 504
    if(_hBgBrush) {
        DeleteObject(_hBgBrush);
        _hBgBrush = 0;
    }
505 506
}

507
bool VLCHolderWnd::Create(HWND hWndParent)
508
{
509 510 511
    return VLCWnd::Create(TEXT("Holder Window"),
                            WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE,
                            0, 0, 0, 0, hWndParent, 0);
512 513
}

514
void VLCHolderWnd::PreRegisterWindowClass(WNDCLASS* wc)
515
{
516
    if( !_hBgBrush){
517
        unsigned r = 0, g = 0, b = 0;
518
        HTMLColor2RGB(PO()->get_bg_color().c_str(), &r, &g, &b);
519 520 521 522
        _hBgBrush = CreateSolidBrush(RGB(r, g, b));
    }

    wc->hbrBackground = _hBgBrush;
523 524
    wc->lpszClassName = TEXT("Web Plugin VLC Window Holder Class");
}
525

526 527
LRESULT VLCHolderWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
528 529 530 531 532 533 534
    switch( uMsg )
    {
        case WM_CREATE:{
            CREATESTRUCT* CreateStruct = (CREATESTRUCT*)(lParam);

            RECT ParentClientRect;
            GetClientRect(CreateStruct->hwndParent, &ParentClientRect);
535
            MoveWindow(hWnd(), 0, 0,
536 537
                       (ParentClientRect.right-ParentClientRect.left),
                       (ParentClientRect.bottom-ParentClientRect.top), FALSE);
538 539 540
            _CtrlsWnd =
                VLCControlsWnd::CreateControlsWindow(hInstance(), _wm,
                                                     hWnd());
541 542 543 544 545 546 547 548 549 550 551 552 553 554
            // This needs to access the media player HWND, therefore we need
            // to wait for the vout to be created
            VP()->get_mp().eventManager().onVout([this](int nbVout) {
                if ( nbVout == 0 )
                    return;
                HWND hwnd = FindMP_hWnd();
                if ( hwnd == nullptr )
                    return;
                DWORD s = GetWindowLong(hwnd, GWL_STYLE);
                s |= WS_CLIPSIBLINGS;
                SetWindowLong(hwnd, GWL_STYLE, s);
                // Now we can hook onto the media player's HWND:
                MouseHook(true);
            });
555 556 557 558
            break;
        }
        case WM_PAINT:{
            PAINTSTRUCT PaintStruct;
559
            HDC hDC = BeginPaint(hWnd(), &PaintStruct);
560 561 562 563 564 565 566 567
            if( PO() && PO()->get_enable_branding() )
            {
                RECT rect;
                GetClientRect(hWnd(), &rect);
                int IconX = ((rect.right - rect.left) - GetSystemMetrics(SM_CXICON))/2;
                int IconY = ((rect.bottom - rect.top) - GetSystemMetrics(SM_CYICON))/2;
                DrawIcon(hDC, IconX, IconY, RC().hBackgroundIcon);
            }
568
            EndPaint(hWnd(), &PaintStruct);
569 570
            break;
        }
571 572
        case WM_SHOWWINDOW:{
            if(FALSE!=wParam){ //showing
573
                _CtrlsWnd->NeedShowControls();
574 575 576 577
            }
            break;
        }
        case WM_SIZE:
578
            if(_CtrlsWnd){
579 580 581 582
                int new_client_width = LOWORD(lParam);
                int new_client_height = HIWORD(lParam);

                RECT rect;
583
                GetWindowRect(_CtrlsWnd->hWnd(), &rect);
584

585
                MoveWindow(_CtrlsWnd->hWnd(),
586 587 588 589
                           0, new_client_height - (rect.bottom - rect.top),
                           new_client_width, (rect.bottom-rect.top), TRUE);
            }
            break;
590
        case WM_MOUSEMOVE:
591 592 593 594 595 596 597
            if (_CtrlsWnd && (_oldMouseCoords.x != GET_X_LPARAM(lParam) ||
                _oldMouseCoords.y != GET_Y_LPARAM(lParam)))
            {
                _CtrlsWnd->NeedShowControls();
                _oldMouseCoords = MAKEPOINTS(lParam);
            }
            break;
598
        case WM_LBUTTONDBLCLK:
599
            WM().OnMouseEvent(uMsg);
600 601
            break;
        case WM_MOUSE_EVENT_NOTIFY:{
602 603 604 605
            // This is called synchronously, though handling it directly from the mouse hook
            // deadlocks (quite likely because we're destroying the windows we're being called from)
            // Just repost this asynchronously, and let the called know we received the message.
            PostMessage(hWnd(), WM_MOUSE_EVENT_REPOST, wParam, lParam);
606 607
            return WM_MOUSE_EVENT_NOTIFY_SUCCESS;
        }
608 609 610
        case WM_MOUSE_EVENT_REPOST:
            WM().OnMouseEvent(wParam);
            break;
611
        default:
612
            return VLCWnd::WindowProc(uMsg, wParam, lParam);
613 614
    }
    return 0;
615
}
616 617 618

void VLCHolderWnd::DestroyWindow()
{
619 620 621 622 623
    if(_CtrlsWnd){
        delete _CtrlsWnd;
        _CtrlsWnd = 0;
    }

624 625
    if(hWnd())
        ::DestroyWindow(hWnd());
626
}
627 628

LRESULT CALLBACK VLCHolderWnd::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
629
{
630 631 632 633 634 635
    bool AllowReceiveMessage = true;
    if(nCode >= 0){
        switch(wParam){
            case WM_MOUSEMOVE:
            case WM_LBUTTONDBLCLK:{
                MOUSEHOOKSTRUCT* mhs = reinterpret_cast<MOUSEHOOKSTRUCT*>(lParam);
636

637 638 639 640 641 642 643 644 645 646
                //try to find HolderWnd and notify it
                HWND hNotifyWnd = mhs->hwnd;
                LRESULT SMRes = ::SendMessage(hNotifyWnd, WM_MOUSE_EVENT_NOTIFY, wParam, 0);
                while( hNotifyWnd && WM_MOUSE_EVENT_NOTIFY_SUCCESS != SMRes){
                    hNotifyWnd = GetParent(hNotifyWnd);
                    SMRes = ::SendMessage(hNotifyWnd, WM_MOUSE_EVENT_NOTIFY, wParam, 0);
                }

                AllowReceiveMessage = WM_MOUSEMOVE==wParam || (WM_MOUSE_EVENT_NOTIFY_SUCCESS != SMRes);
                break;
647 648 649 650
            }
        }
    }

651 652 653 654 655
    LRESULT NHRes = CallNextHookEx(NULL, nCode, wParam, lParam);
    if(AllowReceiveMessage)
        return NHRes;
    else
        return 1;
656 657
}

658
void VLCHolderWnd::MouseHook(bool SetHook)
659
{
660
    if(SetHook){
661 662 663
        HWND hMPWnd = FindMP_hWnd();
        const DWORD WndThreadID = (hMPWnd) ? GetWindowThreadProcessId(hMPWnd, NULL) : 0;
        if( _hMouseHook &&( !hMPWnd || WndThreadID != _MouseHookThreadId) ){
664 665 666
            //unhook if something changed
            MouseHook(false);
        }
667

668
        if(!_hMouseHook && hMPWnd && WndThreadID){
669 670 671 672 673 674 675 676 677 678 679 680
            _MouseHookThreadId = WndThreadID;
            _hMouseHook =
                SetWindowsHookEx(WH_MOUSE, VLCHolderWnd::MouseHookProc,
                                 NULL, WndThreadID);
        }
    }
    else{
        if(_hMouseHook){
            UnhookWindowsHookEx(_hMouseHook);
            _MouseHookThreadId=0;
            _hMouseHook = 0;
        }
681 682 683
    }
}

684 685 686 687 688
HWND VLCHolderWnd::FindMP_hWnd()
{
    if(_CtrlsWnd)
        return GetWindow(_CtrlsWnd->hWnd(), GW_HWNDNEXT);
    else
689
        return GetWindow(hWnd(), GW_CHILD);
690 691
}

692 693 694 695 696
////////////////////////////////////////////////////////////////////////////////
//VLCFullScreenWnd members
////////////////////////////////////////////////////////////////////////////////
HINSTANCE VLCFullScreenWnd::_hinstance = 0;
ATOM VLCFullScreenWnd::_fullscreen_wndclass_atom = 0;
697

698
void VLCFullScreenWnd::RegisterWndClassName(HINSTANCE hInstance)
699
{
700 701
    //save hInstance for future use
    _hinstance = hInstance;
702

703
    WNDCLASS wClass;
704

705 706 707 708 709 710 711 712 713 714 715 716 717
    memset(&wClass, 0 , sizeof(wClass));
    if( ! GetClassInfo(_hinstance,  getClassName(), &wClass) )
    {
        wClass.style          = CS_NOCLOSE|CS_DBLCLKS;
        wClass.lpfnWndProc    = FSWndWindowProc;
        wClass.cbClsExtra     = 0;
        wClass.cbWndExtra     = 0;
        wClass.hInstance      = _hinstance;
        wClass.hIcon          = NULL;
        wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wClass.hbrBackground  = (HBRUSH)(COLOR_3DFACE+1);
        wClass.lpszMenuName   = NULL;
        wClass.lpszClassName  = getClassName();
718

719 720 721 722 723 724
        _fullscreen_wndclass_atom = RegisterClass(&wClass);
    }
    else
    {
        _fullscreen_wndclass_atom = 0;
    }
725 726

}
727
void VLCFullScreenWnd::UnRegisterWndClassName()
728
{
729 730 731 732
    if(0 != _fullscreen_wndclass_atom){
        UnregisterClass(MAKEINTATOM(_fullscreen_wndclass_atom), _hinstance);
        _fullscreen_wndclass_atom = 0;
    }
733 734
}

735
LRESULT CALLBACK VLCFullScreenWnd::FSWndWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
736
{
737 738 739
    VLCFullScreenWnd* fs_data = reinterpret_cast<VLCFullScreenWnd *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));

    switch( uMsg )
740
    {
741 742 743 744 745 746 747
        case WM_CREATE:{
            CREATESTRUCT* CreateStruct = (CREATESTRUCT*)(lParam);
            VLCWindowsManager* WM = (VLCWindowsManager*)CreateStruct->lpCreateParams;

            fs_data = new VLCFullScreenWnd(hWnd, WM);
            SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(fs_data));

748
            break;
749 750 751 752
        }
        case WM_NCDESTROY:
            delete fs_data;
            SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
753
            break;
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
        case WM_SHOWWINDOW:{
            if(FALSE==wParam){ //hiding
                break;
            }

            //simulate lParam for WM_SIZE
            RECT ClientRect;
            GetClientRect(hWnd, &ClientRect);
            lParam = MAKELPARAM(ClientRect.right, ClientRect.bottom);
        }
        case WM_SIZE:{
            if(fs_data->_WindowsManager->IsFullScreen()){
                int new_client_width = LOWORD(lParam);
                int new_client_height = HIWORD(lParam);
                VLCHolderWnd* HolderWnd =  fs_data->_WindowsManager->getHolderWnd();
769 770 771
                SetWindowPos(HolderWnd->hWnd(), HWND_BOTTOM, 0, 0,
                             new_client_width, new_client_height,
                             SWP_NOACTIVATE|SWP_NOOWNERZORDER);
772
            }
773
            break;
774
        }
775 776 777 778 779
        case WM_KEYDOWN: {
            if (fs_data)
                fs_data->_WindowsManager->OnKeyDownEvent(wParam);
            break;
        }
780 781
        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
782
    }
783 784
    return 0L;
};
785

786
VLCFullScreenWnd* VLCFullScreenWnd::CreateFSWindow(VLCWindowsManager* WM)
787
{
788
    HWND hWnd = CreateWindow(getClassName(),
789
                TEXT("VLC Full Screen Window"),
790 791 792 793 794 795 796 797 798
                WS_POPUP|WS_CLIPCHILDREN,
                0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
                0,
                0,
                VLCFullScreenWnd::_hinstance,
                (LPVOID)WM
                );
    if(hWnd)
        return reinterpret_cast<VLCFullScreenWnd*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
799

800
    return 0;
801 802 803 804 805
}

///////////////////////
//VLCWindowsManager
///////////////////////
806
VLCWindowsManager::VLCWindowsManager(HMODULE hModule, const VLCViewResources& rc,
807 808
                                     vlc_player* player, const vlc_player_options* po)
    :_rc(rc), _hModule(hModule), _po(po), _hWindowedParentWnd(0), _vp(player),
809
    _HolderWnd(0), _FSWnd(0), _b_new_messages_flag(false), Last_WM_MOUSEMOVE_Pos(0)
810 811 812 813 814 815 816 817 818 819 820 821 822 823
{
    VLCFullScreenWnd::RegisterWndClassName(hModule);
}

VLCWindowsManager::~VLCWindowsManager()
{
    VLCFullScreenWnd::UnRegisterWndClassName();
}

void VLCWindowsManager::CreateWindows(HWND hWindowedParentWnd)
{
    _hWindowedParentWnd = hWindowedParentWnd;

    if(!_HolderWnd){
824 825 826
        _HolderWnd =
            VLCHolderWnd::CreateHolderWindow(getHModule(),
                                             hWindowedParentWnd, this);
827 828 829 830 831
    }
}

void VLCWindowsManager::DestroyWindows()
{
832 833
    if(_HolderWnd){
        _HolderWnd->DestroyWindow();
834 835
        delete _HolderWnd;
        _HolderWnd = 0;
836
    }
837 838 839 840 841

    if(_FSWnd){
        _FSWnd->DestroyWindow();
    }
    _FSWnd = 0;
842 843 844 845
}

void VLCWindowsManager::StartFullScreen()
{
846
    if( !_HolderWnd || ( PO() && !PO()->get_enable_fs() ) )
847 848
        return;//VLCWindowsManager::CreateWindows was not called

849 850
    if( VP() && !IsFullScreen() ){
        if( !_FSWnd ){
851 852 853
            _FSWnd= VLCFullScreenWnd::CreateFSWindow(this);
        }

854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
        RECT FSRect = { 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) };

        HMONITOR hMonitor = MonitorFromWindow(_hWindowedParentWnd, MONITOR_DEFAULTTONEAREST);
        MONITORINFO MonInfo;
        memset(&MonInfo, 0, sizeof(MonInfo));
        MonInfo.cbSize = sizeof(MonInfo);
        if( GetMonitorInfo(hMonitor, &MonInfo) ) {
            FSRect = MonInfo.rcMonitor;
        }

#ifdef _DEBUG
        //to simplify debugging in fullscreen mode
        UINT FSFlags = SWP_NOZORDER;
#else
        UINT FSFlags = 0;
#endif

871
        SetParent(_HolderWnd->hWnd(), _FSWnd->getHWND());
872 873 874 875
        SetWindowPos(_FSWnd->getHWND(), HWND_TOPMOST,
                     FSRect.left, FSRect.top,
                     FSRect.right - FSRect.left, FSRect.bottom - FSRect.top,
                     FSFlags);
876 877 878 879 880 881 882

        ShowWindow(_FSWnd->getHWND(), SW_SHOW);
    }
}

void VLCWindowsManager::EndFullScreen()
{
883 884 885
    if(!_HolderWnd)
        return;//VLCWindowsManager::CreateWindows was not called

886
    if(IsFullScreen()){
887
        SetParent(_HolderWnd->hWnd(), _hWindowedParentWnd);
888 889 890

        RECT WindowedParentRect;
        GetClientRect(_hWindowedParentWnd, &WindowedParentRect);
891 892
        MoveWindow(_HolderWnd->hWnd(), 0, 0,
                   WindowedParentRect.right, WindowedParentRect.bottom, FALSE);
893 894

        ShowWindow(_FSWnd->getHWND(), SW_HIDE);
895

896 897
        _FSWnd->DestroyWindow();
        _FSWnd = nullptr;
898
   }
899 900 901 902
}

void VLCWindowsManager::ToggleFullScreen()
{
903 904
    auto isFullscreen = IsFullScreen();
    if ( isFullscreen ){
905 906 907 908 909
        EndFullScreen();
    }
    else{
        StartFullScreen();
    }
910
    _HolderWnd->ControlWindow()->UpdateFullscreenButton( !isFullscreen );
911 912 913 914
}

bool VLCWindowsManager::IsFullScreen()
{
915
    return 0!=_FSWnd && 0!=_HolderWnd && GetParent(_HolderWnd->hWnd())==_FSWnd->getHWND();
916 917
}

918 919 920 921 922 923
void VLCWindowsManager::OnKeyDownEvent(UINT uKeyMsg)
{
    switch(uKeyMsg){
        case VK_ESCAPE:
        case 'F':
            EndFullScreen();
924
            _HolderWnd->ControlWindow()->UpdateFullscreenButton( FALSE );
925 926 927 928
            break;
    }
}

929 930 931 932 933 934 935 936 937 938
void VLCWindowsManager::OnMouseEvent(UINT uMouseMsg)
{
    switch(uMouseMsg){
        case WM_LBUTTONDBLCLK:
            ToggleFullScreen();
            break;
        case WM_MOUSEMOVE:{
            DWORD MsgPos = GetMessagePos();
            if(Last_WM_MOUSEMOVE_Pos != MsgPos){
                Last_WM_MOUSEMOVE_Pos = MsgPos;
939
                _HolderWnd->ControlWindow()->NeedShowControls();
940 941 942 943 944
            }
            break;
        }
    }
}
945