Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / WinWindowOcclusionTracker.h
blobb82a41b98443faae043443b3a7635981186f42ec
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 #ifndef widget_windows_WinWindowOcclusionTracker_h
8 #define widget_windows_WinWindowOcclusionTracker_h
10 #include <string>
11 #include <unordered_map>
12 #include <unordered_set>
13 #include <vector>
15 #include "nsIWeakReferenceUtils.h"
16 #include "mozilla/Monitor.h"
17 #include "mozilla/StaticPtr.h"
18 #include "mozilla/WindowsVersion.h"
19 #include "mozilla/ThreadSafeWeakPtr.h"
20 #include "mozilla/widget/WindowOcclusionState.h"
21 #include "mozilla/widget/WinEventObserver.h"
22 #include "Units.h"
23 #include "nsThreadUtils.h"
25 class nsBaseWidget;
26 struct IVirtualDesktopManager;
27 class WinWindowOcclusionTrackerTest;
28 class WinWindowOcclusionTrackerInteractiveTest;
30 namespace base {
31 class Thread;
32 } // namespace base
34 namespace mozilla {
36 namespace widget {
38 class OcclusionUpdateRunnable;
39 class SerializedTaskDispatcher;
40 class UpdateOcclusionStateRunnable;
42 // This class handles window occlusion tracking by using HWND.
43 // Implementation is borrowed from chromium's NativeWindowOcclusionTrackerWin.
44 class WinWindowOcclusionTracker final : public DisplayStatusListener,
45 public SessionChangeListener {
46 public:
47 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinWindowOcclusionTracker)
49 /// Can only be called from the main thread.
50 static WinWindowOcclusionTracker* Get();
52 /// Can only be called from the main thread.
53 static void Ensure();
55 /// Can only be called from the main thread.
56 static void ShutDown();
58 /// Can be called from any thread.
59 static MessageLoop* OcclusionCalculatorLoop();
61 /// Can be called from any thread.
62 static bool IsInWinWindowOcclusionThread();
64 /// Can only be called from the main thread.
65 void EnsureDisplayStatusObserver();
67 /// Can only be called from the main thread.
68 void EnsureSessionChangeObserver();
70 // Enables notifying to widget via NotifyOcclusionState() when the occlusion
71 // state has been computed.
72 void Enable(nsBaseWidget* aWindow, HWND aHwnd);
74 // Disables notifying to widget via NotifyOcclusionState() when the occlusion
75 // state has been computed.
76 void Disable(nsBaseWidget* aWindow, HWND aHwnd);
78 // Called when widget's visibility is changed
79 void OnWindowVisibilityChanged(nsBaseWidget* aWindow, bool aVisible);
81 SerializedTaskDispatcher* GetSerializedTaskDispatcher() {
82 return mSerializedTaskDispatcher;
85 void TriggerCalculation();
87 void DumpOccludingWindows(HWND aHWnd);
89 private:
90 friend class ::WinWindowOcclusionTrackerTest;
91 friend class ::WinWindowOcclusionTrackerInteractiveTest;
93 explicit WinWindowOcclusionTracker(UniquePtr<base::Thread> aThread);
94 virtual ~WinWindowOcclusionTracker();
96 // This class computes the occlusion state of the tracked windows.
97 // It runs on a separate thread, and notifies the main thread of
98 // the occlusion state of the tracked windows.
99 class WindowOcclusionCalculator {
100 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WindowOcclusionCalculator)
101 public:
102 // Creates WindowOcclusionCalculator instance.
103 static void CreateInstance();
105 // Clear WindowOcclusionCalculator instance.
106 static void ClearInstance();
108 // Returns existing WindowOcclusionCalculator instance.
109 static WindowOcclusionCalculator* GetInstance() { return sCalculator; }
111 void Initialize();
112 void Shutdown();
114 void EnableOcclusionTrackingForWindow(HWND hwnd);
115 void DisableOcclusionTrackingForWindow(HWND hwnd);
117 // If a window becomes visible, makes sure event hooks are registered.
118 void HandleVisibilityChanged(bool aVisible);
120 void HandleTriggerCalculation();
122 private:
123 WindowOcclusionCalculator();
124 ~WindowOcclusionCalculator();
126 // Registers event hooks, if not registered.
127 void MaybeRegisterEventHooks();
129 // This is the callback registered to get notified of various Windows
130 // events, like window moving/resizing.
131 static void CALLBACK EventHookCallback(HWINEVENTHOOK aWinEventHook,
132 DWORD aEvent, HWND aHwnd,
133 LONG aIdObject, LONG aIdChild,
134 DWORD aEventThread,
135 DWORD aMsEventTime);
137 // EnumWindows callback used to iterate over all hwnds to determine
138 // occlusion status of all tracked root windows. Also builds up
139 // |current_pids_with_visible_windows_| and registers event hooks for newly
140 // discovered processes with visible hwnds.
141 static BOOL CALLBACK
142 ComputeNativeWindowOcclusionStatusCallback(HWND hwnd, LPARAM lParam);
144 // EnumWindows callback used to update the list of process ids with
145 // visible hwnds, |pids_for_location_change_hook_|.
146 static BOOL CALLBACK UpdateVisibleWindowProcessIdsCallback(HWND aHwnd,
147 LPARAM aLParam);
149 // Determines which processes owning visible application windows to set the
150 // EVENT_OBJECT_LOCATIONCHANGE event hook for and stores the pids in
151 // |pids_for_location_change_hook_|.
152 void UpdateVisibleWindowProcessIds();
154 // Computes the native window occlusion status for all tracked root gecko
155 // windows in |root_window_hwnds_occlusion_state_| and notifies them if
156 // their occlusion status has changed.
157 void ComputeNativeWindowOcclusionStatus();
159 // Schedules an occlusion calculation , if one isn't already scheduled.
160 void ScheduleOcclusionCalculationIfNeeded();
162 // Registers a global event hook (not per process) for the events in the
163 // range from |event_min| to |event_max|, inclusive.
164 void RegisterGlobalEventHook(DWORD aEventMin, DWORD aEventMax);
166 // Registers the EVENT_OBJECT_LOCATIONCHANGE event hook for the process with
167 // passed id. The process has one or more visible, opaque windows.
168 void RegisterEventHookForProcess(DWORD aPid);
170 // Registers/Unregisters the event hooks necessary for occlusion tracking
171 // via calls to RegisterEventHook. These event hooks are disabled when all
172 // tracked windows are minimized.
173 void RegisterEventHooks();
174 void UnregisterEventHooks();
176 // EnumWindows callback for occlusion calculation. Returns true to
177 // continue enumeration, false otherwise. Currently, always returns
178 // true because this function also updates currentPidsWithVisibleWindows,
179 // and needs to see all HWNDs.
180 bool ProcessComputeNativeWindowOcclusionStatusCallback(
181 HWND aHwnd, std::unordered_set<DWORD>* aCurrentPidsWithVisibleWindows);
183 // Processes events sent to OcclusionEventHookCallback.
184 // It generally triggers scheduling of the occlusion calculation, but
185 // ignores certain events in order to not calculate occlusion more than
186 // necessary.
187 void ProcessEventHookCallback(HWINEVENTHOOK aWinEventHook, DWORD aEvent,
188 HWND aHwnd, LONG aIdObject, LONG aIdChild);
190 // EnumWindows callback for determining which processes to set the
191 // EVENT_OBJECT_LOCATIONCHANGE event hook for. We set that event hook for
192 // processes hosting fully visible, opaque windows.
193 void ProcessUpdateVisibleWindowProcessIdsCallback(HWND aHwnd);
195 // Returns true if the window is visible, fully opaque, and on the current
196 // virtual desktop, false otherwise.
197 bool WindowCanOccludeOtherWindowsOnCurrentVirtualDesktop(
198 HWND aHwnd, LayoutDeviceIntRect* aWindowRect);
200 // Returns true if aHwnd is definitely on the current virtual desktop,
201 // false if it's definitely not on the current virtual desktop, and Nothing
202 // if we we can't tell for sure.
203 Maybe<bool> IsWindowOnCurrentVirtualDesktop(HWND aHwnd);
205 static StaticRefPtr<WindowOcclusionCalculator> sCalculator;
207 // Map of root app window hwnds and their occlusion state. This contains
208 // both visible and hidden windows.
209 // It is accessed from WinWindowOcclusionTracker::UpdateOcclusionState()
210 // without using mutex. The access is safe by using
211 // SerializedTaskDispatcher.
212 std::unordered_map<HWND, OcclusionState> mRootWindowHwndsOcclusionState;
214 // Values returned by SetWinEventHook are stored so that hooks can be
215 // unregistered when necessary.
216 std::vector<HWINEVENTHOOK> mGlobalEventHooks;
218 // Map from process id to EVENT_OBJECT_LOCATIONCHANGE event hook.
219 std::unordered_map<DWORD, HWINEVENTHOOK> mProcessEventHooks;
221 // Pids of processes for which the EVENT_OBJECT_LOCATIONCHANGE event hook is
222 // set.
223 std::unordered_set<DWORD> mPidsForLocationChangeHook;
225 // Used as a timer to delay occlusion update.
226 RefPtr<CancelableRunnable> mOcclusionUpdateRunnable;
228 // Used to determine if a window is occluded. As we iterate through the
229 // hwnds in z-order, we subtract each opaque window's rect from
230 // mUnoccludedDesktopRegion. When we get to a root window, we subtract
231 // it from mUnoccludedDesktopRegion, and if mUnoccludedDesktopRegion
232 // doesn't change, the root window was already occluded.
233 LayoutDeviceIntRegion mUnoccludedDesktopRegion;
235 // Keeps track of how many root windows we need to compute the occlusion
236 // state of in a call to ComputeNativeWindowOcclusionStatus. Once we've
237 // determined the state of all root windows, we can stop subtracting
238 // windows from mUnoccludedDesktopRegion;.
239 int mNumRootWindowsWithUnknownOcclusionState;
241 // This is true if the task bar thumbnails or the alt tab thumbnails are
242 // showing.
243 bool mShowingThumbnails = false;
245 // Used to keep track of the window that's currently moving. That window
246 // is ignored for calculation occlusion so that tab dragging won't
247 // ignore windows occluded by the dragged window.
248 HWND mMovingWindow = 0;
250 // Only used on Win10+.
251 RefPtr<IVirtualDesktopManager> mVirtualDesktopManager;
253 // Used to serialize tasks related to mRootWindowHwndsOcclusionState.
254 RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher;
256 // This is an alias to the singleton WinWindowOcclusionTracker mMonitor,
257 // and is used in ShutDown().
258 Monitor& mMonitor;
260 friend class OcclusionUpdateRunnable;
263 static BOOL CALLBACK DumpOccludingWindowsCallback(HWND aHWnd, LPARAM aLParam);
265 // Returns true if we are interested in |hwnd| for purposes of occlusion
266 // calculation. We are interested in |hwnd| if it is a window that is
267 // visible, opaque, bounded, and not a popup or floating window. If we are
268 // interested in |hwnd|, stores the window rectangle in |window_rect|.
269 static bool IsWindowVisibleAndFullyOpaque(HWND aHwnd,
270 LayoutDeviceIntRect* aWindowRect);
272 void Destroy();
274 static void CallUpdateOcclusionState(
275 std::unordered_map<HWND, OcclusionState>* aMap, bool aShowAllWindows);
277 // Updates root windows occclusion state. If aShowAllWindows is true,
278 // all non-hidden windows will be marked visible. This is used to force
279 // rendering of thumbnails.
280 void UpdateOcclusionState(std::unordered_map<HWND, OcclusionState>* aMap,
281 bool aShowAllWindows);
283 // This is called with session changed notifications. If the screen is locked
284 // by the current session, it marks app windows as occluded.
285 void OnSessionChange(WPARAM aStatusCode,
286 Maybe<bool> aIsCurrentSession) override;
288 // This is called when the display is put to sleep. If the display is sleeping
289 // it marks app windows as occluded.
290 void OnDisplayStateChanged(bool aDisplayOn) override;
292 // Marks all root windows as either occluded, or if hwnd IsIconic, hidden.
293 void MarkNonIconicWindowsOccluded();
295 static StaticRefPtr<WinWindowOcclusionTracker> sTracker;
297 // "WinWindowOcclusionCalc" thread.
298 UniquePtr<base::Thread> mThread;
299 Monitor mMonitor;
301 // Has ShutDown been called on us? We might have survived if our thread join
302 // timed out.
303 bool mHasAttemptedShutdown = false;
305 // Map of HWND to widget. Maintained on main thread, and used to send
306 // occlusion state notifications to Windows from
307 // mRootWindowHwndsOcclusionState.
308 std::unordered_map<HWND, nsWeakPtr> mHwndRootWindowMap;
310 // This is set by UpdateOcclusionState(). It is currently only used by tests.
311 int mNumVisibleRootWindows = 0;
313 // If the screen is locked, windows are considered occluded.
314 bool mScreenLocked = false;
316 // If the display is off, windows are considered occluded.
317 bool mDisplayOn = true;
319 RefPtr<DisplayStatusObserver> mDisplayStatusObserver;
321 RefPtr<SessionChangeObserver> mSessionChangeObserver;
323 // Used to serialize tasks related to mRootWindowHwndsOcclusionState.
324 RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher;
326 friend class OcclusionUpdateRunnable;
327 friend class UpdateOcclusionStateRunnable;
330 } // namespace widget
331 } // namespace mozilla
333 #endif // widget_windows_WinWindowOcclusionTracker_h