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