1 // Copyright 2016 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_window.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "base/bind.h"
11 #include "build/chromeos_buildflags.h"
12 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_utils.h"
15 #include "ui/events/ozone/events_ozone.h"
16 #include "ui/events/platform/platform_event_source.h"
17 #include "ui/gfx/geometry/point_f.h"
18 #include "ui/ozone/common/features.h"
19 #include "ui/ozone/platform/wayland/common/wayland_util.h"
20 #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
21 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
22 #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
23 #include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
24 #include "ui/ozone/platform/wayland/host/wayland_pointer.h"
25 #include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
26 #include "ui/ozone/platform/wayland/host/wayland_zcr_cursor_shapes.h"
27 #include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h"
28
29 namespace {
30
OverlayStackOrderCompare(const ui::ozone::mojom::WaylandOverlayConfigPtr & i,const ui::ozone::mojom::WaylandOverlayConfigPtr & j)31 bool OverlayStackOrderCompare(
32 const ui::ozone::mojom::WaylandOverlayConfigPtr& i,
33 const ui::ozone::mojom::WaylandOverlayConfigPtr& j) {
34 return i->z_order < j->z_order;
35 }
36
37 } // namespace
38
39 namespace ui {
40
WaylandWindow(PlatformWindowDelegate * delegate,WaylandConnection * connection)41 WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate,
42 WaylandConnection* connection)
43 : delegate_(delegate),
44 connection_(connection),
45 wayland_overlay_delegation_enabled_(IsWaylandOverlayDelegationEnabled()),
46 accelerated_widget_(
47 connection->wayland_window_manager()->AllocateAcceleratedWidget()) {}
48
~WaylandWindow()49 WaylandWindow::~WaylandWindow() {
50 shutting_down_ = true;
51
52 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
53
54 if (wayland_overlay_delegation_enabled_) {
55 connection_->wayland_window_manager()->RemoveSubsurface(
56 GetWidget(), primary_subsurface_.get());
57 }
58 for (const auto& widget_subsurface : wayland_subsurfaces()) {
59 connection_->wayland_window_manager()->RemoveSubsurface(
60 GetWidget(), widget_subsurface.get());
61 }
62 if (root_surface_)
63 connection_->wayland_window_manager()->RemoveWindow(GetWidget());
64
65 if (parent_window_)
66 parent_window_->set_child_window(nullptr);
67 }
68
OnWindowLostCapture()69 void WaylandWindow::OnWindowLostCapture() {
70 delegate_->OnLostCapture();
71 }
72
UpdateBufferScale(bool update_bounds)73 void WaylandWindow::UpdateBufferScale(bool update_bounds) {
74 DCHECK(connection_->wayland_output_manager());
75 const auto* screen = connection_->wayland_output_manager()->wayland_screen();
76
77 // The client might not create screen at all.
78 if (!screen)
79 return;
80
81 const auto widget = GetWidget();
82
83 int32_t new_scale = 0;
84 if (parent_window_) {
85 new_scale = parent_window_->buffer_scale();
86 ui_scale_ = parent_window_->ui_scale_;
87 } else {
88 const auto display = (widget == gfx::kNullAcceleratedWidget)
89 ? screen->GetPrimaryDisplay()
90 : screen->GetDisplayForAcceleratedWidget(widget);
91 new_scale = connection_->wayland_output_manager()
92 ->GetOutput(display.id())
93 ->scale_factor();
94
95 if (display::Display::HasForceDeviceScaleFactor())
96 ui_scale_ = display::Display::GetForcedDeviceScaleFactor();
97 else
98 ui_scale_ = display.device_scale_factor();
99 }
100 // At this point, buffer_scale() still returns the old scale.
101 if (update_bounds)
102 SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / buffer_scale()));
103
104 root_surface_->SetBufferScale(new_scale, update_bounds);
105 }
106
GetWidget() const107 gfx::AcceleratedWidget WaylandWindow::GetWidget() const {
108 return accelerated_widget_;
109 }
110
SetPointerFocus(bool focus)111 void WaylandWindow::SetPointerFocus(bool focus) {
112 has_pointer_focus_ = focus;
113
114 // Whenever the window gets the pointer focus back, we must reinitialize the
115 // cursor. Otherwise, it is invalidated whenever the pointer leaves the
116 // surface and is not restored by the Wayland compositor.
117 if (has_pointer_focus_ && bitmap_) {
118 // Translate physical pixels to DIPs.
119 gfx::Point hotspot_in_dips =
120 gfx::ScaleToRoundedPoint(bitmap_->hotspot(), 1.0f / ui_scale_);
121 connection_->SetCursorBitmap(bitmap_->bitmaps(), hotspot_in_dips,
122 buffer_scale());
123 }
124 }
125
Show(bool inactive)126 void WaylandWindow::Show(bool inactive) {
127 if (background_buffer_id_ != 0u)
128 should_attach_background_buffer_ = true;
129 }
130
Hide()131 void WaylandWindow::Hide() {
132 NOTREACHED();
133 }
134
Close()135 void WaylandWindow::Close() {
136 delegate_->OnClosed();
137 }
138
IsVisible() const139 bool WaylandWindow::IsVisible() const {
140 NOTREACHED();
141 return false;
142 }
143
PrepareForShutdown()144 void WaylandWindow::PrepareForShutdown() {}
145
SetBounds(const gfx::Rect & bounds_px)146 void WaylandWindow::SetBounds(const gfx::Rect& bounds_px) {
147 if (bounds_px_ == bounds_px)
148 return;
149 bounds_px_ = bounds_px;
150
151 root_surface_->SetOpaqueRegion(bounds_px);
152 delegate_->OnBoundsChanged(bounds_px_);
153 }
154
GetBounds() const155 gfx::Rect WaylandWindow::GetBounds() const {
156 return bounds_px_;
157 }
158
SetTitle(const base::string16 & title)159 void WaylandWindow::SetTitle(const base::string16& title) {}
160
SetCapture()161 void WaylandWindow::SetCapture() {
162 // Wayland doesn't allow explicit grabs. Instead, it sends events to "entered"
163 // windows. That is, if user enters their mouse pointer to a window, that
164 // window starts to receive events. However, Chromium may want to reroute
165 // these events to another window. In this case, tell the window manager that
166 // this specific window has grabbed the events, and they will be rerouted in
167 // WaylandWindow::DispatchEvent method.
168 if (!HasCapture())
169 connection_->wayland_window_manager()->GrabLocatedEvents(this);
170 }
171
ReleaseCapture()172 void WaylandWindow::ReleaseCapture() {
173 if (HasCapture())
174 connection_->wayland_window_manager()->UngrabLocatedEvents(this);
175 // See comment in SetCapture() for details on wayland and grabs.
176 }
177
HasCapture() const178 bool WaylandWindow::HasCapture() const {
179 return connection_->wayland_window_manager()->located_events_grabber() ==
180 this;
181 }
182
ToggleFullscreen()183 void WaylandWindow::ToggleFullscreen() {}
184
Maximize()185 void WaylandWindow::Maximize() {}
186
Minimize()187 void WaylandWindow::Minimize() {}
188
Restore()189 void WaylandWindow::Restore() {}
190
GetPlatformWindowState() const191 PlatformWindowState WaylandWindow::GetPlatformWindowState() const {
192 // Remove normal state for all the other types of windows as it's only the
193 // WaylandToplevelWindow that supports state changes.
194 return PlatformWindowState::kNormal;
195 }
196
Activate()197 void WaylandWindow::Activate() {
198 NOTIMPLEMENTED_LOG_ONCE();
199 }
200
Deactivate()201 void WaylandWindow::Deactivate() {
202 NOTIMPLEMENTED_LOG_ONCE();
203 }
204
SetUseNativeFrame(bool use_native_frame)205 void WaylandWindow::SetUseNativeFrame(bool use_native_frame) {
206 // See comment below in ShouldUseNativeFrame.
207 NOTIMPLEMENTED_LOG_ONCE();
208 }
209
ShouldUseNativeFrame() const210 bool WaylandWindow::ShouldUseNativeFrame() const {
211 // This depends on availability of XDG-Decoration protocol extension.
212 NOTIMPLEMENTED_LOG_ONCE();
213 return false;
214 }
215
SetCursor(PlatformCursor cursor)216 void WaylandWindow::SetCursor(PlatformCursor cursor) {
217 scoped_refptr<BitmapCursorOzone> bitmap =
218 BitmapCursorFactoryOzone::GetBitmapCursor(cursor);
219 if (bitmap_ == bitmap)
220 return;
221
222 bitmap_ = bitmap;
223
224 if (!bitmap_) {
225 // Hide the cursor.
226 connection_->SetCursorBitmap(std::vector<SkBitmap>(), gfx::Point(),
227 buffer_scale());
228 return;
229 }
230 // Check for Wayland server-side cursor support (e.g. exo for lacros).
231 if (connection_->zcr_cursor_shapes()) {
232 base::Optional<int32_t> shape =
233 WaylandZcrCursorShapes::ShapeFromType(bitmap->type());
234 // If the server supports this cursor type, use a server-side cursor.
235 if (shape.has_value()) {
236 #if BUILDFLAG(IS_CHROMEOS_LACROS)
237 // Lacros should not load image assets for default cursors. See
238 // BitmapCursorFactoryOzone::GetDefaultCursor().
239 DCHECK(bitmap_->bitmaps().empty());
240 #endif // BUILDFLAG(IS_CHROMEOS_LACROS)
241 connection_->zcr_cursor_shapes()->SetCursorShape(shape.value());
242 return;
243 }
244 // Fall through to client-side bitmap cursors.
245 }
246 // Translate physical pixels to DIPs.
247 gfx::Point hotspot_in_dips =
248 gfx::ScaleToRoundedPoint(bitmap_->hotspot(), 1.0f / ui_scale_);
249 connection_->SetCursorBitmap(bitmap_->bitmaps(), hotspot_in_dips,
250 buffer_scale());
251 }
252
MoveCursorTo(const gfx::Point & location)253 void WaylandWindow::MoveCursorTo(const gfx::Point& location) {
254 NOTIMPLEMENTED();
255 }
256
ConfineCursorToBounds(const gfx::Rect & bounds)257 void WaylandWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
258 NOTIMPLEMENTED();
259 }
260
SetRestoredBoundsInPixels(const gfx::Rect & bounds_px)261 void WaylandWindow::SetRestoredBoundsInPixels(const gfx::Rect& bounds_px) {
262 restored_bounds_px_ = bounds_px;
263 }
264
GetRestoredBoundsInPixels() const265 gfx::Rect WaylandWindow::GetRestoredBoundsInPixels() const {
266 return restored_bounds_px_;
267 }
268
ShouldWindowContentsBeTransparent() const269 bool WaylandWindow::ShouldWindowContentsBeTransparent() const {
270 NOTIMPLEMENTED_LOG_ONCE();
271 return false;
272 }
273
SetAspectRatio(const gfx::SizeF & aspect_ratio)274 void WaylandWindow::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
275 NOTIMPLEMENTED_LOG_ONCE();
276 }
277
SetWindowIcons(const gfx::ImageSkia & window_icon,const gfx::ImageSkia & app_icon)278 void WaylandWindow::SetWindowIcons(const gfx::ImageSkia& window_icon,
279 const gfx::ImageSkia& app_icon) {
280 NOTIMPLEMENTED_LOG_ONCE();
281 }
282
SizeConstraintsChanged()283 void WaylandWindow::SizeConstraintsChanged() {}
284
CanDispatchEvent(const PlatformEvent & event)285 bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) {
286 if (event->IsMouseEvent())
287 return has_pointer_focus_;
288 if (event->IsKeyEvent())
289 return has_keyboard_focus_;
290 if (event->IsTouchEvent())
291 return has_touch_focus_;
292 if (event->IsScrollEvent())
293 return has_pointer_focus_;
294 return false;
295 }
296
DispatchEvent(const PlatformEvent & native_event)297 uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) {
298 Event* event = static_cast<Event*>(native_event);
299
300 if (event->IsLocatedEvent()) {
301 auto* event_grabber =
302 connection_->wayland_window_manager()->located_events_grabber();
303 auto* root_parent_window = GetRootParentWindow();
304
305 // Wayland sends locations in DIP so they need to be translated to
306 // physical pixels.
307 event->AsLocatedEvent()->set_location_f(gfx::ScalePoint(
308 event->AsLocatedEvent()->location_f(), buffer_scale(), buffer_scale()));
309
310 // We must reroute the events to the event grabber iff these windows belong
311 // to the same root parent window. For example, there are 2 top level
312 // Wayland windows. One of them (window_1) has a child menu window that is
313 // the event grabber. If the mouse is moved over the window_1, it must
314 // reroute the events to the event grabber. If the mouse is moved over the
315 // window_2, the events mustn't be rerouted, because that belongs to another
316 // stack of windows. Remember that Wayland sends local surface coordinates,
317 // and continuing rerouting all the events may result in events sent to the
318 // grabber even though the mouse is over another root window.
319 //
320 if (event_grabber &&
321 root_parent_window == event_grabber->GetRootParentWindow()) {
322 ConvertEventLocationToTargetWindowLocation(
323 event_grabber->GetBounds().origin(), GetBounds().origin(),
324 event->AsLocatedEvent());
325 return event_grabber->DispatchEventToDelegate(native_event);
326 }
327 }
328
329 // Dispatch all keyboard events to the root window.
330 if (event->IsKeyEvent())
331 return GetRootParentWindow()->DispatchEventToDelegate(event);
332
333 return DispatchEventToDelegate(native_event);
334 }
335
HandleSurfaceConfigure(int32_t widht,int32_t height,bool is_maximized,bool is_fullscreen,bool is_activated)336 void WaylandWindow::HandleSurfaceConfigure(int32_t widht,
337 int32_t height,
338 bool is_maximized,
339 bool is_fullscreen,
340 bool is_activated) {
341 NOTREACHED()
342 << "Only shell surfaces must receive HandleSurfaceConfigure calls.";
343 }
344
HandlePopupConfigure(const gfx::Rect & bounds_dip)345 void WaylandWindow::HandlePopupConfigure(const gfx::Rect& bounds_dip) {
346 NOTREACHED() << "Only shell popups must receive HandlePopupConfigure calls.";
347 }
348
OnCloseRequest()349 void WaylandWindow::OnCloseRequest() {
350 delegate_->OnCloseRequest();
351 }
352
OnDragEnter(const gfx::PointF & point,std::unique_ptr<OSExchangeData> data,int operation)353 void WaylandWindow::OnDragEnter(const gfx::PointF& point,
354 std::unique_ptr<OSExchangeData> data,
355 int operation) {}
356
OnDragMotion(const gfx::PointF & point,int operation)357 int WaylandWindow::OnDragMotion(const gfx::PointF& point, int operation) {
358 return -1;
359 }
360
OnDragDrop(std::unique_ptr<OSExchangeData> data)361 void WaylandWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) {}
362
OnDragLeave()363 void WaylandWindow::OnDragLeave() {}
364
OnDragSessionClose(uint32_t dnd_action)365 void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) {}
366
SetBoundsDip(const gfx::Rect & bounds_dip)367 void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) {
368 SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale()));
369 }
370
Initialize(PlatformWindowInitProperties properties)371 bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) {
372 root_surface_ = std::make_unique<WaylandSurface>(connection_, this);
373 if (!root_surface_->Initialize()) {
374 LOG(ERROR) << "Failed to create wl_surface";
375 return false;
376 }
377
378 // Properties contain DIP bounds but the buffer scale is initially 1 so it's
379 // OK to assign. The bounds will be recalculated when the buffer scale
380 // changes.
381 bounds_px_ = properties.bounds;
382 opacity_ = properties.opacity;
383 type_ = properties.type;
384
385 connection_->wayland_window_manager()->AddWindow(GetWidget(), this);
386
387 if (!OnInitialize(std::move(properties)))
388 return false;
389
390 if (wayland_overlay_delegation_enabled_) {
391 primary_subsurface_ =
392 std::make_unique<WaylandSubsurface>(connection_, this);
393 if (!primary_subsurface_->surface())
394 return false;
395 connection_->wayland_window_manager()->AddSubsurface(
396 GetWidget(), primary_subsurface_.get());
397 }
398
399 connection_->ScheduleFlush();
400
401 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
402 delegate_->OnAcceleratedWidgetAvailable(GetWidget());
403
404 // Will do nothing for menus because they have got their scale above.
405 UpdateBufferScale(false);
406 root_surface_->SetOpaqueRegion(bounds_px_);
407
408 return true;
409 }
410
GetRootParentWindow()411 WaylandWindow* WaylandWindow::GetRootParentWindow() {
412 return parent_window_ ? parent_window_->GetRootParentWindow() : this;
413 }
414
AddEnteredOutputId(struct wl_output * output)415 void WaylandWindow::AddEnteredOutputId(struct wl_output* output) {
416 // Wayland does weird things for menus so instead of tracking outputs that
417 // we entered or left, we take that from the parent window and ignore this
418 // event.
419 if (wl::IsMenuType(type()))
420 return;
421
422 const uint32_t entered_output_id =
423 connection_->wayland_output_manager()->GetIdForOutput(output);
424 DCHECK_NE(entered_output_id, 0u);
425 auto result = entered_outputs_ids_.insert(entered_output_id);
426 DCHECK(result.first != entered_outputs_ids_.end());
427
428 UpdateBufferScale(true);
429 }
430
RemoveEnteredOutputId(struct wl_output * output)431 void WaylandWindow::RemoveEnteredOutputId(struct wl_output* output) {
432 // Wayland does weird things for menus so instead of tracking outputs that
433 // we entered or left, we take that from the parent window and ignore this
434 // event.
435 if (wl::IsMenuType(type()))
436 return;
437
438 const uint32_t left_output_id =
439 connection_->wayland_output_manager()->GetIdForOutput(output);
440 auto entered_output_id_it = entered_outputs_ids_.find(left_output_id);
441 // Workaround: when a user switches physical output between two displays,
442 // a window does not necessarily receive enter events immediately or until
443 // a user resizes/moves the window. It means that switching output between
444 // displays in a single output mode results in leave events, but the surface
445 // might not have received enter event before. Thus, remove the id of left
446 // output only if it was stored before.
447 if (entered_output_id_it != entered_outputs_ids_.end())
448 entered_outputs_ids_.erase(entered_output_id_it);
449
450 UpdateBufferScale(true);
451 }
452
UpdateCursorPositionFromEvent(std::unique_ptr<Event> event)453 void WaylandWindow::UpdateCursorPositionFromEvent(
454 std::unique_ptr<Event> event) {
455 DCHECK(event->IsLocatedEvent());
456
457 // This is a tricky part. Initially, Wayland sends events to surfaces the
458 // events are targeted for. But, in order to fulfill Chromium's assumptions
459 // about event targets, some of the events are rerouted and their locations
460 // are converted. The event we got here is rerouted and it has had its
461 // location fixed.
462 //
463 // Basically, this method must translate coordinates of all events
464 // in regards to top-level windows' coordinates as it's always located at
465 // origin (0,0) from Chromium point of view (remember that Wayland doesn't
466 // provide global coordinates to its clients). And it's totally fine to use it
467 // as the target. Thus, the location of the |event| is always converted using
468 // the top-level window's bounds as the target excluding cases, when the
469 // mouse/touch is over a top-level window.
470 auto* toplevel_window = GetRootParentWindow();
471 if (toplevel_window != this) {
472 ConvertEventLocationToTargetWindowLocation(
473 toplevel_window->GetBounds().origin(), GetBounds().origin(),
474 event->AsLocatedEvent());
475 }
476 auto* cursor_position = connection_->wayland_cursor_position();
477 if (cursor_position) {
478 cursor_position->OnCursorPositionChanged(
479 event->AsLocatedEvent()->location());
480 }
481 }
482
GetTopLevelWindow()483 WaylandWindow* WaylandWindow::GetTopLevelWindow() {
484 return parent_window_ ? parent_window_->GetTopLevelWindow() : this;
485 }
486
GetTopMostChildWindow()487 WaylandWindow* WaylandWindow::GetTopMostChildWindow() {
488 return child_window_ ? child_window_->GetTopMostChildWindow() : this;
489 }
490
IsOpaqueWindow() const491 bool WaylandWindow::IsOpaqueWindow() const {
492 return opacity_ == ui::PlatformWindowOpacity::kOpaqueWindow;
493 }
494
IsActive() const495 bool WaylandWindow::IsActive() const {
496 // Please read the comment where the IsActive method is declared.
497 return false;
498 }
499
DispatchEventToDelegate(const PlatformEvent & native_event)500 uint32_t WaylandWindow::DispatchEventToDelegate(
501 const PlatformEvent& native_event) {
502 auto* event = static_cast<Event*>(native_event);
503 if (event->IsLocatedEvent())
504 UpdateCursorPositionFromEvent(Event::Clone(*event));
505
506 bool handled = DispatchEventFromNativeUiEvent(
507 native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
508 base::Unretained(delegate_)));
509 return handled ? POST_DISPATCH_STOP_PROPAGATION : POST_DISPATCH_NONE;
510 }
511
TakeWaylandSurface()512 std::unique_ptr<WaylandSurface> WaylandWindow::TakeWaylandSurface() {
513 DCHECK(shutting_down_);
514 DCHECK(root_surface_);
515 root_surface_->UnsetRootWindow();
516 return std::move(root_surface_);
517 }
518
RequestSubsurface()519 bool WaylandWindow::RequestSubsurface() {
520 auto subsurface = std::make_unique<WaylandSubsurface>(connection_, this);
521 if (!subsurface->surface())
522 return false;
523 connection_->wayland_window_manager()->AddSubsurface(GetWidget(),
524 subsurface.get());
525 subsurface_stack_above_.push_back(subsurface.get());
526 auto result = wayland_subsurfaces_.emplace(std::move(subsurface));
527 DCHECK(result.second);
528 return true;
529 }
530
ArrangeSubsurfaceStack(size_t above,size_t below)531 bool WaylandWindow::ArrangeSubsurfaceStack(size_t above, size_t below) {
532 while (wayland_subsurfaces_.size() < above + below) {
533 if (!RequestSubsurface())
534 return false;
535 }
536
537 DCHECK(subsurface_stack_below_.size() + subsurface_stack_above_.size() >=
538 above + below);
539
540 if (subsurface_stack_above_.size() < above) {
541 auto splice_start = subsurface_stack_below_.begin();
542 for (size_t i = 0; i < below; ++i)
543 ++splice_start;
544 subsurface_stack_above_.splice(subsurface_stack_above_.end(),
545 subsurface_stack_below_, splice_start,
546 subsurface_stack_below_.end());
547
548 } else if (subsurface_stack_below_.size() < below) {
549 auto splice_start = subsurface_stack_above_.end();
550 for (size_t i = 0; i < below - subsurface_stack_below_.size(); ++i)
551 --splice_start;
552 subsurface_stack_below_.splice(subsurface_stack_below_.end(),
553 subsurface_stack_above_, splice_start,
554 subsurface_stack_above_.end());
555 }
556
557 DCHECK(subsurface_stack_below_.size() >= below);
558 DCHECK(subsurface_stack_above_.size() >= above);
559 return true;
560 }
561
CommitOverlays(std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> & overlays)562 bool WaylandWindow::CommitOverlays(
563 std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr>& overlays) {
564 // |overlays| is sorted from bottom to top.
565 std::sort(overlays.begin(), overlays.end(), OverlayStackOrderCompare);
566
567 // Find the location where z_oder becomes non-negative.
568 ozone::mojom::WaylandOverlayConfigPtr value =
569 ozone::mojom::WaylandOverlayConfig::New();
570 auto split = std::lower_bound(overlays.begin(), overlays.end(), value,
571 OverlayStackOrderCompare);
572 CHECK(split == overlays.end() || (*split)->z_order >= 0);
573 size_t num_primary_planes =
574 (split != overlays.end() && (*split)->z_order == 0) ? 1 : 0;
575
576 size_t above = (overlays.end() - split) - num_primary_planes;
577 size_t below = split - overlays.begin();
578
579 if (overlays.front()->z_order == INT32_MIN)
580 --below;
581
582 // Re-arrange the list of subsurfaces to fit the |overlays|. Request extra
583 // subsurfaces if needed.
584 if (!ArrangeSubsurfaceStack(above, below))
585 return false;
586
587 {
588 // Iterate through |subsurface_stack_below_|, setup subsurfaces and place
589 // them in corresponding order. Commit wl_buffers once a subsurface is
590 // configured.
591 auto overlay_iter = split - 1;
592 for (auto iter = subsurface_stack_below_.begin();
593 iter != subsurface_stack_below_.end(); ++iter, --overlay_iter) {
594 if (overlays.front()->z_order == INT32_MIN
595 ? overlay_iter >= ++overlays.begin()
596 : overlay_iter >= overlays.begin()) {
597 WaylandSurface* reference_above = nullptr;
598 if (overlay_iter == split - 1) {
599 // It's possible that |overlays| does not contain primary plane, we
600 // still want to place relative to the surface with z_order=0.
601 reference_above = primary_subsurface_->wayland_surface();
602 } else {
603 reference_above = (*std::next(iter))->wayland_surface();
604 }
605 (*iter)->ConfigureAndShowSurface(
606 (*overlay_iter)->transform, (*overlay_iter)->crop_rect,
607 (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend,
608 nullptr, reference_above);
609 connection_->buffer_manager_host()->CommitBufferInternal(
610 (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(),
611 /*wait_for_frame_callback=*/false);
612 } else {
613 // If there're more subsurfaces requested that we don't need at the
614 // moment, hide them.
615 (*iter)->Hide();
616 }
617 }
618
619 // Iterate through |subsurface_stack_above_|, setup subsurfaces and place
620 // them in corresponding order. Commit wl_buffers once a subsurface is
621 // configured.
622 overlay_iter = split + num_primary_planes;
623 for (auto iter = subsurface_stack_above_.begin();
624 iter != subsurface_stack_above_.end(); ++iter, ++overlay_iter) {
625 if (overlay_iter < overlays.end()) {
626 WaylandSurface* reference_below = nullptr;
627 if (overlay_iter == split + num_primary_planes) {
628 // It's possible that |overlays| does not contain primary plane, we
629 // still want to place relative to the surface with z_order=0.
630 reference_below = primary_subsurface_->wayland_surface();
631 } else {
632 reference_below = (*std::prev(iter))->wayland_surface();
633 }
634 (*iter)->ConfigureAndShowSurface(
635 (*overlay_iter)->transform, (*overlay_iter)->crop_rect,
636 (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend,
637 reference_below, nullptr);
638 connection_->buffer_manager_host()->CommitBufferInternal(
639 (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(),
640 /*wait_for_frame_callback=*/false);
641 } else {
642 // If there're more subsurfaces requested that we don't need at the
643 // moment, hide them.
644 (*iter)->Hide();
645 }
646 }
647 }
648
649 if (!wayland_overlay_delegation_enabled_) {
650 connection_->buffer_manager_host()->CommitBufferInternal(
651 root_surface(), (*split)->buffer_id, (*split)->damage_region,
652 /*wait_for_frame_callback=*/true);
653 return true;
654 }
655
656 if (num_primary_planes) {
657 primary_subsurface_->ConfigureAndShowSurface(
658 (*split)->transform, (*split)->crop_rect, (*split)->bounds_rect,
659 (*split)->enable_blend, nullptr, nullptr);
660 connection_->buffer_manager_host()->CommitBufferInternal(
661 primary_subsurface_->wayland_surface(), (*split)->buffer_id,
662 (*split)->damage_region, /*wait_for_frame_callback=*/false);
663 }
664
665 root_surface_->SetViewportDestination(bounds_px_.size());
666
667 if (overlays.front()->z_order == INT32_MIN) {
668 background_buffer_id_ = overlays.front()->buffer_id;
669 should_attach_background_buffer_ = true;
670 }
671
672 if (should_attach_background_buffer_) {
673 connection_->buffer_manager_host()->CommitBufferInternal(
674 root_surface(), background_buffer_id_, /*damage_region=*/gfx::Rect(),
675 /*wait_for_frame_callback=*/true);
676 should_attach_background_buffer_ = false;
677 } else {
678 // Subsurfaces are set to sync, above surface configs will only take effect
679 // when root_surface is committed.
680 connection_->buffer_manager_host()->CommitWithoutBufferInternal(
681 root_surface(), /*wait_for_frame_callback=*/true);
682 }
683
684 return true;
685 }
686
687 } // namespace ui
688