Bug 1160014 part 4 - Implement fullscreen transition on Windows. r=jimm

This commit is contained in:
Xidorn Quan 2015-07-11 10:08:59 +10:00
parent 07678e24c1
commit 48b75ae6e3
3 changed files with 192 additions and 4 deletions

View File

@ -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<nsIRunnable> callback =
already_AddRefed<nsIRunnable>((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<FullscreenTransitionInitData*>(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<nsIScreen> 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<FullscreenTransitionData*>(aData);
nsCOMPtr<nsIRunnable> 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)
{

View File

@ -126,6 +126,11 @@ public:
uint32_t aHotspotX, uint32_t aHotspotY);
NS_IMETHOD SetCursor(nsCursor aCursor);
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& 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;

View File

@ -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";
/**************************************************************
*