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"
18 static StaticRefPtr
<WinCompositorWindowThread
> sWinCompositorWindowThread
;
20 /// A window procedure that logs when an input event is received to the gfx
23 /// This is done because this window is supposed to be WM_DISABLED, but
24 /// malfunctioning software may still end up targetting this window. If that
25 /// happens, it's almost-certainly a bug and should be brought to the attention
26 /// of the developers that are debugging the issue.
27 static LRESULT CALLBACK
InputEventRejectingWindowProc(HWND window
, UINT msg
,
45 << "The compositor window received an input event even though it's "
46 "disabled. There is likely malfunctioning "
47 "software on the user's machine.";
53 return ::DefWindowProcW(window
, msg
, wparam
, lparam
);
56 WinCompositorWindowThread::WinCompositorWindowThread(base::Thread
* aThread
)
57 : mThread(aThread
), mMonitor("WinCompositorWindowThread") {}
60 WinCompositorWindowThread
* WinCompositorWindowThread::Get() {
61 if (!sWinCompositorWindowThread
||
62 sWinCompositorWindowThread
->mHasAttemptedShutdown
) {
65 return sWinCompositorWindowThread
;
69 void WinCompositorWindowThread::Start() {
70 MOZ_ASSERT(NS_IsMainThread());
72 base::Thread::Options options
;
73 // HWND requests ui thread.
74 options
.message_loop_type
= MessageLoop::TYPE_UI
;
76 if (sWinCompositorWindowThread
) {
77 // Try to reuse the thread, which involves stopping and restarting it.
78 sWinCompositorWindowThread
->mThread
->Stop();
79 if (sWinCompositorWindowThread
->mThread
->StartWithOptions(options
)) {
81 sWinCompositorWindowThread
->mHasAttemptedShutdown
= false;
84 // Restart failed, so null out our sWinCompositorWindowThread and
85 // try again with a new thread. This will cause the old singleton
86 // instance to be deallocated, which will destroy its mThread as well.
87 sWinCompositorWindowThread
= nullptr;
90 base::Thread
* thread
= new base::Thread("WinCompositor");
91 if (!thread
->StartWithOptions(options
)) {
96 sWinCompositorWindowThread
= new WinCompositorWindowThread(thread
);
100 void WinCompositorWindowThread::ShutDown() {
101 MOZ_ASSERT(NS_IsMainThread());
102 MOZ_ASSERT(sWinCompositorWindowThread
);
104 sWinCompositorWindowThread
->mHasAttemptedShutdown
= true;
106 // Our thread could hang while we're waiting for it to stop.
107 // Since we're shutting down, that's not a critical problem.
108 // We set a reasonable amount of time to wait for shutdown,
109 // and if it succeeds within that time, we correctly stop
110 // our thread by nulling out the refptr, which will cause it
111 // to be deallocated and join the thread. If it times out,
112 // we do nothing, which means that the thread will not be
113 // joined and sWinCompositorWindowThread memory will leak.
116 // It's important to hold the lock before posting the
117 // runnable. This ensures that the runnable can't begin
118 // until we've started our Wait, which prevents us from
119 // Waiting on a monitor that has already been notified.
120 MonitorAutoLock
lock(sWinCompositorWindowThread
->mMonitor
);
122 static const TimeDuration TIMEOUT
= TimeDuration::FromSeconds(2.0);
123 RefPtr
<Runnable
> runnable
=
124 NewRunnableMethod("WinCompositorWindowThread::ShutDownTask",
125 sWinCompositorWindowThread
.get(),
126 &WinCompositorWindowThread::ShutDownTask
);
127 Loop()->PostTask(runnable
.forget());
129 // Monitor uses SleepConditionVariableSRW, which can have
130 // spurious wakeups which are reported as timeouts, so we
131 // check timestamps to ensure that we've waited as long we
132 // intended to. If we wake early, we don't bother calculating
133 // a precise amount for the next wait; we just wait the same
134 // amount of time. This means timeout might happen after as
135 // much as 2x the TIMEOUT time.
136 TimeStamp timeStart
= TimeStamp::NowLoRes();
138 status
= sWinCompositorWindowThread
->mMonitor
.Wait(TIMEOUT
);
139 } while ((status
== CVStatus::Timeout
) &&
140 ((TimeStamp::NowLoRes() - timeStart
) < TIMEOUT
));
143 if (status
== CVStatus::NoTimeout
) {
144 sWinCompositorWindowThread
= nullptr;
148 void WinCompositorWindowThread::ShutDownTask() {
149 MonitorAutoLock
lock(mMonitor
);
151 MOZ_ASSERT(IsInCompositorWindowThread());
152 mMonitor
.NotifyAll();
156 MessageLoop
* WinCompositorWindowThread::Loop() {
157 return sWinCompositorWindowThread
158 ? sWinCompositorWindowThread
->mThread
->message_loop()
163 bool WinCompositorWindowThread::IsInCompositorWindowThread() {
164 return sWinCompositorWindowThread
&&
165 sWinCompositorWindowThread
->mThread
->thread_id() ==
166 PlatformThread::CurrentId();
169 const wchar_t kClassNameCompositorInitalParent
[] =
170 L
"MozillaCompositorInitialParentClass";
171 const wchar_t kClassNameCompositor
[] = L
"MozillaCompositorWindowClass";
173 ATOM g_compositor_inital_parent_window_class
;
174 ATOM g_compositor_window_class
;
176 // This runs on the window owner thread.
177 void InitializeInitialParentWindowClass() {
178 if (g_compositor_inital_parent_window_class
) {
184 wc
.lpfnWndProc
= ::DefWindowProcW
;
187 wc
.hInstance
= GetModuleHandle(nullptr);
189 wc
.hCursor
= nullptr;
190 wc
.hbrBackground
= nullptr;
191 wc
.lpszMenuName
= nullptr;
192 wc
.lpszClassName
= kClassNameCompositorInitalParent
;
193 g_compositor_inital_parent_window_class
= ::RegisterClassW(&wc
);
196 // This runs on the window owner thread.
197 void InitializeWindowClass() {
198 if (g_compositor_window_class
) {
204 wc
.lpfnWndProc
= InputEventRejectingWindowProc
;
207 wc
.hInstance
= GetModuleHandle(nullptr);
209 wc
.hCursor
= nullptr;
210 wc
.hbrBackground
= nullptr;
211 wc
.lpszMenuName
= nullptr;
212 wc
.lpszClassName
= kClassNameCompositor
;
213 g_compositor_window_class
= ::RegisterClassW(&wc
);
217 WinCompositorWnds
WinCompositorWindowThread::CreateCompositorWindow() {
221 return WinCompositorWnds(nullptr, nullptr);
224 layers::SynchronousTask
task("Create compositor window");
226 HWND initialParentWnd
= nullptr;
227 HWND compositorWnd
= nullptr;
229 RefPtr
<Runnable
> runnable
= NS_NewRunnableFunction(
230 "WinCompositorWindowThread::CreateCompositorWindow::Runnable", [&]() {
231 layers::AutoCompleteTask
complete(&task
);
233 InitializeInitialParentWindowClass();
234 InitializeWindowClass();
236 // Create initial parent window.
237 // We could not directly create a compositor window with a main window
238 // as parent window, so instead create it with a temporary placeholder
239 // parent. Its parent is set as main window in UI process.
241 ::CreateWindowEx(WS_EX_TOOLWINDOW
, kClassNameCompositorInitalParent
,
242 nullptr, WS_POPUP
| WS_DISABLED
, 0, 0, 1, 1,
243 nullptr, 0, GetModuleHandle(nullptr), 0);
244 if (!initialParentWnd
) {
245 gfxCriticalNoteOnce
<< "Inital parent window failed "
250 DWORD extendedStyle
= WS_EX_NOPARENTNOTIFY
| WS_EX_NOREDIRECTIONBITMAP
;
252 if (!StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
253 extendedStyle
|= WS_EX_LAYERED
| WS_EX_TRANSPARENT
;
256 compositorWnd
= ::CreateWindowEx(
257 extendedStyle
, kClassNameCompositor
, nullptr,
258 WS_CHILDWINDOW
| WS_DISABLED
| WS_VISIBLE
, 0, 0, 1, 1,
259 initialParentWnd
, 0, GetModuleHandle(nullptr), 0);
260 if (!compositorWnd
) {
261 gfxCriticalNoteOnce
<< "Compositor window failed "
266 Loop()->PostTask(runnable
.forget());
270 return WinCompositorWnds(compositorWnd
, initialParentWnd
);
274 void WinCompositorWindowThread::DestroyCompositorWindow(
275 WinCompositorWnds aWnds
) {
276 MOZ_ASSERT(aWnds
.mCompositorWnd
);
277 MOZ_ASSERT(aWnds
.mInitialParentWnd
);
284 RefPtr
<Runnable
> runnable
= NS_NewRunnableFunction(
285 "WinCompositorWidget::CreateNativeWindow::Runnable", [aWnds
]() {
286 ::DestroyWindow(aWnds
.mCompositorWnd
);
287 ::DestroyWindow(aWnds
.mInitialParentWnd
);
290 Loop()->PostTask(runnable
.forget());
293 } // namespace widget
294 } // namespace mozilla