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