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/. */ 6 7 #ifndef widget_windows_WinWindowOcclusionTracker_h 8 #define widget_windows_WinWindowOcclusionTracker_h 9 10 #include <string> 11 #include <unordered_map> 12 #include <unordered_set> 13 #include <vector> 14 15 #include "nsIWeakReferenceUtils.h" 16 #include "mozilla/ThreadSafeWeakPtr.h" 17 #include "mozilla/widget/WindowOcclusionState.h" 18 #include "mozilla/widget/WinEventObserver.h" 19 20 class nsBaseWidget; 21 struct IVirtualDesktopManager; 22 class WinWindowOcclusionTrackerTest; 23 class WinWindowOcclusionTrackerInteractiveTest; 24 25 namespace base { 26 class Thread; 27 } // namespace base 28 29 namespace mozilla { 30 31 namespace layers { 32 class SynchronousTask; 33 } 34 35 namespace widget { 36 37 class OcclusionUpdateRunnable; 38 class SerializedTaskDispatcher; 39 class UpdateOcclusionStateRunnable; 40 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) 47 48 /// Can only be called from the main thread. 49 static WinWindowOcclusionTracker* Get(); 50 51 /// Can only be called from the main thread. 52 static void Ensure(); 53 54 /// Can only be called from the main thread. 55 static void ShutDown(); 56 57 /// Can be called from any thread. 58 static MessageLoop* OcclusionCalculatorLoop(); 59 60 /// Can be called from any thread. 61 static bool IsInWinWindowOcclusionThread(); 62 63 /// Can only be called from the main thread. 64 void EnsureDisplayStatusObserver(); 65 66 /// Can only be called from the main thread. 67 void EnsureSessionChangeObserver(); 68 69 // Enables notifying to widget via NotifyOcclusionState() when the occlusion 70 // state has been computed. 71 void Enable(nsBaseWidget* aWindow, HWND aHwnd); 72 73 // Disables notifying to widget via NotifyOcclusionState() when the occlusion 74 // state has been computed. 75 void Disable(nsBaseWidget* aWindow, HWND aHwnd); 76 77 // Called when widget's visibility is changed 78 void OnWindowVisibilityChanged(nsBaseWidget* aWindow, bool aVisible); 79 GetSerializedTaskDispatcher()80 SerializedTaskDispatcher* GetSerializedTaskDispatcher() { 81 return mSerializedTaskDispatcher; 82 } 83 84 void TriggerCalculation(); 85 86 void DumpOccludingWindows(HWND aHWnd); 87 88 private: 89 friend class ::WinWindowOcclusionTrackerTest; 90 friend class ::WinWindowOcclusionTrackerInteractiveTest; 91 92 explicit WinWindowOcclusionTracker(base::Thread* aThread); 93 virtual ~WinWindowOcclusionTracker(); 94 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(); 103 104 // Clear WindowOcclusionCalculator instance. 105 static void ClearInstance(); 106 107 // Returns existing WindowOcclusionCalculator instance. GetInstance()108 static WindowOcclusionCalculator* GetInstance() { return sCalculator; } 109 110 void Initialize(); 111 void Shutdown(layers::SynchronousTask* aTask); 112 113 void EnableOcclusionTrackingForWindow(HWND hwnd); 114 void DisableOcclusionTrackingForWindow(HWND hwnd); 115 116 // If a window becomes visible, makes sure event hooks are registered. 117 void HandleVisibilityChanged(bool aVisible); 118 119 void HandleTriggerCalculation(); 120 121 private: 122 WindowOcclusionCalculator(); 123 ~WindowOcclusionCalculator(); 124 125 // Registers event hooks, if not registered. 126 void MaybeRegisterEventHooks(); 127 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); 135 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); 142 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); 147 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(); 152 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(); 157 158 // Schedules an occlusion calculation , if one isn't already scheduled. 159 void ScheduleOcclusionCalculationIfNeeded(); 160 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); 164 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); 168 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(); 174 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); 181 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); 188 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); 193 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); 198 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); 203 204 static StaticRefPtr<WindowOcclusionCalculator> sCalculator; 205 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; 212 213 // Values returned by SetWinEventHook are stored so that hooks can be 214 // unregistered when necessary. 215 std::vector<HWINEVENTHOOK> mGlobalEventHooks; 216 217 // Map from process id to EVENT_OBJECT_LOCATIONCHANGE event hook. 218 std::unordered_map<DWORD, HWINEVENTHOOK> mProcessEventHooks; 219 220 // Pids of processes for which the EVENT_OBJECT_LOCATIONCHANGE event hook is 221 // set. 222 std::unordered_set<DWORD> mPidsForLocationChangeHook; 223 224 // Used as a timer to delay occlusion update. 225 RefPtr<CancelableRunnable> mOcclusionUpdateRunnable; 226 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; 233 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; 239 240 // This is true if the task bar thumbnails or the alt tab thumbnails are 241 // showing. 242 bool mShowingThumbnails = false; 243 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; 248 249 // Only used on Win10+. 250 RefPtr<IVirtualDesktopManager> mVirtualDesktopManager; 251 252 // Used to serialize tasks related to mRootWindowHwndsOcclusionState. 253 RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher; 254 255 friend class OcclusionUpdateRunnable; 256 }; 257 258 static BOOL CALLBACK DumpOccludingWindowsCallback(HWND aHWnd, LPARAM aLParam); 259 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); 266 267 void Destroy(); 268 269 static void CallUpdateOcclusionState( 270 std::unordered_map<HWND, OcclusionState>* aMap, bool aShowAllWindows); 271 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); 277 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; 282 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; 286 287 // Marks all root windows as either occluded, or if hwnd IsIconic, hidden. 288 void MarkNonIconicWindowsOccluded(); 289 290 static StaticRefPtr<WinWindowOcclusionTracker> sTracker; 291 292 // "WinWindowOcclusionCalc" thread. 293 base::Thread* const mThread; 294 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; 299 300 // This is set by UpdateOcclusionState(). It is currently only used by tests. 301 int mNumVisibleRootWindows = 0; 302 303 // If the screen is locked, windows are considered occluded. 304 bool mScreenLocked = false; 305 306 // If the display is off, windows are considered occluded. 307 bool mDisplayOn = true; 308 309 RefPtr<DisplayStatusObserver> mDisplayStatusObserver; 310 311 RefPtr<SessionChangeObserver> mSessionChangeObserver; 312 313 // Used to serialize tasks related to mRootWindowHwndsOcclusionState. 314 RefPtr<SerializedTaskDispatcher> mSerializedTaskDispatcher; 315 316 friend class OcclusionUpdateRunnable; 317 friend class UpdateOcclusionStateRunnable; 318 }; 319 320 } // namespace widget 321 } // namespace mozilla 322 323 #endif // widget_windows_WinWindowOcclusionTracker_h 324