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