| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_ |
| #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/files/scoped_file.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/rrect_f.h" |
| #include "ui/gfx/gpu_fence_handle.h" |
| #include "ui/gfx/native_widget_types.h" |
| #include "ui/gfx/overlay_priority_hint.h" |
| #include "ui/gfx/overlay_transform.h" |
| #include "ui/ozone/platform/wayland/common/wayland_object.h" |
| #include "ui/ozone/platform/wayland/host/wayland_zcr_color_space.h" |
| |
| struct wp_content_type_v1; |
| struct wp_fractional_scale_v1; |
| struct zwp_linux_buffer_release_v1; |
| struct zcr_blending_v1; |
| |
| namespace ui { |
| |
| class WaylandConnection; |
| class WaylandOutput; |
| class WaylandWindow; |
| class WaylandBufferHandle; |
| class WaylandZcrColorManagementSurface; |
| class WaylandZAuraSurface; |
| |
| // Wrapper of a wl_surface, owned by a WaylandWindow or a WlSubsurface. |
| class WaylandSurface { |
| public: |
| using ExplicitReleaseCallback = |
| base::OnceCallback<void(wl_buffer*, base::ScopedFD)>; |
| |
| WaylandSurface(WaylandConnection* connection, WaylandWindow* root_window); |
| WaylandSurface(const WaylandSurface&) = delete; |
| WaylandSurface& operator=(const WaylandSurface&) = delete; |
| ~WaylandSurface(); |
| |
| WaylandWindow* root_window() const { return root_window_; } |
| overlay_prioritized_surface* overlay_priority_surface() { |
| return overlay_priority_surface_.get(); |
| } |
| wl_surface* surface() const { return surface_.get(); } |
| wp_viewport* viewport() const { return viewport_.get(); } |
| zcr_blending_v1* blending() const { return blending_.get(); } |
| |
| uint32_t buffer_id() const { return state_.buffer_id; } |
| float opacity() const { return state_.opacity; } |
| bool use_blending() const { return state_.use_blending; } |
| |
| const std::vector<uint32_t>& entered_outputs() const { |
| return entered_outputs_; |
| } |
| |
| // Creates a zaura_surface extension object for this surface if it does not |
| // already exist and is supported by the server. Returns the surface |
| // extension. |
| WaylandZAuraSurface* CreateZAuraSurface(); |
| |
| // Resets the zaura_surface extension object for this surface if it exists. |
| // This may be done for the purposes of unmapping the toplevel surface when |
| // hiding the window (see crrev.com/c/3628350/comment/b6315793_85f0b073). |
| void ResetZAuraSurface(); |
| |
| WaylandZAuraSurface* zaura_surface() { return zaura_surface_.get(); } |
| |
| // Requests an explicit release for the next commit. |
| void RequestExplicitRelease(ExplicitReleaseCallback callback); |
| |
| // Returns an id that identifies the |wl_surface_|. |
| uint32_t get_surface_id() const { return surface_ ? surface_.id() : 0u; } |
| |
| // Returns a gfx::AcceleratedWidget that identifies the WaylandWindow that |
| // this WaylandSurface belongs to. |
| gfx::AcceleratedWidget get_widget() const; |
| |
| // Initializes the WaylandSurface and returns true iff success. |
| // This may return false if a wl_surface could not be created, for example. |
| bool Initialize(); |
| |
| // Unsets |root_window_|. This is intended to be used in special cases, where |
| // the underlying wl_surface must be kept alive with no root window associated |
| // (e.g: window/tab dragging sessions). |
| void UnsetRootWindow(); |
| void SetRootWindow(WaylandWindow* window); |
| |
| // Attaches the given wl_buffer to the underlying wl_surface at (0, 0). |
| // Returns true if wl_surface.attach will be called in ApplyPendingStates(). |
| bool AttachBuffer(WaylandBufferHandle* buffer_handle); |
| |
| // Describes where the surface needs to be repainted according to |
| // |buffer_pending_damage_region|, which should be in buffer coordinates (px). |
| void UpdateBufferDamageRegion(const gfx::Rect& damage_px); |
| |
| // Sets a non-null in-fence, must be combined with an AttachBuffer() and a |
| // Commit(). |
| void set_acquire_fence(gfx::GpuFenceHandle acquire_fence); |
| |
| // Sets an optional transformation for how the Wayland compositor interprets |
| // the contents of the buffer attached to this surface. |
| void set_buffer_transform(gfx::OverlayTransform transform) { |
| DCHECK(!apply_state_immediately_); |
| DCHECK(transform != gfx::OVERLAY_TRANSFORM_INVALID); |
| pending_state_.buffer_transform = transform; |
| return; |
| } |
| |
| // Sets the |buffer_scale| (with respect to the scale factor used by the GPU |
| // process) for the next submitted buffer. This helps Wayland compositor to |
| // determine buffer size in dip (GPU operates in pixels. So, when buffers are |
| // created, their requested size is in pixels). |
| void set_surface_buffer_scale(float scale); |
| |
| // Sets the region that is opaque on this surface in physical pixels. This is |
| // expected to be called whenever the region that the surface span changes or |
| // the opacity changes. Rects in |region_px| are specified surface-local, in |
| // physical pixels. If |region_px| is nullptr or empty, the opaque region is |
| // reset to empty. |
| void set_opaque_region(const std::vector<gfx::Rect>* region_px); |
| |
| // Sets the input region on this surface in physical pixels. |
| // The input region indicates which parts of the surface accept pointer and |
| // touch input events. This is expected to be called from ToplevelWindow |
| // whenever the region that the surface span changes or window state changes |
| // when custom frame is used. If |region_px| is nullptr, the input region is |
| // reset to cover the entire wl_surface. |
| void set_input_region(const gfx::Rect* region_px); |
| |
| // Set the crop uv of the attached wl_buffer. |
| // Unlike wp_viewport.set_source, this crops the buffer prior to |
| // |buffer_transform| being applied to the buffer, it will be transformed s.t. |
| // wp_viewport.source is called with correct params. |
| // See: |
| // https://cgit.freedesktop.org/wayland/wayland-protocols/tree/stable/viewporter/viewporter.xml |
| // If |crop| is empty, the source rectangle is unset. |
| // Note this method does not send corresponding wayland requests until |
| // attaching the next buffer. |
| void set_buffer_crop(const gfx::RectF& crop) { |
| DCHECK(!apply_state_immediately_); |
| pending_state_.crop = crop == gfx::RectF{1.f, 1.f} ? gfx::RectF() : crop; |
| } |
| |
| // Sets the opacity of the wl_surface using zcr_blending_v1_set_alpha. |
| // See: alpha-compositing-unstable-v1.xml |
| void set_opacity(const float opacity) { |
| DCHECK(!apply_state_immediately_); |
| if (blending()) |
| pending_state_.opacity = opacity; |
| } |
| |
| // Sets the blending equation of the wl_surface using |
| // zcr_blending_v1_set_blending. See: alpha-compositing-unstable-v1.xml |
| void set_blending(const bool use_blending) { |
| DCHECK(!apply_state_immediately_); |
| if (blending()) |
| pending_state_.use_blending = use_blending; |
| } |
| |
| // Set the destination size of the associated wl_surface according to |
| // |dest_size_px|, which should be in physical pixels. |
| // Note this method sends corresponding wayland requests immediately because |
| // it does not need a new buffer attach to take effect. |
| void set_viewport_destination(const gfx::SizeF& dest_size_px) { |
| DCHECK(!apply_state_immediately_); |
| pending_state_.viewport_px = dest_size_px; |
| } |
| |
| // Sets the priority hint for the overlay that is committed via this surface. |
| void set_overlay_priority(gfx::OverlayPriorityHint priority_hint) { |
| if (overlay_priority_surface()) |
| pending_state_.priority_hint = priority_hint; |
| } |
| |
| // Sets the rounded clip bounds for this surface. |
| void set_rounded_clip_bounds(const gfx::RRectF& rounded_clip_bounds) { |
| if (get_augmented_surface()) |
| pending_state_.rounded_clip_bounds = rounded_clip_bounds; |
| } |
| |
| // Sets the background color for this surface, which will be blended with the |
| // wl_buffer contents during the compositing step on the Wayland compositor |
| // side. |
| void set_background_color(absl::optional<SkColor4f> background_color) { |
| if (get_augmented_surface()) |
| pending_state_.background_color = background_color; |
| } |
| |
| // Sets whether this surface contains a video. |
| void set_contains_video(bool contains_video) { |
| pending_state_.contains_video = contains_video; |
| } |
| |
| // Creates a wl_subsurface relating this surface and a parent surface, |
| // |parent|. Callers take ownership of the wl_subsurface. |
| wl::Object<wl_subsurface> CreateSubsurface(WaylandSurface* parent); |
| |
| // When display is removed, the WaylandOutput from `entered_outputs_` should |
| // be removed. |
| void RemoveEnteredOutput(uint32_t id); |
| |
| // Set surface ColorSpace |
| void set_color_space(gfx::ColorSpace color_space); |
| |
| // Validates the |pending_state_| and generates the corresponding requests. |
| // Then copy |pending_states_| to |states_|. |
| void ApplyPendingState(); |
| |
| // Commits the underlying wl_surface, triggers a wayland connection flush if |
| // |flush| is true. |
| void Commit(bool flush = true); |
| |
| // Workaround used by GLSurfaceWayland when libgbm is not available. Causes |
| // SetSurfaceBufferScale() SetOpaqueRegion(), and SetInputRegion() to take |
| // effect immediately. |
| void ForceImmediateStateApplication(); |
| |
| // Asks the Wayland compositor to enable or disable the keyboard shortcuts |
| // inhibition for this surface. i.e: to receive key events even if they match |
| // compositor accelerators, e.g: Alt+Tab, etc. |
| void SetKeyboardShortcutsInhibition(bool enabled); |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(WaylandWindowTest, |
| DoesNotCreateSurfaceSyncOnCommitWithoutBuffers); |
| // Holds information about each explicit synchronization buffer release. |
| struct ExplicitReleaseInfo { |
| ExplicitReleaseInfo( |
| wl::Object<zwp_linux_buffer_release_v1>&& linux_buffer_release, |
| wl_buffer* buffer, |
| ExplicitReleaseCallback explicit_release_callback); |
| ~ExplicitReleaseInfo(); |
| |
| ExplicitReleaseInfo(const ExplicitReleaseInfo&) = delete; |
| ExplicitReleaseInfo& operator=(const ExplicitReleaseInfo&) = delete; |
| |
| ExplicitReleaseInfo(ExplicitReleaseInfo&&); |
| ExplicitReleaseInfo& operator=(ExplicitReleaseInfo&&); |
| |
| wl::Object<zwp_linux_buffer_release_v1> linux_buffer_release; |
| // The buffer associated with this explicit release. |
| raw_ptr<wl_buffer, DanglingUntriaged> buffer; |
| // The associated release callback with this request. |
| ExplicitReleaseCallback explicit_release_callback; |
| }; |
| |
| struct State { |
| State(); |
| State(const State& other) = delete; |
| State& operator=(const State& other); |
| ~State(); |
| |
| std::vector<gfx::Rect> damage_px; |
| std::vector<gfx::Rect> opaque_region_px; |
| absl::optional<gfx::Rect> input_region_px = absl::nullopt; |
| |
| // The current color space of the surface. |
| scoped_refptr<WaylandZcrColorSpace> color_space = nullptr; |
| |
| // The acquire gpu fence to associate with the surface buffer. |
| gfx::GpuFenceHandle acquire_fence; |
| |
| uint32_t buffer_id = 0; |
| // Note that this wl_buffer ptr is never cleared, even when the |
| // buffer_handle owning this wl_buffer is destroyed. Accessing this field |
| // should ensure wl_buffer exists by calling |
| // WaylandBufferManagerHost::EnsureBufferHandle(buffer_id). |
| raw_ptr<wl_buffer, DanglingUntriaged> buffer = nullptr; |
| gfx::Size buffer_size_px; |
| |
| // The buffer scale refers to the ratio between the buffer size and the |
| // window size. This allows support for high-DPI displays. |
| float buffer_scale_float = 1; |
| |
| // Transformation for how the compositor interprets the contents of the |
| // buffer. |
| gfx::OverlayTransform buffer_transform = gfx::OVERLAY_TRANSFORM_NONE; |
| |
| // Following fields are used to help determine the damage_region in |
| // surface-local coordinates if wl_surface_damage_buffer() is not available. |
| // Normalized bounds of the buffer to be displayed in |viewport_px|. |
| // If empty, no cropping is applied. |
| gfx::RectF crop = {0.f, 0.f}; |
| |
| // Current size of the destination of the viewport in physical pixels. |
| // Wayland compositor will scale the (cropped) buffer content to fit the |
| // |viewport_px|. |
| // If empty, no scaling is applied. |
| gfx::SizeF viewport_px = {0, 0}; |
| |
| // The opacity of the wl_surface used to call zcr_blending_v1_set_alpha. |
| float opacity = 1.f; |
| |
| // The blending equation of the wl_surface used to call |
| // zcr_blending_v1_set_blending. |
| bool use_blending = true; |
| |
| gfx::RRectF rounded_clip_bounds; |
| gfx::OverlayPriorityHint priority_hint = gfx::OverlayPriorityHint::kRegular; |
| |
| // Optional background color for this surface. This information |
| // can be used by Wayland compositor to correctly display delegated textures |
| // which require background color applied. |
| absl::optional<SkColor4f> background_color; |
| |
| // Whether or not this surface contains video, for wp_content_type_v1. |
| bool contains_video = false; |
| }; |
| |
| // The wayland scale refers to the scale factor between the buffer coordinates |
| // and Wayland surface coordinates. When SurfaceSubmissionInPixelCoordinates |
| // is true, this is always 1. Otherwise, this is buffer_scale_float unless the |
| // value is less than 1. In that case 1 is returned. Additionally, if |
| // viewporter surface scaling is disabled, the value will be rounded up to the |
| // next integer. |
| float GetWaylandScale(const State& state); |
| |
| bool IsViewportScaled(const State& state); |
| |
| // Tracks the last sent src and dst values across wayland protocol s.t. we |
| // skip resending them when possible. |
| wl_fixed_t src_set_[4] = {wl_fixed_from_int(-1), wl_fixed_from_int(-1), |
| wl_fixed_from_int(-1), wl_fixed_from_int(-1)}; |
| float dst_set_[2] = {-1.f, -1.f}; |
| // Tracks the last sent surface_scale value s.t. we skip resending. |
| // This is used by wl_surface_set_buffer_scale which only supports integer |
| // scales. |
| int32_t surface_scale_set_ = 1; |
| |
| wl::Object<wl_region> CreateAndAddRegion( |
| const std::vector<gfx::Rect>& region_px, |
| float buffer_scale); |
| |
| // wl_surface states that are stored in Wayland client. It moves to |state_| |
| // on ApplyPendingState(). |
| State pending_state_; |
| |
| // wl_surface states that are either active or will be active once Commit() is |
| // called. |
| State state_; |
| |
| // Creates (if not created) the synchronization surface and returns a pointer |
| // to it. |
| zwp_linux_surface_synchronization_v1* GetOrCreateSurfaceSync(); |
| augmented_surface* get_augmented_surface() { |
| return augmented_surface_.get(); |
| } |
| |
| const raw_ptr<WaylandConnection> connection_; |
| raw_ptr<WaylandWindow> root_window_ = nullptr; |
| bool apply_state_immediately_ = false; |
| wl::Object<wl_surface> surface_; |
| wl::Object<wp_viewport> viewport_; |
| wl::Object<zcr_blending_v1> blending_; |
| wl::Object<zwp_linux_surface_synchronization_v1> surface_sync_; |
| wl::Object<overlay_prioritized_surface> overlay_priority_surface_; |
| wl::Object<augmented_surface> augmented_surface_; |
| wl::Object<wp_content_type_v1> content_type_; |
| wl::Object<wp_fractional_scale_v1> fractional_scale_; |
| std::unique_ptr<WaylandZcrColorManagementSurface> |
| zcr_color_management_surface_; |
| std::unique_ptr<WaylandZAuraSurface> zaura_surface_; |
| base::flat_map<zwp_linux_buffer_release_v1*, ExplicitReleaseInfo> |
| linux_buffer_releases_; |
| ExplicitReleaseCallback next_explicit_release_request_; |
| |
| // A cached copy of connection->SurfaceSubmissionInPixelCoordinates(). While |
| // it is technically possible to handle this value as mutable, in practice |
| // it's constant. |
| const bool surface_submission_in_pixel_coordinates_; |
| |
| // Same as above except it caches |
| // connection->UseViewporterSurfaceScaling(). |
| const bool use_viewporter_surface_scaling_; |
| |
| // For top level window, stores outputs that the window is currently rendered |
| // at. |
| // |
| // Not used by popups. When sub-menus are hidden and shown again, Wayland |
| // 'repositions' them to wrong outputs by sending them leave and enter |
| // events so their list of entered outputs becomes meaningless after they have |
| // been hidden at least once. To determine which output the popup belongs to, |
| // we ask its parent. |
| std::vector<uint32_t> entered_outputs_; |
| |
| void ExplicitRelease(struct zwp_linux_buffer_release_v1* linux_buffer_release, |
| base::ScopedFD fence); |
| |
| // wl_surface_listener |
| static void Enter(void* data, |
| struct wl_surface* wl_surface, |
| struct wl_output* output); |
| static void Leave(void* data, |
| struct wl_surface* wl_surface, |
| struct wl_output* output); |
| |
| // wp_fractional_scale_v1_listener |
| static void PreferredScale( |
| void* data, |
| struct wp_fractional_scale_v1* wp_fractional_scale_v1, |
| uint32_t scale); |
| |
| // zwp_linux_buffer_release_v1_listener |
| static void FencedRelease( |
| void* data, |
| struct zwp_linux_buffer_release_v1* linux_buffer_release, |
| int32_t fence); |
| static void ImmediateRelease( |
| void* data, |
| struct zwp_linux_buffer_release_v1* linux_buffer_release); |
| }; |
| |
| } // namespace ui |
| |
| #endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_ |