// Copyright 2015 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 COMPONENTS_EXO_SURFACE_H_ #define COMPONENTS_EXO_SURFACE_H_ #include #include #include #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/time/time.h" #include "build/chromeos_buildflags.h" #include "cc/base/region.h" #include "components/exo/buffer.h" #include "components/exo/layer_tree_frame_sink_holder.h" #include "components/exo/surface_delegate.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/resources/transferable_resource.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "ui/aura/window.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rounded_corners_f.h" #include "ui/gfx/geometry/rrect_f.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/size_f.h" #include "ui/gfx/geometry/transform.h" #include "ui/gfx/native_widget_types.h" class SkPath; namespace ash { class OutputProtectionDelegate; } namespace base { namespace trace_event { class TracedValue; } } // namespace base namespace gfx { class ColorSpace; class GpuFence; struct PresentationFeedback; } namespace viz { class CompositorFrame; } namespace exo { class Buffer; class FrameSinkResourceManager; class SurfaceObserver; namespace subtle { class PropertyHelper; } // Counter-clockwise rotations. enum class Transform { NORMAL, ROTATE_90, ROTATE_180, ROTATE_270, FLIPPED, FLIPPED_ROTATE_90, FLIPPED_ROTATE_180, FLIPPED_ROTATE_270 }; // Priority for overlay promotion. enum class OverlayPriority { LOW, REGULAR, REQUIRED }; // A property key to store the surface Id set by the client. extern const ui::ClassProperty* const kClientSurfaceIdKey; // A property key to store the window session Id set by client or full_restore // component. extern const ui::ClassProperty* const kWindowSessionId; // This class represents a rectangular area that is displayed on the screen. // It has a location, size and pixel contents. class Surface final : public ui::PropertyHandler { public: using PropertyDeallocator = void (*)(int64_t value); using LeaveEnterCallback = base::RepeatingCallback; Surface(); Surface(const Surface&) = delete; Surface& operator=(const Surface&) = delete; ~Surface() override; // Type-checking downcast routine. static Surface* AsSurface(const aura::Window* window); aura::Window* window() { return window_.get(); } void set_leave_enter_callback(LeaveEnterCallback callback) { leave_enter_callback_ = callback; } // Called when the display the surface is on has changed. // Returns true if successful, and false if it fails. bool UpdateDisplay(int64_t old_id, int64_t new_id); // Called when the output is added for new display. void OnNewOutputAdded(); // Set a buffer as the content of this surface. A buffer can only be attached // to one surface at a time. void Attach(Buffer* buffer); void Attach(Buffer* buffer, gfx::Vector2d offset); gfx::Vector2d GetBufferOffset(); // Returns whether the surface has an uncommitted attached buffer. bool HasPendingAttachedBuffer() const; // Describe the regions where the pending buffer is different from the // current surface contents, and where the surface therefore needs to be // repainted. void Damage(const gfx::Rect& rect); // Request notification when it's a good time to produce a new frame. Useful // for throttling redrawing operations, and driving animations. using FrameCallback = base::RepeatingCallback; void RequestFrameCallback(const FrameCallback& callback); // Request notification when the next frame is displayed. Useful for // throttling redrawing operations, and driving animations. using PresentationCallback = base::RepeatingCallback; void RequestPresentationCallback(const PresentationCallback& callback); // This sets the region of the surface that contains opaque content. void SetOpaqueRegion(const cc::Region& region); // This sets the region of the surface that can receive pointer and touch // events. The region is clipped to the surface bounds. void SetInputRegion(const cc::Region& region); const cc::Region& hit_test_region() const { return hit_test_region_; } // This resets the region of the surface that can receive pointer and touch // events to be wide-open. This will be clipped to the surface bounds. void ResetInputRegion(); // This overrides the input region to the surface bounds with an outset. // TODO(domlaskowski): Remove this once client-driven resizing is removed. void SetInputOutset(int outset); // This sets the scaling factor used to interpret the contents of the buffer // attached to the surface. Note that if the scale is larger than 1, then you // have to attach a buffer that is larger (by a factor of scale in each // dimension) than the desired surface size. void SetBufferScale(float scale); // This sets the transformation used to interpret the contents of the buffer // attached to the surface. void SetBufferTransform(Transform transform); // Functions that control sub-surface state. All sub-surface state is // double-buffered and will be applied when Commit() is called. void AddSubSurface(Surface* sub_surface); void RemoveSubSurface(Surface* sub_surface); // Allow for finer granularity for sub surface positioning. void SetSubSurfacePosition(Surface* sub_surface, const gfx::PointF& position); void PlaceSubSurfaceAbove(Surface* sub_surface, Surface* reference); void PlaceSubSurfaceBelow(Surface* sub_surface, Surface* sibling); void OnSubSurfaceCommit(); void SetRoundedCorners(const gfx::RRectF& rounded_corners_bounds); void SetOverlayPriorityHint(OverlayPriority hint); // This sets the surface viewport for scaling. void SetViewport(const gfx::SizeF& viewport); // This sets the surface crop rectangle. void SetCrop(const gfx::RectF& crop); // This sets the only visible on secure output flag, preventing it from // appearing in screenshots or from being viewed on non-secure displays. void SetOnlyVisibleOnSecureOutput(bool only_visible_on_secure_output); // This sets the blend mode that will be used when drawing the surface. void SetBlendMode(SkBlendMode blend_mode); // This sets the alpha value that will be applied to the whole surface. void SetAlpha(float alpha); // Request that surface should have the specified frame type. void SetFrame(SurfaceFrameType type); // Request that the server should start resize on this surface. void SetServerStartResize(); // Request that surface should use a specific set of frame colors. void SetFrameColors(SkColor active_color, SkColor inactive_color); // Request that surface should have a specific startup ID string. void SetStartupId(const char* startup_id); // Request that surface should have a specific application ID string. void SetApplicationId(const char* application_id); // Whether to show/hide the shelf when fullscreen. If true, the titlebar/shelf // will show when the mouse moves to the top/bottom of the screen. If false // (plain fullscreen), the titlebar and shelf are always hidden. void SetUseImmersiveForFullscreen(bool value); // Called to show the snap preview to the primary or secondary position, or // to hide it. void ShowSnapPreviewToSecondary(); void ShowSnapPreviewToPrimary(); void HideSnapPreview(); // Called when the client was snapped to primary or secondary position, or // reset. void SetSnappedToSecondary(); void SetSnappedToPrimary(); void UnsetSnap(); // Whether the current client window can go back, as per its navigation list. void SetCanGoBack(); void UnsetCanGoBack(); // This sets the color space for the buffer for this surface. void SetColorSpace(gfx::ColorSpace color_space); // Request "parent" for surface. void SetParent(Surface* parent, const gfx::Point& position); // Request that surface should have a specific ID assigned by client. void SetClientSurfaceId(const char* client_surface_id); std::string GetClientSurfaceId() const; // Enable embedding of an arbitrary viz surface in this exo surface. // If the callback is valid, a SurfaceDrawQuad will be emitted targeting // the returned SurfaceId each frame. void SetEmbeddedSurfaceId( base::RepeatingCallback surface_id_callback); // Set the size of the embedded surface, to allow proper scaling. void SetEmbeddedSurfaceSize(const gfx::Size& size); // Request that the attached surface buffer at the next commit is associated // with a gpu fence to be signaled when the buffer is ready for use. void SetAcquireFence(std::unique_ptr gpu_fence); // Returns whether the surface has an uncommitted acquire fence. bool HasPendingAcquireFence() const; // Request a callback when the buffer attached at the next commit is // no longer used by that commit. void SetPerCommitBufferReleaseCallback( Buffer::PerCommitExplicitReleaseCallback callback); // Whether the surface has an uncommitted per-commit buffer release callback. bool HasPendingPerCommitBufferReleaseCallback() const; // Surface state (damage regions, attached buffers, etc.) is double-buffered. // A Commit() call atomically applies all pending state, replacing the // current state. Commit() is not guaranteed to be synchronous. See // CommitSurfaceHierarchy() below. void Commit(); // This will commit all pending state of the surface and its descendants by // recursively calling CommitSurfaceHierarchy() for each sub-surface. // If |synchronized| is set to false, then synchronized surfaces should not // commit pending state. void CommitSurfaceHierarchy(bool synchronized); // This will append current callbacks for surface and its descendants to // |frame_callbacks| and |presentation_callbacks|. void AppendSurfaceHierarchyCallbacks( std::list* frame_callbacks, std::list* presentation_callbacks); // This will append contents for surface and its descendants to frame. void AppendSurfaceHierarchyContentsToFrame( const gfx::PointF& origin, float device_scale_factor, FrameSinkResourceManager* resource_manager, viz::CompositorFrame* frame); // Returns true if surface is in synchronized mode. bool IsSynchronized() const; // Returns true if surface should receive input events. bool IsInputEnabled(Surface* surface) const; // Returns false if the hit test region is empty. bool HasHitTestRegion() const; // Returns true if |point| is inside the surface. bool HitTest(const gfx::Point& point) const; // Sets |mask| to the path that delineates the hit test region of the surface. void GetHitTestMask(SkPath* mask) const; // Set the surface delegate. void SetSurfaceDelegate(SurfaceDelegate* delegate); // Returns true if surface has been assigned a surface delegate. bool HasSurfaceDelegate() const; // Surface does not own observers. It is the responsibility of the observer // to remove itself when it is done observing. void AddSurfaceObserver(SurfaceObserver* observer); void RemoveSurfaceObserver(SurfaceObserver* observer); bool HasSurfaceObserver(const SurfaceObserver* observer) const; // Returns a trace value representing the state of the surface. std::unique_ptr AsTracedValue() const; // Called when the begin frame source has changed. void SetBeginFrameSource(viz::BeginFrameSource* begin_frame_source); // Returns the active content size. const gfx::SizeF& content_size() const { return content_size_; } // Returns the active content bounds for surface hierarchy. ie. the bounding // box of the surface and its descendants, in the local coordinate space of // the surface. const gfx::Rect& surface_hierarchy_content_bounds() const { return surface_hierarchy_content_bounds_; } // Returns true if the associated window is in 'stylus-only' mode. bool IsStylusOnly(); // Enables 'stylus-only' mode for the associated window. void SetStylusOnly(); // Notify surface that resources and subsurfaces' resources have been lost. void SurfaceHierarchyResourcesLost(); // Returns true if the surface's bounds should be filled opaquely. bool FillsBoundsOpaquely() const; bool HasPendingDamageForTesting(const gfx::Rect& damage) const { return pending_state_.damage.Contains(damage); } // Set occlusion tracking region for surface. void SetOcclusionTracking(bool tracking); // Triggers sending an occlusion update to observers. void OnWindowOcclusionChanged(); // Triggers sending a locking status to observers. // true : lock a frame to normal or restore state // false : unlock the previously locked frame void SetFrameLocked(bool lock); // True if the window for this surface has its occlusion tracked. bool IsTrackingOcclusion(); // Sets the |surface_hierarchy_content_bounds_|. void SetSurfaceHierarchyContentBoundsForTest(const gfx::Rect& content_bounds); // Requests that this surface should be made active (i.e. foregrounded). void RequestActivation(); // Requests that surface my have a window session ID assigned by client or // full_restore component. void SetWindowSessionId(int32_t window_session_id); int32_t GetWindowSessionId(); // Requests that the surface enters PIP mode. void SetPip(); // Requests that the surface exits PIP mode. void UnsetPip(); // Requests that the surface maintains the given aspect ratio. void SetAspectRatio(const gfx::SizeF& aspect_ratio); // Triggers send desk state of the window to observers. // |state| is the index of the desk which the window moved to, // or -1 for a window assigned to all desks. void OnDeskChanged(int state); // Requests that DesksController to move the window to a desk at |desk_index|. void MoveToDesk(int desk_index); // Requests that window is visible on all workspaces. void SetVisibleOnAllWorkspaces(); // Sets the initial workspace to restore a window to the corresponding desk. void SetInitialWorkspace(const char* initial_workspace); // Pins/locks a window to the screen so that the user cannot do anything // else before the mode is released. If trusted is set, it is an invocation // from a trusted app like a school test mode app. void Pin(bool trusted); // Release the pinned mode and allows the user to do other things again. void Unpin(); // Starts or ends throttling on the surface. void ThrottleFrameRate(bool on); private: struct State { State(); ~State(); bool operator==(const State& other) const; bool operator!=(const State& other) const { return !(*this == other); } cc::Region opaque_region; absl::optional input_region; int input_outset = 0; float buffer_scale = 1.0f; Transform buffer_transform = Transform::NORMAL; gfx::SizeF viewport; gfx::RectF crop; bool only_visible_on_secure_output = false; SkBlendMode blend_mode = SkBlendMode::kSrcOver; float alpha = 1.0f; gfx::Vector2d offset; gfx::ColorSpace color_space; bool is_tracking_occlusion = false; }; class BufferAttachment { public: BufferAttachment(); BufferAttachment(const BufferAttachment&) = delete; BufferAttachment& operator=(const BufferAttachment&) = delete; ~BufferAttachment(); BufferAttachment(BufferAttachment&& buffer); BufferAttachment& operator=(BufferAttachment&& buffer); base::WeakPtr& buffer(); const base::WeakPtr& buffer() const; const gfx::Size& size() const; void Reset(base::WeakPtr buffer); private: base::WeakPtr buffer_; gfx::Size size_; }; // State for this surface. State is committed in a three step process: // 1. Pending state is accummulated into before commit. // 2. On commit, state is copied to a cached state. This is to support // synchronized commit of a tree of surfaces. When the tree of surfaces is // set to be synchronized, the state of the tree will not be committed // until the root of the tree (precisely, until a unsynchronized root of a // subtree) is committed. // 3. State is committed. // Some fields are persisted between commits (e.g. which buffer is attached), // and some fields are not (e.g. acquire fence). For fields that are // persisted, they either need to be copyable, or if they are move only, they // need to be wrapped in absl::optional and only copied on commit if they // have been changed. Not doing this can lead to broken behaviour, such as // losing the attached buffer if some unrelated field is updated in a commit. // If you add new fields to this struct, please document whether the field // should be persisted between commits. // See crbug.com/1283305 for context. struct ExtendedState { ExtendedState(); ~ExtendedState(); State basic_state; // The buffer that will become the content of surface. // Persisted between commits. absl::optional buffer; // The rounded corners bounds for the surface. // Persisted between commits. gfx::RRectF rounded_corners_bounds; // The damage region to schedule paint for. // Not persisted between commits. cc::Region damage; // These lists contain the callbacks to notify the client when it is a good // time to start producing a new frame. // Not persisted between commits. std::list frame_callbacks; // These lists contain the callbacks to notify the client when surface // contents have been presented. // Not persisted between commits. std::list presentation_callbacks; // The acquire gpu fence to associate with the surface buffer. // Not persisted between commits. std::unique_ptr acquire_fence; // Callback to notify about the per-commit buffer release. The wayland // Exo backend uses this callback to implement the immediate_release // event of the explicit sync protocol. // Not persisted between commits. Buffer::PerCommitExplicitReleaseCallback per_commit_explicit_release_callback_; // The hint for overlay prioritization // Persisted between commits. OverlayPriority overlay_priority_hint = OverlayPriority::REGULAR; }; friend class subtle::PropertyHelper; // Updates current_resource_ with a new resource id corresponding to the // contents of the attached buffer (or id 0, if no buffer is attached). // UpdateSurface must be called afterwards to ensure the release callback // will be called. void UpdateResource(FrameSinkResourceManager* resource_manager); // Updates buffer_transform_ to match the current buffer parameters. void UpdateBufferTransform(bool y_invert); // Puts the current surface into a draw quad, and appends the draw quads into // the |frame|. void AppendContentsToFrame(const gfx::PointF& origin, float device_scale_factor, viz::CompositorFrame* frame); // Update surface content size base on current buffer size. void UpdateContentSize(); // This returns true when the surface has some contents assigned to it. bool has_contents() const { return state_.buffer.has_value() && !state_.buffer->size().IsEmpty(); } // This window has the layer which contains the Surface contents. std::unique_ptr window_; // This true, if sub_surfaces_ has changes (order, position, etc). bool sub_surfaces_changed_ = false; // This is the size of the last committed contents. gfx::SizeF content_size_; // This is the bounds of the last committed surface hierarchy contents. gfx::Rect surface_hierarchy_content_bounds_; // This is true when Attach() has been called and new contents should be // cached next time Commit() is called. bool has_pending_contents_ = false; // This is true when new contents are cached and should take effect next time // synchronized CommitSurfaceHierarchy() is called. bool has_cached_contents_ = false; // This is the state that has yet to be cached. ExtendedState pending_state_; // This is the state that has yet to be committed. ExtendedState cached_state_; // This is the state that has been committed. ExtendedState state_; // Cumulative input region of surface and its sub-surfaces. cc::Region hit_test_region_; // The stack of sub-surfaces to take effect when Commit() is called. // Bottom-most sub-surface at the front of the list and top-most sub-surface // at the back. using SubSurfaceEntry = std::pair; using SubSurfaceEntryList = std::list; SubSurfaceEntryList pending_sub_surfaces_; SubSurfaceEntryList sub_surfaces_; // The last resource that was sent to a surface. viz::TransferableResource current_resource_; // Whether the last resource that was sent to a surface has an alpha channel. bool current_resource_has_alpha_ = false; // This is true if a call to Commit() as been made but // CommitSurfaceHierarchy() has not yet been called. bool needs_commit_surface_ = false; // This is true if UpdateResources() should be called. bool needs_update_resource_ = true; // The current buffer transform matrix. It specifies the transformation from // normalized buffer coordinates to post-tranform buffer coordinates. gfx::Transform buffer_transform_; // This is set when the compositing starts and passed to active frame // callbacks when compositing successfully ends. base::TimeTicks last_compositing_start_time_; // This can be set to have some functions delegated. E.g. ShellSurface class // can set this to handle Commit() and apply any double buffered state it // maintains. SurfaceDelegate* delegate_ = nullptr; // Surface observer list. Surface does not own the observers. base::ObserverList::Unchecked observers_; #if BUILDFLAG(IS_CHROMEOS_ASH) std::unique_ptr output_protection_; #endif // BUILDFLAG(IS_CHROMEOS_ASH) viz::SurfaceId first_embedded_surface_id_; viz::SurfaceId latest_embedded_surface_id_; base::RepeatingCallback get_current_surface_id_; // The embedded surface is actually |embedded_surface_size_|. This is used // for calculating clipping and scaling. gfx::Size embedded_surface_size_; LeaveEnterCallback leave_enter_callback_; }; class ScopedSurface { public: ScopedSurface(Surface* surface, SurfaceObserver* observer); ScopedSurface(const ScopedSurface&) = delete; ScopedSurface& operator=(const ScopedSurface&) = delete; virtual ~ScopedSurface(); Surface* get() { return surface_; } private: Surface* const surface_; SurfaceObserver* const observer_; }; } // namespace exo #endif // COMPONENTS_EXO_SURFACE_H_