// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef UI_AURA_WINDOW_OCCLUSION_TRACKER_H_ #define UI_AURA_WINDOW_OCCLUSION_TRACKER_H_ #include #include #include "base/callback.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/scoped_multi_source_observation.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/aura/aura_export.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" #include "ui/aura/window_tree_host_observer.h" #include "ui/compositor/layer_animation_observer.h" struct SkIRect; namespace gfx { class Transform; } namespace aura { namespace test { class WindowOcclusionTrackerTestApi; } class WindowOcclusionChangeBuilder; // Notifies tracked Windows when their occlusion state change. // // To start tracking the occlusion state of a Window, call // aura::Window::TrackOcclusionState() // // A Window is occluded if its bounds and transform are not animated and one of // these conditions is true: // - The Window is hidden (Window::IsVisible() is true). // - The bounds of the Window are completely covered by opaque and axis-aligned // Windows whose bounds and transform are not animated. // Note that an occluded window may be drawn on the screen by window switching // features such as "Alt-Tab" or "Overview". class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, public WindowObserver, public WindowTreeHostObserver { public: // Prevents window occlusion state computations within its scope. If an event // that could cause window occlusion states to change occurs within the scope // of a ScopedPause, window occlusion state computations are delayed until all // ScopedPause objects have been destroyed. class AURA_EXPORT ScopedPause { public: ScopedPause(); ScopedPause(const ScopedPause&) = delete; ScopedPause& operator=(const ScopedPause&) = delete; ~ScopedPause(); }; // Used to exclude a window and all descendants from occlusion calculation. // The occlusion state of the window and all descendants is set from the // the drawn state of the window, *not* based on what windows may be stacked // above them. Further, ignores the window that is excluded and its // descendants when computing the occlusion state of other windows in the // tree. // // This is useful for a window being dragged or resized to avoid unnecessary // occlusion state change triggered by these operation, because the window // bounds are temporary until it is finished. // // Note that this is intended to be used by window manager, such as Ash. class AURA_EXPORT ScopedExclude : public WindowObserver { public: explicit ScopedExclude(Window* window); ScopedExclude(const ScopedExclude&) = delete; ScopedExclude& operator=(const ScopedExclude&) = delete; ~ScopedExclude() override; Window* window() { return window_; } private: // WindowObserver: void OnWindowDestroying(Window* window) override; void Shutdown(); raw_ptr window_; }; // Forces the occlusion state of a window to VISIBLE regardless of the drawn // state of the window. Causes the occlusion state of descendants of the // window that is forced VISIBLE to be computed as if they were in an // isolated tree with a root that is drawn. Ignores the window that is forced // VISIBLE and its descendants when computing the occlusion state of other // windows in the tree. // // This function is primarily useful for situations that show the contents of // a hidden window, such as overview mode on ChromeOS. class AURA_EXPORT ScopedForceVisible : public WindowObserver { public: explicit ScopedForceVisible(Window* window); ScopedForceVisible(const ScopedForceVisible&) = delete; ScopedForceVisible& operator=(const ScopedForceVisible&) = delete; ~ScopedForceVisible() override; private: // WindowObserver: void OnWindowDestroying(Window* window) override; void Shutdown(); raw_ptr window_; }; // Holds occlusion related information for tracked windows. struct OcclusionData { // Occlusion state for a tracked window. Window::OcclusionState occlusion_state = Window::OcclusionState::UNKNOWN; // Region in root window coordinates that is occluded. SkRegion occluded_region; }; WindowOcclusionTracker(const WindowOcclusionTracker&) = delete; WindowOcclusionTracker& operator=(const WindowOcclusionTracker&) = delete; // Start tracking the occlusion state of |window|. void Track(Window* window); // Compute the occlusion state and occluded region that |window| will have // once all bounds, transform, opacity, and visibility animations have // completed. |window| must be a window that has its occlusion state tracked. OcclusionData ComputeTargetOcclusionForWindow(Window* window); // Returns true if there are ignored animating windows. bool HasIgnoredAnimatingWindows() const { return !animated_windows_.empty(); } // Set a callback to determine whether a window has content to draw in // addition to layer type check (window layer type != ui::LAYER_NOT_DRAWN). using WindowHasContentCallback = base::RepeatingCallback; void set_window_has_content_callback(WindowHasContentCallback callback) { window_has_content_callback_ = std::move(callback); } // Set the factory to create WindowOcclusionChangeBuilder. using OcclusionChangeBuilderFactory = base::RepeatingCallback()>; void set_occlusion_change_builder_factory( OcclusionChangeBuilderFactory factory) { occlusion_change_builder_factory_ = std::move(factory); } bool IsPaused() const { return num_pause_occlusion_tracking_; } private: friend class test::WindowOcclusionTrackerTestApi; friend class Env; friend std::unique_ptr::deleter_type; struct RootWindowState { // Number of Windows whose occlusion state is tracked under this root // Window. int num_tracked_windows = 0; // Whether the occlusion state of tracked Windows under this root is stale. bool dirty = false; // The occlusion state of the root window's host. Window::OcclusionState occlusion_state = Window::OcclusionState::UNKNOWN; SkRegion occluded_region; }; WindowOcclusionTracker(); ~WindowOcclusionTracker() override; // Returns true iff the occlusion states in |tracked_windows| match those // returned by Window::GetOcclusionState(). static bool OcclusionStatesMatch( const base::flat_map& tracked_windows); // Recomputes the occlusion state of tracked windows under roots marked as // dirty in |root_windows_| if there are no active ScopedPause instance. void MaybeComputeOcclusion(); // Recomputes the occlusion state of |window| and its descendants. // |parent_transform_relative_to_root| is the transform of |window->parent()| // relative to the root window. |clipped_bounds| is an optional mask for the // bounds of |window| and its descendants. |occluded_region| is a region // covered by windows which are on top of |window|. Returns true if at least // one window in the hierarchy starting at |window| is NOT_OCCLUDED. bool RecomputeOcclusionImpl( Window* window, const gfx::Transform& parent_transform_relative_to_root, const SkIRect* clipped_bounds, SkRegion* occluded_region); // Returns true if |window| can occlude other windows (e.g. because it is // not transparent or has opaque regions for occlusion). // |window| must be visible. bool VisibleWindowCanOccludeOtherWindows(Window* window) const; // Returns true if |window| has content. bool WindowHasContent(Window* window) const; // Removes windows whose bounds and transform are not animated from // |animated_windows_|. Marks the root of those windows as dirty. void CleanupAnimatedWindows(); // If the bounds or transform of |window| are animated and |window| is not in // |animated_windows_|, adds |window| to |animated_windows_| and returns true. bool MaybeObserveAnimatedWindow(Window* window); // Calls SetOccluded() with |is_occluded| as argument for |window| and its // descendants. |is_parent_visible| is true if the parent is visible. void SetWindowAndDescendantsAreOccluded(Window* window, bool is_occluded, bool is_parent_visible); // Updates the occlusion state of |window| in |tracked_windows_|, based on // |is_occluded| and window->IsVisible(). Updates the occluded region of // |window| using |occluded_region|. No-op if |window| is not in // |tracked_windows_|. void SetOccluded(Window* window, bool is_occluded, bool is_parent_visible, const SkRegion& occluded_region); // Returns true if |window| is in |tracked_windows_|. bool WindowIsTracked(Window* window) const; // Returns true if |window| is in |animated_windows_|. bool WindowIsAnimated(Window* window) const; // Returns true if |window| is in |excluded_windows_|. bool WindowIsExcluded(Window* window) const; // Returns true if |window| is considered visible. Use this over IsVisible() // to ensure forced visible windows are considered. bool WindowIsVisible(Window* window) const; // Returns true if |window| is forced visible. This does *not* recurse and // only checks |window|. Use WindowIsVisible() to consider parents. bool WindowIsForcedVisible(Window* window) const; // If the root of |window| is not dirty and |predicate| is true, marks the // root of |window| as dirty. Then, calls MaybeComputeOcclusion(). // |predicate| is not evaluated if the root of |window| is already dirty when // this is called. template void MarkRootWindowAsDirtyAndMaybeComputeOcclusionIf(Window* window, Predicate predicate); // Marks |root_window_state| as dirty. void MarkRootWindowStateAsDirty(RootWindowState* root_window_state); // Marks |root_window| as dirty. Returns false if none of the descendent // windows in |root_window| are tracked. bool MarkRootWindowAsDirty(Window* root_window); // Returns true if |window| or one of its parents is in |animated_windows_|. bool WindowOrParentIsAnimated(Window* window) const; // Returns true if |window| or one of its descendants is in // |tracked_windows_| and visible. bool WindowOrDescendantIsTrackedAndVisible(Window* window) const; // Returns true if |window| or one of its descendants is visible, has some // opaque region and is not in |animated_windows_|. If |assume_parent_opaque| // is true, the function assumes that the combined opacity of window->parent() // is 1.0f. If |assume_window_opaque|, the function assumes that the opacity // of |window| is 1.0f. bool WindowOrDescendantCanOccludeOtherWindows( Window* window, bool assume_parent_opaque = false, bool assume_window_opaque = false) const; // Returns true if changing the opacity or alpha state of |window| could // affect the occlusion state of a tracked window. bool WindowOpacityChangeMayAffectOcclusionStates(Window* window) const; // Returns true if changing the transform, bounds or stacking order of // |window| could affect the occlusion state of a tracked window. bool WindowMoveMayAffectOcclusionStates(Window* window) const; // Called when a tracked |window| is added to a root window. void TrackedWindowAddedToRoot(Window* window); // Called when a tracked |window| is removed from a root window. void TrackedWindowRemovedFromRoot(Window* window); // Removes |this| from the observer list of |window| and its descendants, // except if they are in |tracked_windows_| or |windows_being_destroyed_|. void RemoveObserverFromWindowAndDescendants(Window* window); // Add |this| to the observer list of |window| and its descendants. void AddObserverToWindowAndDescendants(Window* window); // Pauses/unpauses the occlusion state computation. void Pause(); void Unpause(); // Exclude/Unexclude a window from occlusion tracking. See comment on // ScopedExclude. void Exclude(Window* window); void Unexclude(Window* window); // Called from ScopedForceVisible. void ForceWindowVisible(Window* window); void RemoveForceWindowVisible(Window* window); // Returns true if the occlusion tracker should use target bounds, opacity // transform, and visibility for occlusion computation. This will be true // if the target occlusion state of a window is being computed via // |ComputeTargetOcclusionForWindow|. bool ShouldUseTargetValues() const; // ui::LayerAnimationObserver: void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override; void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override; void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) override; // WindowObserver: void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override; void OnWindowAdded(Window* window) override; void OnWillRemoveWindow(Window* window) override; void OnWindowVisibilityChanged(Window* window, bool visible) override; void OnWindowBoundsChanged(Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds, ui::PropertyChangeReason reason) override; void OnWindowOpacitySet(Window* window, ui::PropertyChangeReason reason) override; void OnWindowAlphaShapeSet(Window* window) override; void OnWindowTransparentChanged(Window* window, ui::PropertyChangeReason reason) override; void OnWindowTransformed(Window* window, ui::PropertyChangeReason reason) override; void OnWindowStackingChanged(Window* window) override; void OnWindowDestroyed(Window* window) override; void OnWindowAddedToRootWindow(Window* window) override; void OnWindowRemovingFromRootWindow(Window* window, Window* new_root) override; void OnWindowLayerRecreated(Window* window) override; void OnWindowOpaqueRegionsForOcclusionChanged(Window* window) override; // WindowTreeHostObserver void OnOcclusionStateChanged(WindowTreeHost* host, Window::OcclusionState new_state, const SkRegion& occluded_region) override; // Windows whose occlusion data is tracked. base::flat_map tracked_windows_; // Windows whose target visibility is forced to true. base::flat_map forced_visible_count_map_; // Windows whose bounds or transform are animated. // // To reduce the overhead of the WindowOcclusionTracker, windows in this set // and their descendants are considered non-occluded and cannot occlude other // windows. A window is added to this set the first time that occlusion is // computed after it was animated. It is removed when the animation ends or is // aborted. base::flat_set animated_windows_; // Windows that are excluded from occlustion tracking. See comment on // ScopedExclude. base::flat_set excluded_windows_; // Root Windows of Windows in |tracked_windows_|. base::flat_map root_windows_; // Number of times that occlusion has been recomputed in this process. We keep // track of this for tests. int num_times_occlusion_recomputed_ = 0; // Number of times that the current call to MaybeComputeOcclusion() has // recomputed occlusion states. Always 0 when not in MaybeComputeOcclusion(). int num_times_occlusion_recomputed_in_current_step_ = 0; // Counter of the current occlusion tracking pause. int num_pause_occlusion_tracking_ = 0; // Tracks the observed windows. base::ScopedMultiSourceObservation window_observations_{this}; // Callback to be invoked for additional window has content check. WindowHasContentCallback window_has_content_callback_; // Optional factory to create occlusion change builder. OcclusionChangeBuilderFactory occlusion_change_builder_factory_; // Stores the window for which the occlusion tracker is computing the // occlusion based on target bounds, opacity, transform, and visibility // values. If the occlusion tracker is not computing for a specific window // (most of the time it is not), this will be nullptr. Window* target_occlusion_window_ = nullptr; }; } // namespace aura #endif // UI_AURA_WINDOW_OCCLUSION_TRACKER_H_