1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/ozone/platform/wayland/host/wayland_surface.h"
6 
7 #include <viewporter-client-protocol.h>
8 
9 #include "ui/gfx/geometry/rect_conversions.h"
10 #include "ui/gfx/geometry/size.h"
11 #include "ui/gfx/native_widget_types.h"
12 #include "ui/ozone/platform/wayland/common/wayland_util.h"
13 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
14 #include "ui/ozone/platform/wayland/host/wayland_window.h"
15 
16 namespace ui {
17 
WaylandSurface(WaylandConnection * connection,WaylandWindow * root_window)18 WaylandSurface::WaylandSurface(WaylandConnection* connection,
19                                WaylandWindow* root_window)
20     : connection_(connection),
21       root_window_(root_window),
22       surface_(connection->CreateSurface()) {}
23 
24 WaylandSurface::~WaylandSurface() = default;
25 
GetSurfaceId() const26 uint32_t WaylandSurface::GetSurfaceId() const {
27   if (!surface_)
28     return 0u;
29   return surface_.id();
30 }
31 
GetWidget() const32 gfx::AcceleratedWidget WaylandSurface::GetWidget() const {
33   return root_window_ ? root_window_->GetWidget() : gfx::kNullAcceleratedWidget;
34 }
35 
Initialize()36 bool WaylandSurface::Initialize() {
37   if (!surface_)
38     return false;
39 
40   static struct wl_surface_listener surface_listener = {
41       &WaylandSurface::Enter,
42       &WaylandSurface::Leave,
43   };
44   wl_surface_add_listener(surface_.get(), &surface_listener, this);
45 
46   if (connection_->viewporter()) {
47     viewport_.reset(
48         wp_viewporter_get_viewport(connection_->viewporter(), surface()));
49     if (!viewport_) {
50       LOG(ERROR) << "Failed to create wp_viewport";
51       return false;
52     }
53   }
54 
55   return true;
56 }
57 
UnsetRootWindow()58 void WaylandSurface::UnsetRootWindow() {
59   DCHECK(surface_);
60   root_window_ = nullptr;
61 }
62 
AttachBuffer(wl_buffer * buffer)63 void WaylandSurface::AttachBuffer(wl_buffer* buffer) {
64   // The logic in DamageBuffer currently relies on attachment coordinates of
65   // (0, 0). If this changes, then the calculation in DamageBuffer will also
66   // need to be updated.
67   wl_surface_attach(surface_.get(), buffer, 0, 0);
68   connection_->ScheduleFlush();
69 }
70 
UpdateBufferDamageRegion(const gfx::Rect & pending_damage_region,const gfx::Size & buffer_size)71 void WaylandSurface::UpdateBufferDamageRegion(
72     const gfx::Rect& pending_damage_region,
73     const gfx::Size& buffer_size) {
74   // Buffer-local coordinates are in pixels, surface coordinates are in DIP.
75   // The coordinate transformations from buffer pixel coordinates up to
76   // the surface-local coordinates happen in the following order:
77   //   1. buffer_transform (wl_surface.set_buffer_transform)
78   //   2. buffer_scale (wl_surface.set_buffer_scale)
79   //   3. crop and scale (wp_viewport.set*)
80   // Apply buffer_transform (wl_surface.set_buffer_transform).
81   gfx::Size bounds = wl::ApplyWaylandTransform(
82       buffer_size, wl::ToWaylandTransform(buffer_transform_));
83   // Apply buffer_scale (wl_surface.set_buffer_scale).
84   bounds = gfx::ScaleToCeiledSize(bounds, 1.f / buffer_scale_);
85   // Apply crop (wp_viewport.set_source).
86   gfx::Rect viewport_src = gfx::Rect(bounds);
87   if (!crop_rect_.IsEmpty()) {
88     viewport_src = gfx::ToEnclosedRect(
89         gfx::ScaleRect(crop_rect_, bounds.width(), bounds.height()));
90     wp_viewport_set_source(viewport(), wl_fixed_from_int(viewport_src.x()),
91                            wl_fixed_from_int(viewport_src.y()),
92                            wl_fixed_from_int(viewport_src.width()),
93                            wl_fixed_from_int(viewport_src.height()));
94   }
95   // Apply viewport scale (wp_viewport.set_destination).
96   gfx::Size viewport_dst = bounds;
97   if (!display_size_px_.IsEmpty()) {
98     viewport_dst =
99         gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_);
100   }
101 
102   if (connection_->compositor_version() >=
103       WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
104     // wl_surface_damage_buffer relies on compositor API version 4. See
105     // https://bit.ly/2u00lv6 for details.
106     // We don't need to apply any scaling because pending_damage_region is
107     // already in buffer coordinates.
108     wl_surface_damage_buffer(
109         surface_.get(), pending_damage_region.x(), pending_damage_region.y(),
110         pending_damage_region.width(), pending_damage_region.height());
111   } else {
112     // Calculate the damage region in surface coordinates.
113     // The calculation for damage region relies on the assumption: The buffer is
114     // always attached at surface location (0, 0).
115     // It's possible to write logic that accounts for attaching buffer at other
116     // locations, but it's currently unnecessary.
117 
118     // Apply buffer_transform (wl_surface.set_buffer_transform).
119     gfx::Rect damage =
120         wl::ApplyWaylandTransform(pending_damage_region, buffer_size,
121                                   wl::ToWaylandTransform(buffer_transform_));
122     // Apply buffer_scale (wl_surface.set_buffer_scale).
123     damage = gfx::ScaleToEnclosingRect(damage, 1.f / buffer_scale_);
124     // Adjust coordinates to |viewport_src| (wp_viewport.set_source).
125     damage = wl::TranslateBoundsToParentCoordinates(damage, viewport_src);
126     // Apply viewport scale (wp_viewport.set_destination).
127     damage = gfx::ScaleToEnclosingRect(
128         damage, static_cast<float>(viewport_dst.width()) / viewport_src.width(),
129         static_cast<float>(viewport_dst.height()) / viewport_src.height());
130 
131     wl_surface_damage(surface_.get(), damage.x(), damage.y(), damage.width(),
132                       damage.height());
133   }
134 
135   connection_->ScheduleFlush();
136 }
137 
Commit()138 void WaylandSurface::Commit() {
139   wl_surface_commit(surface_.get());
140   connection_->ScheduleFlush();
141 }
142 
SetBufferTransform(gfx::OverlayTransform transform)143 void WaylandSurface::SetBufferTransform(gfx::OverlayTransform transform) {
144   DCHECK(transform != gfx::OVERLAY_TRANSFORM_INVALID);
145   if (buffer_transform_ == transform)
146     return;
147 
148   buffer_transform_ = transform;
149   wl_output_transform wl_transform = wl::ToWaylandTransform(buffer_transform_);
150   wl_surface_set_buffer_transform(surface_.get(), wl_transform);
151 }
152 
SetBufferScale(int32_t new_scale,bool update_bounds)153 void WaylandSurface::SetBufferScale(int32_t new_scale, bool update_bounds) {
154   DCHECK_GT(new_scale, 0);
155 
156   if (new_scale == buffer_scale_)
157     return;
158 
159   buffer_scale_ = new_scale;
160   wl_surface_set_buffer_scale(surface_.get(), buffer_scale_);
161 
162   if (!display_size_px_.IsEmpty()) {
163     gfx::Size viewport_dst =
164         gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_);
165     wp_viewport_set_destination(viewport(), viewport_dst.width(),
166                                 viewport_dst.height());
167   }
168 
169   connection_->ScheduleFlush();
170 }
171 
SetOpaqueRegion(const gfx::Rect & region_px)172 void WaylandSurface::SetOpaqueRegion(const gfx::Rect& region_px) {
173   // It's important to set opaque region for opaque windows (provides
174   // optimization hint for the Wayland compositor).
175   if (!root_window_ || !root_window_->IsOpaqueWindow())
176     return;
177 
178   wl::Object<wl_region> region(
179       wl_compositor_create_region(connection_->compositor()));
180   gfx::Rect region_dip =
181       gfx::ScaleToEnclosingRect(region_px, 1.f / buffer_scale_);
182   wl_region_add(region.get(), region_dip.x(), region_dip.y(),
183                 region_dip.width(), region_dip.height());
184 
185   wl_surface_set_opaque_region(surface_.get(), region.get());
186 
187   connection_->ScheduleFlush();
188 }
189 
SetViewportSource(const gfx::RectF & src_rect)190 void WaylandSurface::SetViewportSource(const gfx::RectF& src_rect) {
191   if (src_rect == crop_rect_) {
192     return;
193   } else if (src_rect.IsEmpty() || src_rect == gfx::RectF{0.f, 0.f, 1.f, 1.f}) {
194     wp_viewport_set_source(viewport(), wl_fixed_from_int(-1),
195                            wl_fixed_from_int(-1), wl_fixed_from_int(-1),
196                            wl_fixed_from_int(-1));
197     return;
198   }
199 
200   crop_rect_ = src_rect;
201 }
202 
SetViewportDestination(const gfx::Size & dest_size_px)203 void WaylandSurface::SetViewportDestination(const gfx::Size& dest_size_px) {
204   if (dest_size_px == display_size_px_) {
205     return;
206   } else if (dest_size_px.IsEmpty()) {
207     wp_viewport_set_destination(viewport(), -1, -1);
208   }
209   display_size_px_ = dest_size_px;
210   gfx::Size viewport_dst =
211       gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_);
212   wp_viewport_set_destination(viewport(), viewport_dst.width(),
213                               viewport_dst.height());
214 }
215 
CreateSubsurface(WaylandSurface * parent)216 wl::Object<wl_subsurface> WaylandSurface::CreateSubsurface(
217     WaylandSurface* parent) {
218   DCHECK(parent);
219   wl_subcompositor* subcompositor = connection_->subcompositor();
220   DCHECK(subcompositor);
221   wl::Object<wl_subsurface> subsurface(wl_subcompositor_get_subsurface(
222       subcompositor, surface_.get(), parent->surface_.get()));
223   return subsurface;
224 }
225 
226 // static
Enter(void * data,struct wl_surface * wl_surface,struct wl_output * output)227 void WaylandSurface::Enter(void* data,
228                            struct wl_surface* wl_surface,
229                            struct wl_output* output) {
230   if (auto* root_window = static_cast<WaylandSurface*>(data)->root_window_)
231     root_window->AddEnteredOutputId(output);
232 }
233 
234 // static
Leave(void * data,struct wl_surface * wl_surface,struct wl_output * output)235 void WaylandSurface::Leave(void* data,
236                            struct wl_surface* wl_surface,
237                            struct wl_output* output) {
238   if (auto* root_window = static_cast<WaylandSurface*>(data)->root_window_)
239     root_window->RemoveEnteredOutputId(output);
240 }
241 
242 }  // namespace ui
243