blob: fc4cf87eb5b8f5abe93bc87f43700ecdb7f8079d [file] [log] [blame]
// 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 <list>
#include <set>
#include <utility>
#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<std::string*>* const kClientSurfaceIdKey;
// A property key to store the window session Id set by client or full_restore
// component.
extern const ui::ClassProperty<int32_t>* 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<bool(int64_t, int64_t)>;
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(base::TimeTicks frame_time)>;
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(const gfx::PresentationFeedback&)>;
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);
// Sets the background color that shall be associated with the next buffer
// commit.
void SetBackgroundColor(absl::optional<SkColor> background_color);
// 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<viz::SurfaceId()> 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<gfx::GpuFence> 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<FrameCallback>* frame_callbacks,
std::list<PresentationCallback>* 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<base::trace_event::TracedValue> 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);
// If true is set, if this window has a focus, key events should be sent to
// the app, even if it is an ash shortcut (with some exceptions).
// See exo::Keyboard for more details.
void SetKeyboardShortcutsInhibited(bool inhibited);
// Returns whether keyboard shortcuts are inhibited.
bool is_keyboard_shortcuts_inhibited() const {
return keyboard_shortcuts_inhibited_;
}
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<cc::Region> 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 = gfx::ColorSpace::CreateSRGB();
bool is_tracking_occlusion = false;
// Represents optional background color that must be associated with the
// next buffer commit.
absl::optional<SkColor> background_color;
};
class BufferAttachment {
public:
BufferAttachment();
BufferAttachment(const BufferAttachment&) = delete;
BufferAttachment& operator=(const BufferAttachment&) = delete;
~BufferAttachment();
BufferAttachment(BufferAttachment&& buffer);
BufferAttachment& operator=(BufferAttachment&& buffer);
base::WeakPtr<Buffer>& buffer();
const base::WeakPtr<Buffer>& buffer() const;
const gfx::Size& size() const;
void Reset(base::WeakPtr<Buffer> buffer);
private:
base::WeakPtr<Buffer> 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<BufferAttachment> 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<FrameCallback> frame_callbacks;
// These lists contain the callbacks to notify the client when surface
// contents have been presented.
// Not persisted between commits.
std::list<PresentationCallback> presentation_callbacks;
// The acquire gpu fence to associate with the surface buffer.
// Not persisted between commits.
std::unique_ptr<gfx::GpuFence> 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<aura::Window> 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<Surface*, gfx::PointF>;
using SubSurfaceEntryList = std::list<SubSurfaceEntry>;
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<SurfaceObserver, true>::Unchecked observers_;
#if BUILDFLAG(IS_CHROMEOS_ASH)
std::unique_ptr<ash::OutputProtectionDelegate> output_protection_;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
viz::SurfaceId first_embedded_surface_id_;
viz::SurfaceId latest_embedded_surface_id_;
base::RepeatingCallback<viz::SurfaceId()> 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_;
bool keyboard_shortcuts_inhibited_ = false;
};
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_