1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/platform_thread.h"
8 #include "WinCompositorWindowThread.h"
9 #include "mozilla/gfx/Logging.h"
10 #include "mozilla/layers/SynchronousTask.h"
11 #include "mozilla/StaticPtr.h"
12 #include "transport/runnable_utils.h"
13 #include "mozilla/StaticPrefs_apz.h"
16 # define WS_EX_NOREDIRECTIONBITMAP 0x00200000L
22 static StaticRefPtr
<WinCompositorWindowThread
> sWinCompositorWindowThread
;
24 /// A window procedure that logs when an input event is received to the gfx
27 /// This is done because this window is supposed to be WM_DISABLED, but
28 /// malfunctioning software may still end up targetting this window. If that
29 /// happens, it's almost-certainly a bug and should be brought to the attention
30 /// of the developers that are debugging the issue.
31 static LRESULT CALLBACK
InputEventRejectingWindowProc(HWND window
, UINT msg
,
49 << "The compositor window received an input event even though it's "
50 "disabled. There is likely malfunctioning "
51 "software on the user's machine.";
57 return ::DefWindowProcW(window
, msg
, wparam
, lparam
);
60 WinCompositorWindowThread::WinCompositorWindowThread(base::Thread
* aThread
)
61 : mThread(aThread
), mMonitor("WinCompositorWindowThread") {}
64 WinCompositorWindowThread
* WinCompositorWindowThread::Get() {
65 if (!sWinCompositorWindowThread
||
66 sWinCompositorWindowThread
->mHasAttemptedShutdown
) {
69 return sWinCompositorWindowThread
;
73 void WinCompositorWindowThread::Start() {
74 MOZ_ASSERT(NS_IsMainThread());
76 base::Thread::Options options
;
77 // HWND requests ui thread.
78 options
.message_loop_type
= MessageLoop::TYPE_UI
;
80 if (sWinCompositorWindowThread
) {
81 // Try to reuse the thread, which involves stopping and restarting it.
82 sWinCompositorWindowThread
->mThread
->Stop();
83 if (sWinCompositorWindowThread
->mThread
->StartWithOptions(options
)) {
85 sWinCompositorWindowThread
->mHasAttemptedShutdown
= false;
88 // Restart failed, so null out our sWinCompositorWindowThread and
89 // try again with a new thread. This will cause the old singleton
90 // instance to be deallocated, which will destroy its mThread as well.
91 sWinCompositorWindowThread
= nullptr;
94 base::Thread
* thread
= new base::Thread("WinCompositor");
95 if (!thread
->StartWithOptions(options
)) {
100 sWinCompositorWindowThread
= new WinCompositorWindowThread(thread
);
104 void WinCompositorWindowThread::ShutDown() {
105 MOZ_ASSERT(NS_IsMainThread());
106 MOZ_ASSERT(sWinCompositorWindowThread
);
108 sWinCompositorWindowThread
->mHasAttemptedShutdown
= true;
110 // Our thread could hang while we're waiting for it to stop.
111 // Since we're shutting down, that's not a critical problem.
112 // We set a reasonable amount of time to wait for shutdown,
113 // and if it succeeds within that time, we correctly stop
114 // our thread by nulling out the refptr, which will cause it
115 // to be deallocated and join the thread. If it times out,
116 // we do nothing, which means that the thread will not be
117 // joined and sWinCompositorWindowThread memory will leak.
120 // It's important to hold the lock before posting the
121 // runnable. This ensures that the runnable can't begin
122 // until we've started our Wait, which prevents us from
123 // Waiting on a monitor that has already been notified.
124 MonitorAutoLock
lock(sWinCompositorWindowThread
->mMonitor
);
126 static const TimeDuration TIMEOUT
= TimeDuration::FromSeconds(2.0);
127 RefPtr
<Runnable
> runnable
=
128 NewRunnableMethod("WinCompositorWindowThread::ShutDownTask",
129 sWinCompositorWindowThread
.get(),
130 &WinCompositorWindowThread::ShutDownTask
);
131 Loop()->PostTask(runnable
.forget());
133 // Monitor uses SleepConditionVariableSRW, which can have
134 // spurious wakeups which are reported as timeouts, so we
135 // check timestamps to ensure that we've waited as long we
136 // intended to. If we wake early, we don't bother calculating
137 // a precise amount for the next wait; we just wait the same
138 // amount of time. This means timeout might happen after as
139 // much as 2x the TIMEOUT time.
140 TimeStamp timeStart
= TimeStamp::NowLoRes();
142 status
= sWinCompositorWindowThread
->mMonitor
.Wait(TIMEOUT
);
143 } while ((status
== CVStatus::Timeout
) &&
144 ((TimeStamp::NowLoRes() - timeStart
) < TIMEOUT
));
147 if (status
== CVStatus::NoTimeout
) {
148 sWinCompositorWindowThread
= nullptr;
152 void WinCompositorWindowThread::ShutDownTask() {
153 MonitorAutoLock
lock(mMonitor
);
155 MOZ_ASSERT(IsInCompositorWindowThread());
156 mMonitor
.NotifyAll();
160 MessageLoop
* WinCompositorWindowThread::Loop() {
161 return sWinCompositorWindowThread
162 ? sWinCompositorWindowThread
->mThread
->message_loop()
167 bool WinCompositorWindowThread::IsInCompositorWindowThread() {
168 return sWinCompositorWindowThread
&&
169 sWinCompositorWindowThread
->mThread
->thread_id() ==
170 PlatformThread::CurrentId();
173 const wchar_t kClassNameCompositorInitalParent
[] =
174 L
"MozillaCompositorInitialParentClass";
175 const wchar_t kClassNameCompositor
[] = L
"MozillaCompositorWindowClass";
177 ATOM g_compositor_inital_parent_window_class
;
178 ATOM g_compositor_window_class
;
180 // This runs on the window owner thread.
181 void InitializeInitialParentWindowClass() {
182 if (g_compositor_inital_parent_window_class
) {
188 wc
.lpfnWndProc
= ::DefWindowProcW
;
191 wc
.hInstance
= GetModuleHandle(nullptr);
193 wc
.hCursor
= nullptr;
194 wc
.hbrBackground
= nullptr;
195 wc
.lpszMenuName
= nullptr;
196 wc
.lpszClassName
= kClassNameCompositorInitalParent
;
197 g_compositor_inital_parent_window_class
= ::RegisterClassW(&wc
);
200 // This runs on the window owner thread.
201 void InitializeWindowClass() {
202 if (g_compositor_window_class
) {
208 wc
.lpfnWndProc
= InputEventRejectingWindowProc
;
211 wc
.hInstance
= GetModuleHandle(nullptr);
213 wc
.hCursor
= nullptr;
214 wc
.hbrBackground
= nullptr;
215 wc
.lpszMenuName
= nullptr;
216 wc
.lpszClassName
= kClassNameCompositor
;
217 g_compositor_window_class
= ::RegisterClassW(&wc
);
221 WinCompositorWnds
WinCompositorWindowThread::CreateCompositorWindow() {
225 return WinCompositorWnds(nullptr, nullptr);
228 layers::SynchronousTask
task("Create compositor window");
230 HWND initialParentWnd
= nullptr;
231 HWND compositorWnd
= nullptr;
233 RefPtr
<Runnable
> runnable
= NS_NewRunnableFunction(
234 "WinCompositorWindowThread::CreateCompositorWindow::Runnable", [&]() {
235 layers::AutoCompleteTask
complete(&task
);
237 InitializeInitialParentWindowClass();
238 InitializeWindowClass();
240 // Create initial parent window.
241 // We could not directly create a compositor window with a main window
242 // as parent window, so instead create it with a temporary placeholder
243 // parent. Its parent is set as main window in UI process.
245 ::CreateWindowEx(WS_EX_TOOLWINDOW
, kClassNameCompositorInitalParent
,
246 nullptr, WS_POPUP
| WS_DISABLED
, 0, 0, 1, 1,
247 nullptr, 0, GetModuleHandle(nullptr), 0);
248 if (!initialParentWnd
) {
249 gfxCriticalNoteOnce
<< "Inital parent window failed "
254 DWORD extendedStyle
= WS_EX_NOPARENTNOTIFY
| WS_EX_NOREDIRECTIONBITMAP
;
256 if (!StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
257 extendedStyle
|= WS_EX_LAYERED
| WS_EX_TRANSPARENT
;
260 compositorWnd
= ::CreateWindowEx(
261 extendedStyle
, kClassNameCompositor
, nullptr,
262 WS_CHILDWINDOW
| WS_DISABLED
| WS_VISIBLE
, 0, 0, 1, 1,
263 initialParentWnd
, 0, GetModuleHandle(nullptr), 0);
264 if (!compositorWnd
) {
265 gfxCriticalNoteOnce
<< "Compositor window failed "
270 Loop()->PostTask(runnable
.forget());
274 return WinCompositorWnds(compositorWnd
, initialParentWnd
);
278 void WinCompositorWindowThread::DestroyCompositorWindow(
279 WinCompositorWnds aWnds
) {
280 MOZ_ASSERT(aWnds
.mCompositorWnd
);
281 MOZ_ASSERT(aWnds
.mInitialParentWnd
);
288 RefPtr
<Runnable
> runnable
= NS_NewRunnableFunction(
289 "WinCompositorWidget::CreateNativeWindow::Runnable", [aWnds
]() {
290 ::DestroyWindow(aWnds
.mCompositorWnd
);
291 ::DestroyWindow(aWnds
.mInitialParentWnd
);
294 Loop()->PostTask(runnable
.forget());
297 } // namespace widget
298 } // namespace mozilla