Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / windows / WinWindowOcclusionTracker.h
blob924c48d87d752ab90950bbfb9a2d5c3cdba52d72
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/ThreadSafeWeakPtr.h"
17 #include "mozilla/widget/WindowOcclusionState.h"
18 #include "mozilla/widget/WinEventObserver.h"
20 class nsBaseWidget;
21 struct IVirtualDesktopManager;
22 class WinWindowOcclusionTrackerTest;
23 class WinWindowOcclusionTrackerInteractiveTest;
25 namespace base {
26 class Thread;
27 } // namespace base
29 namespace mozilla {
31 namespace layers {
32 class SynchronousTask;
35 namespace widget {
37 class OcclusionUpdateRunnable;
38 class SerializedTaskDispatcher;
39 class UpdateOcclusionStateRunnable;
41 // This class handles window occlusion tracking by using HWND.
42 // Implementation is borrowed from chromium's NativeWindowOcclusionTrackerWin.
43 class WinWindowOcclusionTracker final : public DisplayStatusListener,
44 public SessionChangeListener {
45 public:
46 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinWindowOcclusionTracker)
48 /// Can only be called from the main thread.
49 static WinWindowOcclusionTracker* Get();
51 /// Can only be called from the main thread.
52 static void Ensure();
54 /// Can only be called from the main thread.
55 static void ShutDown();
57 /// Can be called from any thread.
58 static MessageLoop* OcclusionCalculatorLoop();
60 /// Can be called from any thread.
61 static bool IsInWinWindowOcclusionThread();
63 /// Can only be called from the main thread.
64 void EnsureDisplayStatusObserver();
66 /// Can only be called from the main thread.
67 void EnsureSessionChangeObserver();
69 // Enables notifying to widget via NotifyOcclusionState() when the occlusion
70 // state has been computed.
71 void Enable(nsBaseWidget* aWindow, HWND aHwnd);
73 // Disables notifying to widget via NotifyOcclusionState() when the occlusion
74 // state has been computed.
75 void Disable(nsBaseWidget* aWindow, HWND aHwnd);
77 // Called when widget's visibility is changed
78 void OnWindowVisibilityChanged(nsBaseWidget* aWindow, bool aVisible);
80 SerializedTaskDispatcher* GetSerializedTaskDispatcher() {
81 return mSerializedTaskDispatcher;
84 void TriggerCalculation();
86 void DumpOccludingWindows(HWND aHWnd);
88 private:
89 friend class ::WinWindowOcclusionTrackerTest;
90 friend class ::WinWindowOcclusionTrackerInteractiveTest;
92 explicit WinWindowOcclusionTracker(base::Thread* aThread);
93 virtual ~WinWindowOcclusionTracker();
95 // This class computes the occlusion state of the tracked windows.
96 // It runs on a separate thread, and notifies the main thread of
97 // the occlusion state of the tracked windows.
98 class WindowOcclusionCalculator {
99 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WindowOcclusionCalculator)
100 public:
101 // Creates WindowOcclusionCalculator instance.
102 static void CreateInstance();
104 // Clear WindowOcclusionCalculator instance.
105 static void ClearInstance();
107 // Returns existing WindowOcclusionCalculator instance.
108 static WindowOcclusionCalculator* GetInstance() { return sCalculator; }
110 void Initialize();
111 void Shutdown(layers::SynchronousTask* aTask);
113 void EnableOcclusionTrackingForWindow(HWND hwnd);
114 void DisableOcclusionTrackingForWindow(HWND hwnd);
116 // If a window becomes visible, makes sure event hooks are registered.
117 void HandleVisibilityChanged(bool aVisible);
119 void HandleTriggerCalculation();
121 private:
122 WindowOcclusionCalculator();
123 ~WindowOcclusionCalculator();
125 // Registers event hooks, if not registered.
126 void MaybeRegisterEventHooks();
128 // This is the callback registered to get notified of various Windows
129 // events, like window moving/resizing.
130 static void CALLBACK EventHookCallback(HWINEVENTHOOK aWinEventHook,
131 DWORD aEvent, HWND aHwnd,
132 LONG aIdObject, LONG aIdChild,
133 DWORD aEventThread,
134 DWORD aMsEventTime);
136 // EnumWindows callback used to iterate over all hwnds to determine
137 // occlusion status of all tracked root windows. Also builds up
138 // |current_pids_with_visible_windows_| and registers event hooks for newly
139 // discovered processes with visible hwnds.
140 static BOOL CALLBACK
141 ComputeNativeWindowOcclusionStatusCallback(HWND hwnd, LPARAM lParam);
143 // EnumWindows callback used to update the list of process ids with
144 // visible hwnds, |pids_for_location_change_hook_|.
145 static BOOL CALLBACK UpdateVisibleWindowProcessIdsCallback(HWND aHwnd,
146 LPARAM aLParam);
148 // Determines which processes owning visible application windows to set the
149 // EVENT_OBJECT_LOCATIONCHANGE event hook for and stores the pids in
150 // |pids_for_location_change_hook_|.
151 void UpdateVisibleWindowProcessIds();
153 // Computes the native window occlusion status for all tracked root gecko
154 // windows in |root_window_hwnds_occlusion_state_| and notifies them if
155 // their occlusion status has changed.
156 void ComputeNativeWindowOcclusionStatus();
158 // Schedules an occlusion calculation , if one isn't already scheduled.
159 void ScheduleOcclusionCalculationIfNeeded();
161 // Registers a global event hook (not per process) for the events in the
162 // range from |event_min| to |event_max|, inclusive.
163 void RegisterGlobalEventHook(DWORD aEventMin, DWORD aEventMax);
165 // Registers the EVENT_OBJECT_LOCATIONCHANGE event hook for the process with
166 // passed id. The process has one or more visible, opaque windows.
167 void RegisterEventHookForProcess(DWORD aPid);
169 // Registers/Unregisters the event hooks necessary for occlusion tracking
170 // via calls to RegisterEventHook. These event hooks are disabled when all
171 // tracked windows are minimized.
172 void RegisterEventHooks();
173 void UnregisterEventHooks();
175 // EnumWindows callback for occlusion calculation. Returns true to
176 // continue enumeration, false otherwise. Currently, always returns
177 // true because this function also updates currentPidsWithVisibleWindows,
178 // and needs to see all HWNDs.
179 bool ProcessComputeNativeWindowOcclusionStatusCallback(
180 HWND aHwnd, std::unordered_set<DWORD>* aCurrentPidsWithVisibleWindows);
182 // Processes events sent to OcclusionEventHookCallback.
183 // It generally triggers scheduling of the occlusion calculation, but
184 // ignores certain events in order to not calculate occlusion more than
185 // necessary.
186 void ProcessEventHookCallback(HWINEVENTHOOK aWinEventHook, DWORD aEvent,
187 HWND aHwnd, LONG aIdObject, LONG aIdChild);
189 // EnumWindows callback for determining which processes to set the
190 // EVENT_OBJECT_LOCATIONCHANGE event hook for. We set that event hook for
191 // processes hosting fully visible, opaque windows.
192 void ProcessUpdateVisibleWindowProcessIdsCallback(HWND aHwnd);
194 // Returns true if the window is visible, fully opaque, and on the current
195 // virtual desktop, false otherwise.
196 bool WindowCanOccludeOtherWindowsOnCurrentVirtualDesktop(
197 HWND aHwnd, LayoutDeviceIntRect* aWindowRect);
199 // Returns true if aHwnd is definitely on the current virtual desktop,
200 // false if it's definitely not on the current virtual desktop, and Nothing
201 // if we we can't tell for sure.
202 Maybe<bool> IsWindowOnCurrentVirtualDesktop(HWND aHwnd);
204 static StaticRefPtr<WindowOcclusionCalculator> sCalculator;
206 // Map of root app window hwnds and their occlusion state. This contains
207 // both visible and hidden windows.
208 // It is accessed from WinWindowOcclusionTracker::UpdateOcclusionState()
209 // without using mutex. The access is safe by using
210 // SerializedTaskDispatcher.
211 std::unordered_map<HWND, OcclusionState> mRootWindowHwndsOcclusionState;
213 // Values returned by SetWinEventHook are stored so that hooks can be
214 // unregistered when necessary.
215 std::vector<HWINEVENTHOOK> mGlobalEventHooks;
217 // Map from process id to EVENT_OBJECT_LOCATIONCHANGE event hook.
218 std::unordered_map<DWORD, HWINEVENTHOOK> mProcessEventHooks;
220 // Pids of processes for which the EVENT_OBJECT_LOCATIONCHANGE event hook is
221 // set.
222 std::unordered_set<DWORD> mPidsForLocationChangeHook;
224 // Used as a timer to delay occlusion update.
225 RefPtr<CancelableRunnable> mOcclusionUpdateRunnable;
227 // Used to determine if a window is occluded. As we iterate through the
228 // hwnds in z-order, we subtract each opaque window's rect from
229 // mUnoccludedDesktopRegion. When we get to a root window, we subtract
230 // it from mUnoccludedDesktopRegion, and if mUnoccludedDesktopRegion
231 // doesn't change, the root window was already occluded.
232 LayoutDeviceIntRegion mUnoccludedDesktopRegion;
234 // Keeps track of how many root windows we need to compute the occlusion
235 // state of in a call to ComputeNativeWindowOcclusionStatus. Once we've
236 // determined the state of all root windows, we can stop subtracting
237 // windows from mUnoccludedDesktopRegion;.
238 int mNumRootWindowsWithUnknownOcclusionState;
240 // This is true if the task bar thumbnails or the alt tab thumbnails are
241 // showing.
242 bool mShowingThumbnails = false;
244 // Used to keep track of the window that's currently moving. That window
245 // is ignored for calculation occlusion so that tab dragging won't
246 // ignore windows occluded by the dragged window.
247 HWND mMovingWindow = 0;
249 // Only used on Win10+.
250 RefPtr<IVirtualDesktopManager> mVirtualDesktopManager;
252 // Used to serialize tasks related to mRootWindowHwndsOcclusionState.
253 RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher;
255 friend class OcclusionUpdateRunnable;
258 static BOOL CALLBACK DumpOccludingWindowsCallback(HWND aHWnd, LPARAM aLParam);
260 // Returns true if we are interested in |hwnd| for purposes of occlusion
261 // calculation. We are interested in |hwnd| if it is a window that is
262 // visible, opaque, bounded, and not a popup or floating window. If we are
263 // interested in |hwnd|, stores the window rectangle in |window_rect|.
264 static bool IsWindowVisibleAndFullyOpaque(HWND aHwnd,
265 LayoutDeviceIntRect* aWindowRect);
267 void Destroy();
269 static void CallUpdateOcclusionState(
270 std::unordered_map<HWND, OcclusionState>* aMap, bool aShowAllWindows);
272 // Updates root windows occclusion state. If aShowAllWindows is true,
273 // all non-hidden windows will be marked visible. This is used to force
274 // rendering of thumbnails.
275 void UpdateOcclusionState(std::unordered_map<HWND, OcclusionState>* aMap,
276 bool aShowAllWindows);
278 // This is called with session changed notifications. If the screen is locked
279 // by the current session, it marks app windows as occluded.
280 void OnSessionChange(WPARAM aStatusCode,
281 Maybe<bool> aIsCurrentSession) override;
283 // This is called when the display is put to sleep. If the display is sleeping
284 // it marks app windows as occluded.
285 void OnDisplayStateChanged(bool aDisplayOn) override;
287 // Marks all root windows as either occluded, or if hwnd IsIconic, hidden.
288 void MarkNonIconicWindowsOccluded();
290 static StaticRefPtr<WinWindowOcclusionTracker> sTracker;
292 // "WinWindowOcclusionCalc" thread.
293 base::Thread* const mThread;
295 // Map of HWND to widget. Maintained on main thread, and used to send
296 // occlusion state notifications to Windows from
297 // mRootWindowHwndsOcclusionState.
298 std::unordered_map<HWND, nsWeakPtr> mHwndRootWindowMap;
300 // This is set by UpdateOcclusionState(). It is currently only used by tests.
301 int mNumVisibleRootWindows = 0;
303 // If the screen is locked, windows are considered occluded.
304 bool mScreenLocked = false;
306 // If the display is off, windows are considered occluded.
307 bool mDisplayOn = true;
309 RefPtr<DisplayStatusObserver> mDisplayStatusObserver;
311 RefPtr<SessionChangeObserver> mSessionChangeObserver;
313 // Used to serialize tasks related to mRootWindowHwndsOcclusionState.
314 RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher;
316 friend class OcclusionUpdateRunnable;
317 friend class UpdateOcclusionStateRunnable;
320 } // namespace widget
321 } // namespace mozilla
323 #endif // widget_windows_WinWindowOcclusionTracker_h