// 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/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.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/size_f.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/transform.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 }; // 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() 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. void 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); void SetSubSurfacePosition(Surface* sub_surface, const gfx::Point& position); void PlaceSubSurfaceAbove(Surface* sub_surface, Surface* reference); void PlaceSubSurfaceBelow(Surface* sub_surface, Surface* sibling); void OnSubSurfaceCommit(); // This sets the surface viewport for scaling. void SetViewport(const gfx::Size& 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 right or left, or to hide it. void ShowSnapPreviewToRight(); void ShowSnapPreviewToLeft(); void HideSnapPreview(); // Called when the client was snapped to right or left, or reset. void SetSnappedToRight(); void SetSnappedToLeft(); 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::Point& 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::Size& 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(); 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::Size 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(); 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_; DISALLOW_COPY_AND_ASSIGN(BufferAttachment); }; struct ExtendedState { ExtendedState(); ~ExtendedState(); State basic_state; // The buffer that will become the content of surface. BufferAttachment buffer; // The damage region to schedule paint for. cc::Region damage; // These lists contain the callbacks to notify the client when it is a good // time to start producing a new frame. std::list frame_callbacks; // These lists contain the callbacks to notify the client when surface // contents have been presented. std::list presentation_callbacks; // The acquire gpu fence to associate with the surface buffer. 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. Buffer::PerCommitExplicitReleaseCallback per_commit_explicit_release_callback_; }; 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::Point& 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.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::Size 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_; DISALLOW_COPY_AND_ASSIGN(Surface); }; class ScopedSurface { public: ScopedSurface(Surface* surface, SurfaceObserver* observer); virtual ~ScopedSurface(); Surface* get() { return surface_; } private: Surface* const surface_; SurfaceObserver* const observer_; DISALLOW_COPY_AND_ASSIGN(ScopedSurface); }; } // namespace exo #endif // COMPONENTS_EXO_SURFACE_H_