From d381dcaca63d33ca1796c427b430c40c7bc4e376 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sat, 11 Jul 2015 10:08:59 +1000 Subject: [PATCH] Bug 1160014 part 4 - Implement fullscreen transition on Windows. r=jimm --HG-- extra : source : ac50a37a67b04f715f9faee1720e6bf8158bd83e --- widget/windows/nsWindow.cpp | 177 ++++++++++++++++++++++++++++++++++ widget/windows/nsWindow.h | 6 ++ widget/windows/nsWindowDefs.h | 13 ++- 3 files changed, 192 insertions(+), 4 deletions(-) diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 28492c2fc7e4..9db2b972361f 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -325,6 +325,7 @@ nsWindow::nsWindow() : nsWindowBase() mIconSmall = nullptr; mIconBig = nullptr; mWnd = nullptr; + mTransitionWnd = nullptr; mPaintDC = nullptr; mCompositeDC = nullptr; mPrevWndProc = nullptr; @@ -1521,6 +1522,15 @@ NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, ClearThemeRegion(); VERIFY(::SetWindowPos(mWnd, nullptr, x, y, width, GetHeight(height), flags)); + if (mTransitionWnd) { + // If we have a fullscreen transition window, we need to make + // it topmost again, otherwise the taskbar may be raised by + // the system unexpectedly when we leave fullscreen state. + ::SetWindowPos(mTransitionWnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + // Every transition window is only used once. + mTransitionWnd = nullptr; + } SetThemeRegion(); } @@ -2848,6 +2858,173 @@ NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect) return NS_OK; } +static LRESULT CALLBACK +FullscreenTransitionWindowProc(HWND hWnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_FULLSCREEN_TRANSITION_BEFORE: + case WM_FULLSCREEN_TRANSITION_AFTER: { + // The message sender should have added ref for us. + nsCOMPtr callback = + already_AddRefed((nsIRunnable*)wParam); + DWORD duration = (DWORD)lParam; + DWORD flags = AW_BLEND; + if (uMsg == WM_FULLSCREEN_TRANSITION_AFTER) { + flags |= AW_HIDE; + } + ::AnimateWindow(hWnd, duration, flags); + NS_DispatchToMainThread(callback); + break; + } + case WM_DESTROY: + ::PostQuitMessage(0); + break; + default: + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + return 0; +} + +struct FullscreenTransitionInitData +{ + nsIntRect mBounds; + HANDLE mSemaphore; + HANDLE mThread; + HWND mWnd; + + FullscreenTransitionInitData() + : mSemaphore(nullptr) + , mThread(nullptr) + , mWnd(nullptr) { } + + ~FullscreenTransitionInitData() + { + if (mSemaphore) { + ::CloseHandle(mSemaphore); + } + if (mThread) { + ::CloseHandle(mThread); + } + } +}; + +static DWORD WINAPI +FullscreenTransitionThreadProc(LPVOID lpParam) +{ + // Initialize window class + static bool sInitialized = false; + if (!sInitialized) { + WNDCLASSW wc = {}; + wc.lpfnWndProc = ::FullscreenTransitionWindowProc; + wc.hInstance = nsToolkit::mDllInstance; + wc.hbrBackground = ::CreateSolidBrush(RGB(0, 0, 0)); + wc.lpszClassName = kClassNameTransition; + ::RegisterClassW(&wc); + sInitialized = true; + } + + auto data = static_cast(lpParam); + HWND wnd = ::CreateWindowW( + kClassNameTransition, L"", 0, 0, 0, 0, 0, + nullptr, nullptr, nsToolkit::mDllInstance, nullptr); + if (!wnd) { + ::ReleaseSemaphore(data->mSemaphore, 1, nullptr); + return 0; + } + + // Since AnimateWindow blocks the thread of the transition window, + // we need to hide the cursor for that window, otherwise the system + // would show the busy pointer to the user. + ::ShowCursor(false); + ::SetWindowLongW(wnd, GWL_STYLE, 0); + ::SetWindowLongW(wnd, GWL_EXSTYLE, WS_EX_LAYERED | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE); + ::SetWindowPos(wnd, HWND_TOPMOST, data->mBounds.x, data->mBounds.y, + data->mBounds.width, data->mBounds.height, 0); + data->mWnd = wnd; + ::ReleaseSemaphore(data->mSemaphore, 1, nullptr); + // The initialization data may no longer be valid + // after we release the semaphore. + data = nullptr; + + MSG msg; + while (::GetMessageW(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + ::ShowCursor(true); + ::DestroyWindow(wnd); + return 0; +} + +class FullscreenTransitionData final : public nsISupports +{ +public: + NS_DECL_ISUPPORTS + + explicit FullscreenTransitionData(HWND aWnd) + : mWnd(aWnd) + { + MOZ_ASSERT(NS_IsMainThread(), "FullscreenTransitionData " + "should be constructed in the main thread"); + } + + const HWND mWnd; + +private: + ~FullscreenTransitionData() + { + MOZ_ASSERT(NS_IsMainThread(), "FullscreenTransitionData " + "should be deconstructed in the main thread"); + ::PostMessageW(mWnd, WM_DESTROY, 0, 0); + } +}; + +NS_IMPL_ISUPPORTS0(FullscreenTransitionData) + +/* virtual */ bool +nsWindow::PrepareForFullscreenTransition(nsISupports** aData) +{ + FullscreenTransitionInitData initData; + nsCOMPtr screen = GetWidgetScreen(); + screen->GetRectDisplayPix(&initData.mBounds.x, &initData.mBounds.y, + &initData.mBounds.width, &initData.mBounds.height); + // Create a semaphore for synchronizing the window handle which will + // be created by the transition thread and used by the main thread for + // posting the transition messages. + initData.mSemaphore = ::CreateSemaphore(nullptr, 0, 1, nullptr); + if (initData.mSemaphore) { + initData.mThread = ::CreateThread( + nullptr, 0, FullscreenTransitionThreadProc, &initData, 0, nullptr); + if (initData.mThread) { + ::WaitForSingleObject(initData.mSemaphore, INFINITE); + } + } + if (!initData.mWnd) { + return false; + } + + mTransitionWnd = initData.mWnd; + auto data = new FullscreenTransitionData(initData.mWnd); + *aData = data; + NS_ADDREF(data); + return true; +} + +/* virtual */ void +nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage, + uint16_t aDuration, nsISupports* aData, + nsIRunnable* aCallback) +{ + auto data = static_cast(aData); + nsCOMPtr callback = aCallback; + UINT msg = aStage == eBeforeFullscreenToggle ? + WM_FULLSCREEN_TRANSITION_BEFORE : WM_FULLSCREEN_TRANSITION_AFTER; + WPARAM wparam = (WPARAM)callback.forget().take(); + ::PostMessage(data->mWnd, msg, wparam, (LPARAM)aDuration); +} + NS_IMETHODIMP nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 6766c16f8cd5..60050f272d37 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -126,6 +126,11 @@ public: uint32_t aHotspotX, uint32_t aHotspotY); NS_IMETHOD SetCursor(nsCursor aCursor); virtual nsresult ConfigureChildren(const nsTArray& aConfigurations); + virtual bool PrepareForFullscreenTransition(nsISupports** aData) override; + virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, + uint16_t aDuration, + nsISupports* aData, + nsIRunnable* aCallback) override; NS_IMETHOD MakeFullScreen(bool aFullScreen, nsIScreen* aScreen = nullptr); NS_IMETHOD HideWindowChrome(bool aShouldHide); NS_IMETHOD Invalidate(bool aEraseBackground = false, @@ -463,6 +468,7 @@ protected: nsIntSize mLastSize; nsIntPoint mLastPoint; HWND mWnd; + HWND mTransitionWnd; WNDPROC mPrevWndProc; HBRUSH mBrush; bool mIsTopWidgetWindow; diff --git a/widget/windows/nsWindowDefs.h b/widget/windows/nsWindowDefs.h index 5141b63f5909..c64588162f7c 100644 --- a/widget/windows/nsWindowDefs.h +++ b/widget/windows/nsWindowDefs.h @@ -91,16 +91,16 @@ #endif #ifndef WM_DWMCOMPOSITIONCHANGED -#define WM_DWMCOMPOSITIONCHANGED 0x031E +#define WM_DWMCOMPOSITIONCHANGED 0x031E #endif #ifndef WM_DWMNCRENDERINGCHANGED -#define WM_DWMNCRENDERINGCHANGED 0x031F +#define WM_DWMNCRENDERINGCHANGED 0x031F #endif #ifndef WM_DWMCOLORIZATIONCOLORCHANGED -#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 #endif #ifndef WM_DWMWINDOWMAXIMIZEDCHANGE -#define WM_DWMWINDOWMAXIMIZEDCHANGE 0x0321 +#define WM_DWMWINDOWMAXIMIZEDCHANGE 0x0321 #endif // ConstrainPosition window positioning slop value @@ -183,6 +183,10 @@ #define TABLET_INK_TOUCH 0x00000080 #define MOUSE_INPUT_SOURCE() WinUtils::GetMouseInputSource() +// Messages for fullscreen transition window +#define WM_FULLSCREEN_TRANSITION_BEFORE (WM_USER + 0) +#define WM_FULLSCREEN_TRANSITION_AFTER (WM_USER + 1) + /************************************************************** * * SECTION: enums @@ -218,6 +222,7 @@ const wchar_t kClassNameGeneral[] = L"MozillaWindowClass"; const wchar_t kClassNameDialog[] = L"MozillaDialogClass"; const wchar_t kClassNameDropShadow[] = L"MozillaDropShadowWindowClass"; const wchar_t kClassNameTemp[] = L"MozillaTempWindowClass"; +const wchar_t kClassNameTransition[] = L"MozillaTransitionWindowClass"; /************************************************************** *