diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/ui/ozone/platform/wayland/host | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz |
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/ozone/platform/wayland/host')
59 files changed, 2011 insertions, 929 deletions
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc index fef56d4fcc8..e5017840921 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc @@ -43,6 +43,10 @@ bool GtkUiDelegateWayland::SetGdkWindowTransientFor( return false; } +void GtkUiDelegateWayland::ClearTransientFor(gfx::AcceleratedWidget parent) { + NOTIMPLEMENTED_LOG_ONCE(); +} + void GtkUiDelegateWayland::ShowGtkWindow(GtkWindow* window) { // TODO(crbug.com/1008755): Check if gtk_window_present_with_time is needed // here as well, similarly to what is done in X11 impl. diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h index dd7baf93bf7..4f3f606c5ac 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h +++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h @@ -25,6 +25,7 @@ class GtkUiDelegateWayland : public GtkUiDelegate { GdkWindow* GetGdkWindow(gfx::AcceleratedWidget window_id) override; bool SetGdkWindowTransientFor(GdkWindow* window, gfx::AcceleratedWidget parent) override; + void ClearTransientFor(gfx::AcceleratedWidget parent) override; void ShowGtkWindow(GtkWindow* window) override; private: diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc new file mode 100644 index 00000000000..c5e31fd5b9d --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc @@ -0,0 +1,115 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/wayland_auxiliary_window.h" + +#include "ui/ozone/platform/wayland/common/wayland_util.h" +#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" +#include "ui/ozone/platform/wayland/host/wayland_window_manager.h" + +namespace ui { + +WaylandAuxiliaryWindow::WaylandAuxiliaryWindow(PlatformWindowDelegate* delegate, + WaylandConnection* connection) + : WaylandWindow(delegate, connection) {} + +WaylandAuxiliaryWindow::~WaylandAuxiliaryWindow() = default; + +void WaylandAuxiliaryWindow::Show(bool inactive) { + if (subsurface_) + return; + + CreateSubsurface(); + UpdateBufferScale(false); +} + +void WaylandAuxiliaryWindow::Hide() { + if (!subsurface_) + return; + + subsurface_.reset(); + + // Detach buffer from surface in order to completely shutdown menus and + // tooltips, and release resources. + connection()->buffer_manager_host()->ResetSurfaceContents(root_surface()); +} + +bool WaylandAuxiliaryWindow::IsVisible() const { + return !!subsurface_; +} + +void WaylandAuxiliaryWindow::SetBounds(const gfx::Rect& bounds) { + auto old_bounds = GetBounds(); + WaylandWindow::SetBounds(bounds); + + if (old_bounds == bounds || !parent_window()) + return; + + auto subsurface_bounds_dip = + wl::TranslateWindowBoundsToParentDIP(this, parent_window()); + wl_subsurface_set_position(subsurface_.get(), subsurface_bounds_dip.x(), + subsurface_bounds_dip.y()); + root_surface()->Commit(); + connection()->ScheduleFlush(); +} + +void WaylandAuxiliaryWindow::CreateSubsurface() { + auto* parent = parent_window(); + if (!parent) { + // wl_subsurface can be used for several purposes: tooltips and drag arrow + // windows. If we are in a drag process, use the entered window. Otherwise, + // it must be a tooltip. + if (connection()->IsDragInProgress()) { + parent = connection()->data_drag_controller()->entered_window(); + set_parent_window(parent); + } else { + // If Aura does not not provide a reference parent window, needed by + // Wayland, we get the current focused window to place and show the + // tooltips. + parent = + connection()->wayland_window_manager()->GetCurrentFocusedWindow(); + } + } + + // Tooltip and drag arrow creation is an async operation. By the time Aura + // actually creates them, it is possible that the user has already moved the + // mouse/pointer out of the window that triggered the tooltip, or user is no + // longer in a drag/drop process. In this case, parent is nullptr. + if (!parent) + return; + + subsurface_ = root_surface()->CreateSubsurface(parent->root_surface()); + + auto subsurface_bounds_dip = + wl::TranslateWindowBoundsToParentDIP(this, parent); + + DCHECK(subsurface_); + // Convert position to DIP. + wl_subsurface_set_position(subsurface_.get(), subsurface_bounds_dip.x(), + subsurface_bounds_dip.y()); + wl_subsurface_set_desync(subsurface_.get()); + parent->root_surface()->Commit(); + connection()->ScheduleFlush(); + + // Notify the observers the window has been configured. Please note that + // subsurface doesn't send ack configure events. Thus, notify the observers as + // soon as the subsurface is created. + connection()->wayland_window_manager()->NotifyWindowConfigured(this); +} + +bool WaylandAuxiliaryWindow::OnInitialize( + PlatformWindowInitProperties properties) { + // If we do not have parent window provided, we must always use a focused + // window or a window that entered drag whenever the subsurface is created. + if (properties.parent_widget == gfx::kNullAcceleratedWidget) { + DCHECK(!parent_window()); + return true; + } + set_parent_window(GetParentWindow(properties.parent_widget)); + return true; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h new file mode 100644 index 00000000000..0db19755696 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h @@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// 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_AUXILIARY_WINDOW_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_AUXILIARY_WINDOW_H_ + +#include "ui/ozone/platform/wayland/host/wayland_window.h" + +namespace ui { + +// A WaylandWindow implementation to show tooltips and arrow windows. +class WaylandAuxiliaryWindow : public WaylandWindow { + public: + WaylandAuxiliaryWindow(PlatformWindowDelegate* delegate, + WaylandConnection* connection); + WaylandAuxiliaryWindow(const WaylandAuxiliaryWindow&) = delete; + WaylandAuxiliaryWindow& operator=(const WaylandAuxiliaryWindow&) = delete; + ~WaylandAuxiliaryWindow() override; + + // PlatformWindow overrides: + void Show(bool inactive) override; + void Hide() override; + bool IsVisible() const override; + void SetBounds(const gfx::Rect& bounds) override; + + private: + // WaylandWindow overrides: + bool OnInitialize(PlatformWindowInitProperties properties) override; + + // Creates (if necessary) and shows a subsurface window. + void CreateSubsurface(); + + wl::Object<wl_subsurface> subsurface_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_AUXILIARY_WINDOW_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc index 8320574a540..c60c35e4ab4 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc @@ -8,14 +8,15 @@ #include <memory> #include "base/i18n/number_formatting.h" -#include "base/message_loop/message_loop_current.h" #include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/current_thread.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/linux/drm_util_linux.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_drm.h" #include "ui/ozone/platform/wayland/host/wayland_shm.h" +#include "ui/ozone/platform/wayland/host/wayland_subsurface.h" #include "ui/ozone/platform/wayland/host/wayland_surface.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h" @@ -65,8 +66,6 @@ class WaylandBufferManagerHost::Surface { ~Surface() = default; bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) { - DCHECK(!pending_buffer_); - // The window has already been destroyed. if (!wayland_surface_) return true; @@ -96,7 +95,7 @@ class WaylandBufferManagerHost::Surface { if (buffer->attached && !buffer->wl_buffer) return false; - pending_buffer_ = buffer; + pending_buffers_.push_back(buffer); MaybeProcessPendingBuffer(); return true; } @@ -109,35 +108,16 @@ class WaylandBufferManagerHost::Surface { size_t DestroyBuffer(uint32_t buffer_id) { auto* buffer = GetBuffer(buffer_id); - // We ack the submission of a front buffer whenever a previous front back - // becomes the back one and receives a buffer release callback. But the - // following can happen: - // - // Imagine the order: - // the front buffer is buffer 2, then - // Commit[1] - // Destroy[2] - // Commit[3] - // Release[1] - // Ack[3] <= this results in a wrong order of the callbacks. In reality, - // the buffer [1] must have been acked, because the buffer 2 was - // released. - // But if the buffer 2 is destroyed, the buffer release callback never - // comes for that buffer. Thus, if there is a submitted buffer, notify - // the client about successful swap. - // If the window has already been destroyed, no need to complete the - // submission. - if (buffer && !buffer->released && submitted_buffer_ && wayland_surface_) - CompleteSubmission(); - - if (prev_submitted_buffer_ == buffer) - prev_submitted_buffer_ = nullptr; - - if (pending_buffer_ == buffer) - pending_buffer_ = nullptr; - - auto result = buffers_.erase(buffer_id); - return result; + + // Treat destroying a buffer as a release, and make sure to call any + // OnSubmission callbacks that would be sent as a result of that. + if (buffer) { + buffer->released = true; + MaybeProcessSubmittedBuffers(); + base::Erase(pending_buffers_, buffer); + } + + return buffers_.erase(buffer_id); } void AttachWlBuffer(uint32_t buffer_id, wl::Object<wl_buffer> new_buffer) { @@ -154,8 +134,7 @@ class WaylandBufferManagerHost::Surface { if (buffer->wl_buffer) SetupBufferReleaseListener(buffer); - if (pending_buffer_ == buffer) - MaybeProcessPendingBuffer(); + MaybeProcessPendingBuffer(); } void ClearState() { @@ -165,9 +144,8 @@ class WaylandBufferManagerHost::Surface { ResetSurfaceContents(); - prev_submitted_buffer_ = nullptr; - submitted_buffer_ = nullptr; - pending_buffer_ = nullptr; + submitted_buffers_.clear(); + pending_buffers_.clear(); connection_->ScheduleFlush(); } @@ -176,16 +154,11 @@ class WaylandBufferManagerHost::Surface { if (!wayland_surface_) return; - wl_surface_attach(wayland_surface_->surface(), nullptr, 0, 0); - wl_surface_commit(wayland_surface_->surface()); - - // We cannot reset |prev_submitted_buffer_| here as long as the surface - // might have attached a new buffer and is about to receive a release - // callback. Check more comments below where the variable is declared. - contents_reset_ = true; + wayland_surface_->AttachBuffer(nullptr); + wayland_surface_->Commit(); // ResetSurfaceContents happens upon WaylandWindow::Hide call, which - // destroyes xdg_surface, xdg_popup, etc. They are going to be reinitialized + // destroys xdg_surface, xdg_popup, etc. They are going to be reinitialized // once WaylandWindow::Show is called. Thus, they will have to be configured // once again before buffers can be attached. configured_ = false; @@ -200,10 +173,10 @@ class WaylandBufferManagerHost::Surface { bool HasBuffers() const { return !buffers_.empty(); } - void OnWindowRemoved() { wayland_surface_ = nullptr; } - bool HasWindow() const { return !!wayland_surface_; } + void OnSurfaceRemoved() { wayland_surface_ = nullptr; } + bool HasSurface() const { return !!wayland_surface_; } - void OnWindowConfigured() { + void OnSurfaceConfigured() { if (configured_) return; @@ -226,50 +199,44 @@ class WaylandBufferManagerHost::Surface { using PresentationFeedbackQueue = std::vector<FeedbackInfo>; + // Holds information about a submitted buffer. + struct SubmissionInfo { + // ID of the submitted buffer. Buffers may be destroyed after they have been + // submitted but before we send OnSubmission for them, e.g. if the same + // buffer is submitted twice in a row. Keep the ID so we send OnSubmission + // even if a buffer is destroyed. + uint32_t buffer_id; + // Whether this buffer has had OnSubmission sent for it. + bool acked; + }; + bool CommitBufferInternal(WaylandBuffer* buffer) { DCHECK(buffer && wayland_surface_); - DCHECK(!pending_buffer_); - DCHECK(!submitted_buffer_); - submitted_buffer_ = buffer; - - // if the same buffer has been submitted again right after the client + // If the same buffer has been submitted again right after the client // received OnSubmission for that buffer, just damage the buffer and // commit the surface again. - if (prev_submitted_buffer_ != submitted_buffer_) { + if (submitted_buffers_.empty() || + submitted_buffers_.back().buffer_id != buffer->buffer_id) { // Once the BufferRelease is called, the buffer will be released. DCHECK(buffer->released); buffer->released = false; AttachBuffer(buffer); } + // If the client submits the same buffer twice, we need to store it twice, + // because the client will expect two acks for it. + submitted_buffers_.push_back( + SubmissionInfo{buffer->buffer_id, /*acked=*/false}); + DamageBuffer(buffer); SetupFrameCallback(); SetupPresentationFeedback(buffer->buffer_id); CommitSurface(); - connection_->ScheduleFlush(); - - // If the contents were reset, there is no buffer attached. It means we have - // to behave the same way as if it was the very first frame. Check the - // comment below where the |contents_reset_| is declared. - if (contents_reset_) { - prev_submitted_buffer_ = nullptr; - contents_reset_ = false; - } - - // If it was the very first frame, the surface has not had a back buffer - // before, and Wayland won't release the front buffer until next buffer is - // attached. Thus, notify about successful submission immediately. - // - // As said above, if the client submits the same buffer again, we must - // notify the client about the submission immediately as Wayland compositor - // is not going to send a release callback for a buffer committed more than - // once. - if (!prev_submitted_buffer_ || prev_submitted_buffer_ == submitted_buffer_) - CompleteSubmission(); + MaybeProcessSubmittedBuffers(); return true; } @@ -277,7 +244,7 @@ class WaylandBufferManagerHost::Surface { void DamageBuffer(WaylandBuffer* buffer) { DCHECK(wayland_surface_); - gfx::Rect pending_damage_region = std::move(buffer->damage_region); + gfx::Rect pending_damage_region = buffer->damage_region; // If the size of the damage region is empty, wl_surface_damage must be // supplied with the actual size of the buffer, which is going to be // committed. @@ -285,48 +252,17 @@ class WaylandBufferManagerHost::Surface { pending_damage_region.set_size(buffer->size); DCHECK(!pending_damage_region.size().IsEmpty()); - if (connection_->compositor_version() >= - WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { - // wl_surface_damage_buffer relies on compositor API version 4. See - // https://bit.ly/2u00lv6 for details. - // We don't need to apply any scaling because pending_damage_region is - // already in buffer coordinates. - wl_surface_damage_buffer( - wayland_surface_->surface(), pending_damage_region.x(), - pending_damage_region.y(), pending_damage_region.width(), - pending_damage_region.height()); - } else { - // The calculation for damage region relies on two assumptions: - // 1) The buffer is always attached at surface location (0, 0) - // 2) The API wl_surface::set_buffer_transform is not used. - // It's possible to write logic that accounts for both cases above, but - // it's currently unnecessary. - // - // Note: The damage region may not be an integer multiple of scale. To - // keep the implementation simple, the x() and y() coordinates round down, - // and the width() and height() calculations always add an extra pixel. - int scale = wayland_surface_->buffer_scale(); - wl_surface_damage(wayland_surface_->surface(), - pending_damage_region.x() / scale, - pending_damage_region.y() / scale, - pending_damage_region.width() / scale + 1, - pending_damage_region.height() / scale + 1); - } + wayland_surface_->Damage(pending_damage_region); } void AttachBuffer(WaylandBuffer* buffer) { DCHECK(wayland_surface_ && configured_); - - // The logic in DamageBuffer currently relies on attachment coordinates of - // (0, 0). If this changes, then the calculation in DamageBuffer will also - // need to be updated. - wl_surface_attach(wayland_surface_->surface(), buffer->wl_buffer.get(), 0, - 0); + wayland_surface_->AttachBuffer(buffer->wl_buffer.get()); } void CommitSurface() { DCHECK(wayland_surface_); - wl_surface_commit(wayland_surface_->surface()); + wayland_surface_->Commit(); } void SetupFrameCallback() { @@ -390,42 +326,23 @@ class WaylandBufferManagerHost::Surface { void OnRelease(struct wl_buffer* wl_buffer) { DCHECK(wl_buffer); + // Releases may not necessarily come in order, so search the submitted + // buffers. WaylandBuffer* buffer = nullptr; - // The Wayland compositor may release the buffer immediately after it has - // been submitted. Thus, check that wl_buffer belongs to either the - // submitted buffer or the previously submitted buffer. - if (submitted_buffer_ && submitted_buffer_->wl_buffer.get() == wl_buffer) { - buffer = submitted_buffer_; - DCHECK(buffer->wl_buffer.get() == wl_buffer); - } else { - buffer = prev_submitted_buffer_; - DCHECK(buffer && buffer->wl_buffer.get() == wl_buffer); + for (const auto& b : submitted_buffers_) { + auto* submitted_buffer = GetBuffer(b.buffer_id); + if (submitted_buffer && wl_buffer == submitted_buffer->wl_buffer.get()) { + buffer = submitted_buffer; + break; + } } - + DCHECK(buffer); DCHECK(!buffer->released); buffer->released = true; - // It may happen that the client has attached only one buffer and then - // destroyed the window. That means that we manually called OnRelease on - // that very first buffer attach as long as the surface has not had any - // buffers attached before. However, the |submitted_buffer_| can be null in - // the OnRelease and hit the DCHECK when the client does not continue - // attaching new buffers (only one has been attached) and destroyes the - // surface. In this case, the Wayland compositor releases the buffer and the - // DCHECK is hit, because we have already run the OnRelease call manually. - // Please note that the |prev_submitted_buffer_| is the buffer we have - // released manually, and when the Wayland compositor sends OnRelease, the - // validation of the wl_buffers succeeds because of that previous manual - // OnRelease call. - if (!submitted_buffer_) - return; - - // Although, the Wayland compositor has just released the previously - // attached buffer, which became a back buffer, we have to notify the client - // that next buffer has been attach and become the front one. Thus, mark the - // back buffer as released to ensure the DCHECK is not hit, and notify about - // successful submission of the front buffer. - CompleteSubmission(); + // A release means we may be able to send OnSubmission for previously + // submitted buffers. + MaybeProcessSubmittedBuffers(); } // wl_buffer_listener @@ -435,19 +352,49 @@ class WaylandBufferManagerHost::Surface { self->OnRelease(wl_buffer); } - void CompleteSubmission() { - DCHECK(submitted_buffer_); - auto id = submitted_buffer_->buffer_id; - prev_submitted_buffer_ = submitted_buffer_; - submitted_buffer_ = nullptr; - + void MaybeProcessSubmittedBuffers() { if (!wayland_surface_) return; + // We force an OnSubmission call for the very first buffer submitted, + // otherwise buffers are not acked in a quiescent state. We keep track of + // whether it has already been acked. A buffer may have already been acked + // if it is the first buffer submitted and it is destroyed before being + // explicitly released. In that case, don't send an OnSubmission. + if (submitted_buffers_.size() == 1u && !submitted_buffers_[0].acked) + ProcessOldestSubmittedBuffer(); + + // Buffers may be released out of order, but we need to provide the + // guarantee that OnSubmission will be called in order of buffer submission. + while (submitted_buffers_.size() >= 2) { + auto* buffer0 = GetBuffer(submitted_buffers_[0].buffer_id); + // Treat a buffer as released if it has been explicitly released or + // destroyed. + bool buffer0_released = !buffer0 || buffer0->released; + // We can send OnSubmission for the 2nd oldest buffer if the oldest buffer + // is released, or it's the same buffer. + if (!buffer0_released && + submitted_buffers_[0].buffer_id != submitted_buffers_[1].buffer_id) + break; + + DCHECK(submitted_buffers_[0].acked); + DCHECK(!submitted_buffers_[1].acked); + submitted_buffers_.erase(submitted_buffers_.begin()); + ProcessOldestSubmittedBuffer(); + } + } + + void ProcessOldestSubmittedBuffer() { + DCHECK(wayland_surface_); + DCHECK(!submitted_buffers_.empty()); + + submitted_buffers_.front().acked = true; + auto buffer_id = submitted_buffers_.front().buffer_id; + // We can now complete the latest submission. We had to wait for this // release because SwapCompletionCallback indicates to the client that the // previous buffer is available for reuse. - buffer_manager_->OnSubmission(wayland_surface_->GetRootWidget(), id, + buffer_manager_->OnSubmission(wayland_surface_->GetWidget(), buffer_id, gfx::SwapResult::SWAP_ACK); // If presentation feedback is not supported, use a fake feedback. This @@ -455,18 +402,19 @@ class WaylandBufferManagerHost::Surface { if (!connection_->presentation()) { DCHECK(feedback_queue_.empty()); buffer_manager_->OnPresentation( - wayland_surface_->GetWidget(), id, + wayland_surface_->GetWidget(), buffer_id, gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(), GetPresentationKindFlags(0))); } else { for (auto& info : feedback_queue_) { - if (info.buffer_id == id && !info.submission_completed) { + if (info.buffer_id == buffer_id && !info.submission_completed) { info.submission_completed = true; ProcessPresentationFeedbacks(); return; } } - NOTREACHED() << "Did not find matching feedback for buffer_id=" << id; + NOTREACHED() << "Did not find matching feedback for buffer_id=" + << buffer_id; } } @@ -561,9 +509,10 @@ class WaylandBufferManagerHost::Surface { } void MaybeProcessPendingBuffer() { + DCHECK_LE(pending_buffers_.size(), 6u); // There is nothing to process if there is no pending buffer or the window // has been destroyed. - if (!pending_buffer_ || !wayland_surface_) + if (pending_buffers_.empty() || !wayland_surface_) return; // This request may come earlier than the Wayland compositor has imported a @@ -580,19 +529,19 @@ class WaylandBufferManagerHost::Surface { // // The third case happens if the window hasn't been configured until a // request to attach a buffer to its surface is sent. - if (!pending_buffer_->wl_buffer || wl_frame_callback_ || !configured_) + auto* pending_buffer = pending_buffers_.front(); + if (!pending_buffer->wl_buffer || wl_frame_callback_ || !configured_) return; - auto* buffer = pending_buffer_; - pending_buffer_ = nullptr; - CommitBufferInternal(buffer); + pending_buffers_.erase(pending_buffers_.begin()); + CommitBufferInternal(pending_buffer); } // Widget this helper surface backs and has 1:1 relationship with the // WaylandWindow. - // Non-owned. The window this helper surface stores and submits buffers for. - const WaylandSurface* wayland_surface_; + // Non-owned. The surface this helper stores and submits buffers for. + WaylandSurface* wayland_surface_; // Non-owned pointer to the connection. WaylandConnection* const connection_; @@ -608,25 +557,17 @@ class WaylandBufferManagerHost::Surface { // operation. wl::Object<wl_callback> wl_frame_callback_; - // A presentation feedback provided by the Wayland server once frame is - // shown. - PresentationFeedbackQueue feedback_queue_; + // Queue of buffers which are pending to be submitted (look the comment + // in the CommitBuffer method). + std::vector<WaylandBuffer*> pending_buffers_; + + // Queue of buffers which have been submitted and are waiting to be + // acked (send OnSubmission) + std::vector<SubmissionInfo> submitted_buffers_; - // A buffer, which is pending to be submitted (look the comment in the - // CommitBuffer method). - WaylandBuffer* pending_buffer_ = nullptr; - // Current submitted buffer. - WaylandBuffer* submitted_buffer_ = nullptr; - // Previous submitted buffer. - WaylandBuffer* prev_submitted_buffer_ = nullptr; - - // If WaylandWindow becomes hidden, it may need to attach a null buffer to the - // surface it backed to avoid its contents shown on screen. However, it - // means that the Wayland compositor no longer sends new buffer release events - // as long as there has not been buffer attached and no submission callback is - // sent. To avoid this, |contents_reset_| can be used as an identification of - // a need to call submission callback manually. - bool contents_reset_ = false; + // Queue of buffers which have been acked and are waiting to have + // OnPresentation sent. + PresentationFeedbackQueue feedback_queue_; // If WaylandWindow has never been configured, do not try to attach // buffers to its surface. Otherwise, Wayland server will drop the connection @@ -653,25 +594,49 @@ WaylandBufferManagerHost::~WaylandBufferManagerHost() { void WaylandBufferManagerHost::OnWindowAdded(WaylandWindow* window) { DCHECK(window); - surfaces_[window->GetWidget()] = - std::make_unique<Surface>(window->wayland_surface(), connection_, this); + surfaces_[window->root_surface()] = + std::make_unique<Surface>(window->root_surface(), connection_, this); } void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) { DCHECK(window); - auto it = surfaces_.find(window->GetWidget()); + auto it = surfaces_.find(window->root_surface()); DCHECK(it != surfaces_.end()); - if (it->second->HasBuffers()) - it->second->OnWindowRemoved(); - else - surfaces_.erase(it); + if (it->second->HasBuffers()) { + it->second->OnSurfaceRemoved(); + surface_graveyard_.emplace_back(std::move(it->second)); + } + surfaces_.erase(it); } void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) { DCHECK(window); - auto it = surfaces_.find(window->GetWidget()); + auto it = surfaces_.find(window->root_surface()); + DCHECK(it != surfaces_.end()); + it->second->OnSurfaceConfigured(); +} + +void WaylandBufferManagerHost::OnSubsurfaceAdded( + WaylandWindow* window, + WaylandSubsurface* subsurface) { + DCHECK(subsurface); + surfaces_[subsurface->wayland_surface()] = std::make_unique<Surface>( + subsurface->wayland_surface(), connection_, this); + // WaylandSubsurface is always configured. + surfaces_[subsurface->wayland_surface()]->OnSurfaceConfigured(); +} + +void WaylandBufferManagerHost::OnSubsurfaceRemoved( + WaylandWindow* window, + WaylandSubsurface* subsurface) { + DCHECK(subsurface); + auto it = surfaces_.find(subsurface->wayland_surface()); DCHECK(it != surfaces_.end()); - it->second->OnWindowConfigured(); + if (it->second->HasBuffers()) { + it->second->OnSurfaceRemoved(); + surface_graveyard_.emplace_back(std::move(it->second)); + } + surfaces_.erase(it); } void WaylandBufferManagerHost::SetTerminateGpuCallback( @@ -731,7 +696,7 @@ void WaylandBufferManagerHost::CreateDmabufBasedBuffer( uint32_t format, uint32_t planes_count, uint32_t buffer_id) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); DCHECK(error_message_.empty()); TRACE_EVENT2("wayland", "WaylandBufferManagerHost::CreateDmabufBasedBuffer", @@ -771,7 +736,7 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd, uint64_t length, const gfx::Size& size, uint32_t buffer_id) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); DCHECK(error_message_.empty()); TRACE_EVENT1("wayland", "WaylandBufferManagerHost::CreateShmBasedBuffer", @@ -792,10 +757,31 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd, connection_->ScheduleFlush(); } +bool WaylandBufferManagerHost::CommitBufferInternal( + WaylandSurface* wayland_surface, + uint32_t buffer_id, + const gfx::Rect& damage_region) { + DCHECK(base::CurrentUIThread::IsSet()); + + Surface* surface = GetSurface(wayland_surface); + if (!surface || !ValidateBufferIdFromGpu(buffer_id)) + return false; + + if (!surface->CommitBuffer(buffer_id, damage_region)) { + error_message_ = + base::StrCat({"Buffer with ", NumberToString(buffer_id), + " id does not exist or failed to be created."}); + } + + if (!error_message_.empty()) + TerminateGpuProcess(); + return true; +} + void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id, const gfx::Rect& damage_region) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); TRACE_EVENT1("wayland", "WaylandBufferManagerHost::CommitBuffer", "Buffer id", buffer_id); @@ -804,25 +790,42 @@ void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget, if (widget == gfx::kNullAcceleratedWidget) { error_message_ = "Invalid widget."; - } else if (ValidateBufferIdFromGpu(buffer_id)) { - Surface* surface = GetSurface(widget); - if (!surface) + TerminateGpuProcess(); + } else { + auto* window = connection_->wayland_window_manager()->GetWindow(widget); + if (!window) return; - - if (!surface->CommitBuffer(buffer_id, damage_region)) { - error_message_ = - base::StrCat({"Buffer with ", NumberToString(buffer_id), - " id does not exist or failed to be created."}); - } + CommitBufferInternal(window->root_surface(), buffer_id, damage_region); } +} - if (!error_message_.empty()) +void WaylandBufferManagerHost::CommitOverlays( + gfx::AcceleratedWidget widget, + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) { + DCHECK(base::CurrentUIThread::IsSet()); + + TRACE_EVENT0("wayland", "WaylandBufferManagerHost::CommitOverlays"); + + DCHECK(error_message_.empty()); + + if (widget == gfx::kNullAcceleratedWidget) { + error_message_ = "Invalid widget."; TerminateGpuProcess(); + } + WaylandWindow* window = + connection_->wayland_window_manager()->GetWindow(widget); + // In tab dragging, window may have been destroyed when buffers reach here. We + // omit buffer commits and OnSubmission, because the corresponding buffer + // queue in gpu process should be destroyed soon. + if (!window) + return; + + window->CommitOverlays(overlays); } void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); TRACE_EVENT1("wayland", "WaylandBufferManagerHost::DestroyBuffer", "Buffer id", buffer_id); @@ -841,20 +844,45 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget, // has been stored in the |anonymous_buffers_|. // 2) if the |widget| is null, always search a buffer with the |buffer_id| in // the |anonymous_buffers_|. + // 3) if the |widget| hints at a non-existing window, it's likely that the + // window has been destroyed. In that case, the surface containing the buffer + // is in the graveyard. uint32_t destroyed_count = 0u; - Surface* surface = GetSurface(widget); - if (surface) { - destroyed_count = surface->DestroyBuffer(buffer_id); - if (!surface->HasBuffers() && !surface->HasWindow()) - surfaces_.erase(widget); + auto* window = connection_->wayland_window_manager()->GetWindow(widget); + if (window) { + // Case 1). + Surface* surface = GetSurface(window->root_surface()); + if (surface) { + destroyed_count = surface->DestroyBuffer(buffer_id); + if (!surface->HasBuffers() && !surface->HasSurface()) + surfaces_.erase(window->root_surface()); + } + const auto& subsurfaces = window->wayland_subsurfaces(); + for (const auto& it : subsurfaces) { + Surface* subsurface = GetSurface((*it).wayland_surface()); + if (subsurface) + destroyed_count += subsurface->DestroyBuffer(buffer_id); + } + } else { + // Case 3) + auto it = surface_graveyard_.begin(); + while (it != surface_graveyard_.end()) { + destroyed_count += (*it)->DestroyBuffer(buffer_id); + if (!(*it)->HasBuffers() && !(*it)->HasSurface()) { + surface_graveyard_.erase(it++); + } else { + ++it; + } + } } // Ensure that we can't destroy more than 1 buffer. This can be 0 as well // if no buffers are destroyed. DCHECK_LE(destroyed_count, 1u); + // Case 2) if (destroyed_count == 1u || DestroyAnonymousBuffer(buffer_id)) return; @@ -864,8 +892,8 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget, } void WaylandBufferManagerHost::ResetSurfaceContents( - gfx::AcceleratedWidget widget) { - auto* surface = GetSurface(widget); + WaylandSurface* wayland_surface) { + auto* surface = GetSurface(wayland_surface); DCHECK(surface); surface->ResetSurfaceContents(); } @@ -901,8 +929,8 @@ bool WaylandBufferManagerHost::CreateBuffer(const gfx::Size& size, } WaylandBufferManagerHost::Surface* WaylandBufferManagerHost::GetSurface( - gfx::AcceleratedWidget widget) const { - auto it = surfaces_.find(widget); + WaylandSurface* wayland_surface) const { + auto it = surfaces_.find(wayland_surface); return it != surfaces_.end() ? it->second.get() : nullptr; } @@ -1013,7 +1041,7 @@ void WaylandBufferManagerHost::OnSubmission( gfx::AcceleratedWidget widget, uint32_t buffer_id, const gfx::SwapResult& swap_result) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); DCHECK(buffer_manager_gpu_associated_); buffer_manager_gpu_associated_->OnSubmission(widget, buffer_id, swap_result); @@ -1023,7 +1051,7 @@ void WaylandBufferManagerHost::OnPresentation( gfx::AcceleratedWidget widget, uint32_t buffer_id, const gfx::PresentationFeedback& feedback) { - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); DCHECK(buffer_manager_gpu_associated_); buffer_manager_gpu_associated_->OnPresentation(widget, buffer_id, feedback); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h index b467835f726..20a974e1a5c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h @@ -29,7 +29,9 @@ namespace ui { class WaylandConnection; +class WaylandSubsurface; class WaylandWindow; +class WaylandSurface; // This is an internal helper representation of a wayland buffer object, which // the GPU process creates when CreateBuffer is called. It's used for @@ -82,6 +84,10 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, void OnWindowAdded(WaylandWindow* window) override; void OnWindowRemoved(WaylandWindow* window) override; void OnWindowConfigured(WaylandWindow* window) override; + void OnSubsurfaceAdded(WaylandWindow* window, + WaylandSubsurface* subsurface) override; + void OnSubsurfaceRemoved(WaylandWindow* window, + WaylandSubsurface* subsurface) override; void SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_gpu_cb); @@ -137,25 +143,39 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, void CommitBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id, const gfx::Rect& damage_region) override; + // Called by the GPU and asks to configure the surface/subsurfaces and attach + // wl_buffers to WaylandWindow with the specified |widget|. Calls OnSubmission + // and OnPresentation on successful swap and pixels presented. + void CommitOverlays( + gfx::AcceleratedWidget widget, + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) override; + + // Called by the WaylandWindow and asks to attach a wl_buffer with a + // |buffer_id| to a WaylandSurface. + // Calls OnSubmission and OnPresentation on successful swap and pixels + // presented. + bool CommitBufferInternal(WaylandSurface* wayland_surface, + uint32_t buffer_id, + const gfx::Rect& damage_region); // When a surface is hidden, the client may want to detach the buffer attached - // to the surface backed by |widget| to ensure Wayland does not present those - // contents and do not composite in a wrong way. Otherwise, users may see the - // contents of a hidden surface on their screens. - void ResetSurfaceContents(gfx::AcceleratedWidget widget); + // to the surface to ensure Wayland does not present those contents and do not + // composite in a wrong way. Otherwise, users may see the contents of a hidden + // surface on their screens. + void ResetSurfaceContents(WaylandSurface* wayland_surface); // Returns the anonymously created WaylandBuffer. std::unique_ptr<WaylandBuffer> PassAnonymousWlBuffer(uint32_t buffer_id); private: // This is an internal representation of a real surface, which holds a pointer - // to WaylandWindow. Also, this object holds buffers, frame callbacks and - // presentation callbacks for that window's surface. + // to WaylandSurface. Also, this object holds buffers, frame callbacks and + // presentation callbacks for that surface. class Surface; bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id); - Surface* GetSurface(gfx::AcceleratedWidget widget) const; + Surface* GetSurface(WaylandSurface* wayland_surface) const; // Validates data sent from GPU. If invalid, returns false and sets an error // message to |error_message_|. @@ -192,7 +212,14 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, bool DestroyAnonymousBuffer(uint32_t buffer_id); - base::flat_map<gfx::AcceleratedWidget, std::unique_ptr<Surface>> surfaces_; + base::flat_map<WaylandSurface*, std::unique_ptr<Surface>> surfaces_; + + // When a WaylandWindow/WaylandSubsurface is removed, its corresponding + // Surface may still have an un-released buffer and un-acked presentation. + // Thus, we keep removed surfaces in the graveyard. It's safe to delete them + // when all of the Surface's buffers are destroyed because buffer destruction + // is deferred till after buffers are released and presentations are acked. + std::list<std::unique_ptr<Surface>> surface_graveyard_; // Set when invalid data is received from the GPU process. std::string error_message_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc index a0e848a216d..7e1cdf1aee4 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc @@ -109,7 +109,7 @@ class ClipboardImpl final : public Clipboard, public DataSource::Delegate { if (it == data_.end() && mime_type == ui::kMimeTypeTextUtf8) it = data_.find(ui::kMimeTypeText); if (it != data_.end()) - contents->assign(it->second.begin(), it->second.end()); + contents->assign(it->second->data().begin(), it->second->data().end()); } // The device manager used to access data device and create data sources. @@ -157,8 +157,10 @@ void WaylandClipboard::RequestClipboardData( data_map_ = data_map; read_clipboard_closure_ = std::move(callback); auto* clipboard = GetClipboard(buffer); - if (!clipboard || !clipboard->Read(mime_type)) - SetData({}, mime_type); + if (!clipboard || !clipboard->Read(mime_type)) { + SetData(scoped_refptr<base::RefCountedBytes>(new base::RefCountedBytes()), + mime_type); + } } bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) { @@ -183,11 +185,16 @@ void WaylandClipboard::GetAvailableMimeTypes( std::move(callback).Run(mime_types); } -void WaylandClipboard::SetData(const std::vector<uint8_t>& contents, +bool WaylandClipboard::IsSelectionBufferAvailable() const { + return (connection_->primary_selection_device_manager() != nullptr); +} + +void WaylandClipboard::SetData(PlatformClipboard::Data contents, const std::string& mime_type) { if (!data_map_) return; + DCHECK(contents); (*data_map_)[mime_type] = contents; if (!read_clipboard_closure_.is_null()) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h index d4641406e0c..515c85ca6c3 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h @@ -53,11 +53,11 @@ class WaylandClipboard : public PlatformClipboard { bool IsSelectionOwner(ClipboardBuffer buffer) override; void SetSequenceNumberUpdateCb( PlatformClipboard::SequenceNumberUpdateCb cb) override; + bool IsSelectionBufferAvailable() const override; // TODO(nickdiego): Get rid of these methods once DataDevice implementations // are decoupled from WaylandClipboard. - void SetData(const std::vector<uint8_t>& contents, - const std::string& mime_type); + void SetData(PlatformClipboard::Data contents, const std::string& mime_type); void UpdateSequenceNumber(ClipboardBuffer buffer); private: diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc index 0246bc58695..febebefad2c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc @@ -14,8 +14,8 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop_current.h" #include "base/strings/string_util.h" +#include "base/task/current_thread.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/gfx/geometry/point.h" @@ -52,6 +52,7 @@ constexpr uint32_t kMaxXdgShellVersion = 1; constexpr uint32_t kMaxDeviceManagerVersion = 3; constexpr uint32_t kMaxWpPresentationVersion = 1; constexpr uint32_t kMaxTextInputManagerVersion = 1; +constexpr uint32_t kMinAuraShellVersion = 10; constexpr uint32_t kMinWlDrmVersion = 2; constexpr uint32_t kMinWlOutputVersion = 2; } // namespace @@ -117,7 +118,7 @@ void WaylandConnection::ScheduleFlush() { // When we are in tests, the message loop is set later when the // initialization of the OzonePlatform complete. Thus, just // flush directly. This doesn't happen in normal run. - if (!base::MessageLoopCurrentForUI::IsSet()) { + if (!base::CurrentUIThread::IsSet()) { Flush(); } else if (!scheduled_flush_) { base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -140,6 +141,12 @@ bool WaylandConnection::IsDragInProgress() const { WaylandDataDragController::State::kIdle; } +wl::Object<wl_surface> WaylandConnection::CreateSurface() { + DCHECK(compositor_); + return wl::Object<wl_surface>( + wl_compositor_create_surface(compositor_.get())); +} + void WaylandConnection::Flush() { wl_display_flush(display_.get()); scheduled_flush_ = false; @@ -157,26 +164,33 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat, pointer_.reset(); cursor_.reset(); wayland_cursor_position_.reset(); - } else if (wl_pointer* pointer = wl_seat_get_pointer(seat)) { - pointer_ = std::make_unique<WaylandPointer>(pointer, this, event_source()); - cursor_ = std::make_unique<WaylandCursor>(pointer_.get(), this); - wayland_cursor_position_ = std::make_unique<WaylandCursorPosition>(); - } else { - LOG(ERROR) << "Failed to get wl_pointer from seat"; + } else if (!pointer_) { + if (wl_pointer* pointer = wl_seat_get_pointer(seat)) { + pointer_ = + std::make_unique<WaylandPointer>(pointer, this, event_source()); + cursor_ = std::make_unique<WaylandCursor>(pointer_.get(), this); + wayland_cursor_position_ = std::make_unique<WaylandCursorPosition>(); + } else { + LOG(ERROR) << "Failed to get wl_pointer from seat"; + } } if (!has_keyboard) { keyboard_.reset(); - } else if (!CreateKeyboard()) { - LOG(ERROR) << "Failed to create WaylandKeyboard"; + } else if (!keyboard_) { + if (!CreateKeyboard()) { + LOG(ERROR) << "Failed to create WaylandKeyboard"; + } } if (!has_touch) { touch_.reset(); - } else if (wl_touch* touch = wl_seat_get_touch(seat)) { - touch_ = std::make_unique<WaylandTouch>(touch, this, event_source()); - } else { - LOG(ERROR) << "Failed to get wl_touch from seat"; + } else if (!touch_) { + if (wl_touch* touch = wl_seat_get_touch(seat)) { + touch_ = std::make_unique<WaylandTouch>(touch, this, event_source()); + } else { + LOG(ERROR) << "Failed to get wl_touch from seat"; + } } } @@ -352,6 +366,15 @@ void WaylandConnection::Global(void* data, auto wayland_drm = wl::Bind<struct wl_drm>(registry, name, version); connection->drm_ = std::make_unique<WaylandDrm>(wayland_drm.release(), connection); + } else if (!connection->aura_shell_ && + (strcmp(interface, "zaura_shell") == 0) && + version >= kMinAuraShellVersion) { + connection->aura_shell_ = + wl::Bind<struct zaura_shell>(registry, name, version); + if (!connection->aura_shell_) { + LOG(ERROR) << "Failed to bind zaura_shell"; + return; + } } connection->ScheduleFlush(); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h index 5c5754dd2fa..6dfc5b7a560 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h @@ -54,6 +54,7 @@ class WaylandConnection { wl_subcompositor* subcompositor() const { return subcompositor_.get(); } xdg_wm_base* shell() const { return shell_.get(); } zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); } + zaura_shell* aura_shell() const { return aura_shell_.get(); } wl_seat* seat() const { return seat_.get(); } wp_presentation* presentation() const { return presentation_.get(); } zwp_text_input_manager_v1* text_input_manager_v1() const { @@ -121,6 +122,9 @@ class WaylandConnection { // Returns true when dragging is entered or started. bool IsDragInProgress() const; + // Creates a new wl_surface. + wl::Object<wl_surface> CreateSurface(); + private: void Flush(); void UpdateInputDevices(wl_seat* seat, uint32_t capabilities); @@ -162,6 +166,7 @@ class WaylandConnection { wl::Object<wp_presentation> presentation_; wl::Object<zcr_keyboard_extension_v1> keyboard_extension_v1_; wl::Object<zwp_text_input_manager_v1> text_input_manager_v1_; + wl::Object<zaura_shell> aura_shell_; // Event source instance. Must be declared before input objects so it // outlives them so thus being able to properly handle their destruction. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc index 7ffd900fcae..fa12bbb1e31 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc @@ -19,13 +19,9 @@ namespace ui { WaylandCursor::WaylandCursor(WaylandPointer* pointer, WaylandConnection* connection) - : pointer_(pointer), connection_(connection) { - DCHECK(connection); - DCHECK(connection->compositor()); - - pointer_surface_.reset( - wl_compositor_create_surface(connection->compositor())); -} + : pointer_(pointer), + connection_(connection), + pointer_surface_(connection->CreateSurface()) {} WaylandCursor::~WaylandCursor() = default; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h index 5f8b6364bd4..1f73050be9b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h @@ -56,7 +56,7 @@ class WaylandCursor { // Holds the buffers and their memory until the compositor releases them. base::flat_map<wl_buffer*, WaylandShmBuffer> buffers_; - wl::Object<wl_surface> pointer_surface_; + const wl::Object<wl_surface> pointer_surface_; DISALLOW_COPY_AND_ASSIGN(WaylandCursor); }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc index 1edd356006b..f80738e5e34 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc @@ -43,8 +43,8 @@ void WaylandDataDevice::StartDrag(const WaylandDataSource& data_source, drag_delegate_ = delegate; wl_data_device_start_drag(data_device_.get(), data_source.data_source(), - origin_window.surface(), icon_surface, - connection()->serial()); + origin_window.root_surface()->surface(), + icon_surface, connection()->serial()); drag_delegate_->DrawIcon(); connection()->ScheduleFlush(); } @@ -81,12 +81,12 @@ void WaylandDataDevice::SetSelectionSource(WaylandDataSource* source) { connection()->ScheduleFlush(); } -void WaylandDataDevice::ReadDragDataFromFD( - base::ScopedFD fd, - base::OnceCallback<void(const PlatformClipboard::Data&)> callback) { - PlatformClipboard::Data contents; +void WaylandDataDevice::ReadDragDataFromFD(base::ScopedFD fd, + RequestDataCallback callback) { + std::vector<uint8_t> contents; wl::ReadDataFromFD(std::move(fd), &contents); - std::move(callback).Run(contents); + std::move(callback).Run(scoped_refptr<base::RefCountedBytes>( + base::RefCountedBytes::TakeVector(&contents))); } // static @@ -109,7 +109,7 @@ void WaylandDataDevice::OnEnter(void* data, wl_fixed_t x, wl_fixed_t y, wl_data_offer* offer) { - WaylandWindow* window = WaylandWindow::FromSurface(surface); + WaylandWindow* window = wl::RootWindowFromWlSurface(surface); if (!window) { LOG(ERROR) << "Failed to get window."; return; @@ -149,15 +149,15 @@ void WaylandDataDevice::OnDrop(void* data, wl_data_device* data_device) { void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) { auto* self = static_cast<WaylandDataDevice*>(data); - if (self->drag_delegate_) { + if (self->drag_delegate_) self->drag_delegate_->OnDragLeave(); - // When in a DND session initiated by an external application, - // |drag_delegate_| is set at OnEnter, and must be reset here to avoid - // potential use-after-free. - if (!self->drag_delegate_->IsDragSource()) - self->drag_delegate_ = nullptr; - } + // When in a DND session initiated by an external application, + // |drag_delegate_| is set at OnEnter, and must be reset here to avoid + // potential use-after-free. Above call to OnDragLeave() may result in + // |drag_delegate_| being reset, so it must be checked here as well. + if (self->drag_delegate_ && !self->drag_delegate_->IsDragSource()) + self->drag_delegate_ = nullptr; } void WaylandDataDevice::OnSelection(void* data, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h index 5512f4c4480..12b84cf9cb1 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h @@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/files/scoped_file.h" +#include "base/gtest_prod_util.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/wayland_data_device_base.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" @@ -32,8 +33,7 @@ class WaylandWindow; // such as copy-and-paste and drag-and-drop mechanisms. class WaylandDataDevice : public WaylandDataDeviceBase { public: - using RequestDataCallback = - base::OnceCallback<void(const PlatformClipboard::Data&)>; + using RequestDataCallback = base::OnceCallback<void(PlatformClipboard::Data)>; // DragDelegate is responsible for handling drag and drop sessions. class DragDelegate { @@ -79,6 +79,8 @@ class WaylandDataDevice : public WaylandDataDeviceBase { void SetSelectionSource(WaylandDataSource* source); private: + FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDrag); + void ReadDragDataFromFD(base::ScopedFD fd, RequestDataCallback callback); // wl_data_device_listener callbacks diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc index 3e020a31134..7df5d9ec709 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc @@ -56,7 +56,10 @@ void WaylandDataDeviceBase::ReadClipboardDataFromFD( const std::string& mime_type) { std::vector<uint8_t> contents; wl::ReadDataFromFD(std::move(fd), &contents); - connection_->clipboard()->SetData(contents, mime_type); + connection_->clipboard()->SetData( + scoped_refptr<base::RefCountedBytes>( + base::RefCountedBytes::TakeVector(&contents)), + mime_type); } void WaylandDataDeviceBase::RegisterDeferredReadCallback() { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc index b4bedb59ac9..855982cf567 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc @@ -15,7 +15,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard_constants.h" #include "ui/events/base_event_utils.h" -#include "ui/ozone/platform/wayland/test/constants.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/test_data_device.h" #include "ui/ozone/platform/wayland/test/test_data_device_manager.h" @@ -32,15 +31,14 @@ namespace ui { namespace { +constexpr char kSampleClipboardText[] = "This is a sample text for clipboard."; + template <typename StringType> ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) { - ui::PlatformClipboard::Data result; - auto* begin = - reinterpret_cast<typename ui::PlatformClipboard::Data::const_pointer>( - data_string.data()); - result.assign(begin, begin + (data_string.size() * - sizeof(typename StringType::value_type))); - return result; + std::vector<uint8_t> data_vector; + data_vector.assign(data_string.begin(), data_string.end()); + return scoped_refptr<base::RefCountedBytes>( + base::RefCountedBytes::TakeVector(&data_vector)); } } // namespace @@ -64,7 +62,7 @@ class MockClipboardClient { ~MockClipboardClient() = default; // Fill the clipboard backing store with sample data. - void SetData(const PlatformClipboard::Data& data, + void SetData(PlatformClipboard::Data data, const std::string& mime_type, PlatformClipboard::OfferDataClosure callback) { data_types_[mime_type] = data; @@ -115,23 +113,25 @@ class WaylandDataDeviceManagerTest : public WaylandTest { TEST_P(WaylandDataDeviceManagerTest, WriteToClipboard) { // The client writes data to the clipboard ... - PlatformClipboard::Data data; - data.assign(wl::kSampleClipboardText, - wl::kSampleClipboardText + strlen(wl::kSampleClipboardText)); - clipboard_client_->SetData(data, wl::kTextMimeTypeUtf8, - base::BindOnce([]() {})); + std::vector<uint8_t> data_vector( + kSampleClipboardText, + kSampleClipboardText + strlen(kSampleClipboardText)); + clipboard_client_->SetData( + scoped_refptr<base::RefCountedBytes>( + base::RefCountedBytes::TakeVector(&data_vector)), + {kMimeTypeTextUtf8}, base::BindOnce([]() {})); Sync(); // ... and the server reads it. base::RunLoop run_loop; auto callback = base::BindOnce( - [](base::RunLoop* loop, PlatformClipboard::Data&& data) { + [](base::RunLoop* loop, std::vector<uint8_t>&& data) { std::string string_data(data.begin(), data.end()); - EXPECT_EQ(wl::kSampleClipboardText, string_data); + EXPECT_EQ(kSampleClipboardText, string_data); loop->Quit(); }, &run_loop); - data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeUtf8, + data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8, std::move(callback)); run_loop.Run(); } @@ -140,8 +140,8 @@ TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboard) { // TODO(nickdiego): implement this in terms of an actual wl_surface that // gets focused and compositor sends data_device data to it. auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer(wl::kTextMimeTypeUtf8, - ToClipboardData(std::string(wl::kSampleClipboardText))); + data_offer->OnOffer(kMimeTypeTextUtf8, + ToClipboardData(std::string(kSampleClipboardText))); data_device_manager_->data_device()->OnSelection(data_offer); Sync(); @@ -150,10 +150,11 @@ TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboard) { // expectation. auto callback = base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { - std::string string_data = std::string(data->begin(), data->end()); - EXPECT_EQ(wl::kSampleClipboardText, string_data); + auto& bytes = data->get()->data(); + std::string string_data = std::string(bytes.begin(), bytes.end()); + EXPECT_EQ(kSampleClipboardText, string_data); }); - clipboard_client_->ReadData(wl::kTextMimeTypeUtf8, std::move(callback)); + clipboard_client_->ReadData(kMimeTypeTextUtf8, std::move(callback)); Sync(); } @@ -163,18 +164,22 @@ TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboardWithoutOffer) { // an empty string. auto callback = base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { - std::string string_data = std::string(data->begin(), data->end()); + auto& bytes = data->get()->data(); + std::string string_data = std::string(bytes.begin(), bytes.end()); EXPECT_EQ("", string_data); }); - clipboard_client_->ReadData(wl::kTextMimeTypeUtf8, std::move(callback)); + clipboard_client_->ReadData(kMimeTypeTextUtf8, std::move(callback)); } TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) { auto callback = base::BindOnce([]() {}); - PlatformClipboard::Data data; - data.assign(wl::kSampleClipboardText, - wl::kSampleClipboardText + strlen(wl::kSampleClipboardText)); - clipboard_client_->SetData(data, wl::kTextMimeTypeUtf8, std::move(callback)); + std::vector<uint8_t> data_vector( + kSampleClipboardText, + kSampleClipboardText + strlen(kSampleClipboardText)); + clipboard_client_->SetData( + scoped_refptr<base::RefCountedBytes>( + base::RefCountedBytes::TakeVector(&data_vector)), + {kMimeTypeTextUtf8}, std::move(callback)); Sync(); ASSERT_TRUE(clipboard_client_->IsSelectionOwner()); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc index ec14c3e3b7f..e4337897d54 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc @@ -96,7 +96,7 @@ void WaylandDataDragController::StartSession(const OSExchangeData& data, Offer(data, operation); // Create drag icon surface (if any) and store the data to be exchanged. - icon_surface_.reset(CreateIconSurfaceIfNeeded(data)); + CreateIconSurfaceIfNeeded(data); data_ = std::make_unique<OSExchangeData>(data.provider().Clone()); // Starts the wayland drag session setting |this| object as delegate. @@ -216,13 +216,21 @@ void WaylandDataDragController::OnDragDrop() { void WaylandDataDragController::OnDataSourceFinish(bool completed) { DCHECK(data_source_); - if (origin_window_) - origin_window_->OnDragSessionClose(data_source_->dnd_action()); + DCHECK(origin_window_); + + origin_window_->OnDragSessionClose(data_source_->dnd_action()); + + // DnD handlers expect DragLeave to be sent for drag sessions that end up + // with no data transfer (wl_data_source::cancelled event). + if (!completed) + origin_window_->OnDragLeave(); origin_window_ = nullptr; data_source_.reset(); data_offer_.reset(); data_.reset(); + data_device_->ResetDragDelegate(); + state_ = State::kIdle; } @@ -265,11 +273,11 @@ void WaylandDataDragController::Offer(const OSExchangeData& data, data_source_->SetAction(operation); } -wl_surface* WaylandDataDragController::CreateIconSurfaceIfNeeded( +void WaylandDataDragController::CreateIconSurfaceIfNeeded( const OSExchangeData& data) { icon_bitmap_ = GetDragImage(data); - return icon_bitmap_ ? wl_compositor_create_surface(connection_->compositor()) - : nullptr; + if (icon_bitmap_) + icon_surface_ = connection_->CreateSurface(); } // Asynchronously requests and reads data for every negotiated/supported mime @@ -293,9 +301,10 @@ void WaylandDataDragController::HandleUnprocessedMimeTypes() { } void WaylandDataDragController::OnMimeTypeDataTransferred( - const PlatformClipboard::Data& contents) { + PlatformClipboard::Data contents) { DCHECK_EQ(state_, State::kTransferring); - if (!contents.empty()) { + DCHECK(contents); + if (!contents->data().empty()) { std::string mime_type = unprocessed_mime_types_.front(); wl::AddToOSExchangeData(contents, mime_type, received_data_.get()); } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h index 2d1e1b09d6d..5e4c15dd08b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h @@ -78,9 +78,9 @@ class WaylandDataDragController : public WaylandDataDevice::DragDelegate, std::string* contents) override; void Offer(const OSExchangeData& data, int operation); - wl_surface* CreateIconSurfaceIfNeeded(const OSExchangeData& data); + void CreateIconSurfaceIfNeeded(const OSExchangeData& data); void HandleUnprocessedMimeTypes(); - void OnMimeTypeDataTransferred(const PlatformClipboard::Data& contents); + void OnMimeTypeDataTransferred(PlatformClipboard::Data contents); void OnDataTransferFinished( std::unique_ptr<ui::OSExchangeData> received_data); std::string GetNextUnprocessedMimeType(); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc index a320c70dd1d..8bd11603863 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc @@ -14,16 +14,18 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/file_info/file_info.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/events/base_event_utils.h" +#include "ui/gfx/geometry/point.h" #include "ui/ozone/platform/wayland/common/data_util.h" #include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" -#include "ui/ozone/platform/wayland/test/constants.h" +#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/test_data_device.h" #include "ui/ozone/platform/wayland/test/test_data_device_manager.h" @@ -32,7 +34,9 @@ #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" #include "ui/ozone/public/platform_clipboard.h" -#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "ui/platform_window/platform_window_init_properties.h" +#include "ui/platform_window/wm/wm_drag_handler.h" +#include "ui/platform_window/wm/wm_drop_handler.h" #include "url/gurl.h" using testing::_; @@ -42,32 +46,45 @@ namespace ui { namespace { +constexpr char kSampleTextForDragAndDrop[] = + "This is a sample text for drag-and-drop."; + constexpr FilenameToURLPolicy kFilenameToURLPolicy = FilenameToURLPolicy::CONVERT_FILENAMES; template <typename StringType> PlatformClipboard::Data ToClipboardData(const StringType& data_string) { - PlatformClipboard::Data result; - auto* begin = - reinterpret_cast<typename PlatformClipboard::Data::const_pointer>( - data_string.data()); - result.assign(begin, begin + (data_string.size() * - sizeof(typename StringType::value_type))); - return result; + auto* begin = reinterpret_cast<typename std::vector<uint8_t>::const_pointer>( + data_string.data()); + std::vector<uint8_t> result( + begin, + begin + (data_string.size() * sizeof(typename StringType::value_type))); + return scoped_refptr<base::RefCountedBytes>( + base::RefCountedBytes::TakeVector(&result)); } } // namespace +class MockDragHandlerDelegate : public WmDragHandler::Delegate { + public: + MOCK_METHOD1(OnDragLocationChanged, void(const gfx::Point& location)); + MOCK_METHOD1(OnDragOperationChanged, + void(DragDropTypes::DragOperation operation)); + MOCK_METHOD1(OnDragFinished, void(int operation)); +}; + class MockDropHandler : public WmDropHandler { public: MockDropHandler() = default; ~MockDropHandler() override = default; - MOCK_METHOD3(OnDragEnter, + MOCK_METHOD4(OnDragEnter, void(const gfx::PointF& point, std::unique_ptr<OSExchangeData> data, - int operation)); - MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation)); + int operation, + int modifiers)); + MOCK_METHOD3(OnDragMotion, + int(const gfx::PointF& point, int operation, int modifiers)); MOCK_METHOD0(MockOnDragDrop, void()); MOCK_METHOD0(OnDragLeave, void()); @@ -78,7 +95,8 @@ class MockDropHandler : public WmDropHandler { OSExchangeData* dropped_data() { return dropped_data_.get(); } protected: - void OnDragDrop(std::unique_ptr<OSExchangeData> data) override { + void OnDragDrop(std::unique_ptr<OSExchangeData> data, + int modifiers) override { dropped_data_ = std::move(data); MockOnDragDrop(); on_drop_closure_.Run(); @@ -103,6 +121,7 @@ class WaylandDataDragControllerTest : public WaylandTest { data_device_manager_ = server_.data_device_manager(); DCHECK(data_device_manager_); + drag_handler_delegate_ = std::make_unique<MockDragHandlerDelegate>(); drop_handler_ = std::make_unique<MockDropHandler>(); SetWmDropHandler(window_.get(), drop_handler_.get()); } @@ -116,38 +135,88 @@ class WaylandDataDragControllerTest : public WaylandTest { } base::string16 sample_text_for_dnd() const { - static auto text = base::ASCIIToUTF16(wl::kSampleTextForDragAndDrop); + static auto text = base::ASCIIToUTF16(kSampleTextForDragAndDrop); return text; } + void ReadDataWhenSourceIsReady() { + Sync(); + + if (!data_device_manager_->data_source()) { + // The data source is created asynchronously via the window's data drag + // controller. If it is null now, it means that the task for that has not + // yet executed, and we have to come later. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce( + &WaylandDataDragControllerTest::ReadDataWhenSourceIsReady, + base::Unretained(this))); + return; + } + + // Now the server can read the data and give it to our callback. + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + auto callback = base::BindOnce( + [](base::RunLoop* loop, std::vector<uint8_t>&& data) { + std::string result(data.begin(), data.end()); + EXPECT_EQ(kSampleTextForDragAndDrop, result); + loop->Quit(); + }, + &run_loop); + data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8, + std::move(callback)); + run_loop.Run(); + + data_device_manager_->data_source()->OnCancelled(); + Sync(); + } + + void ScheduleDragCancel() { + Sync(); + + if (!data_device_manager_->data_source()) { + // The data source is created asynchronously by the data drag controller. + // If it is null at this point, it means that the task for that has not + // yet executed, and we have to try again a bit later. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&WaylandDataDragControllerTest::ScheduleDragCancel, + base::Unretained(this))); + return; + } + + data_device_manager_->data_source()->OnCancelled(); + Sync(); + } + protected: wl::TestDataDeviceManager* data_device_manager_; std::unique_ptr<MockDropHandler> drop_handler_; + std::unique_ptr<MockDragHandlerDelegate> drag_handler_delegate_; }; TEST_P(WaylandDataDragControllerTest, StartDrag) { - bool restored_focus = window_->has_pointer_focus(); + const bool restored_focus = window_->has_pointer_focus(); window_->SetPointerFocus(true); // The client starts dragging. + ASSERT_EQ(PlatformWindowType::kWindow, window_->type()); OSExchangeData os_exchange_data; os_exchange_data.SetString(sample_text_for_dnd()); - int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; - drag_controller()->StartSession(os_exchange_data, operation); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&WaylandDataDragControllerTest::ReadDataWhenSourceIsReady, + base::Unretained(this))); + + static_cast<WaylandToplevelWindow*>(window_.get()) + ->StartDrag(os_exchange_data, + DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE, {}, true, + drag_handler_delegate_.get()); Sync(); - // The server reads the data and the callback gets it. - base::RunLoop run_loop; - auto callback = base::BindOnce( - [](base::RunLoop* loop, PlatformClipboard::Data&& data) { - std::string result(data.begin(), data.end()); - EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); - loop->Quit(); - }, - &run_loop); - data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeUtf8, - std::move(callback)); - run_loop.Run(); + EXPECT_FALSE(data_device()->drag_delegate_); + window_->SetPointerFocus(restored_focus); } @@ -166,7 +235,7 @@ TEST_P(WaylandDataDragControllerTest, StartDragWithWrongMimeType) { // to read it with a different mime type. base::RunLoop run_loop; auto callback = base::BindOnce( - [](base::RunLoop* loop, PlatformClipboard::Data&& data) { + [](base::RunLoop* loop, std::vector<uint8_t>&& data) { std::string result(data.begin(), data.end()); EXPECT_TRUE(result.empty()); loop->Quit(); @@ -194,9 +263,9 @@ TEST_P(WaylandDataDragControllerTest, StartDragWithText) { // |kTextMimeTypeUtf8|. base::RunLoop run_loop; auto callback = base::BindOnce( - [](base::RunLoop* loop, PlatformClipboard::Data&& data) { + [](base::RunLoop* loop, std::vector<uint8_t>&& data) { std::string result(data.begin(), data.end()); - EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); + EXPECT_EQ(kSampleTextForDragAndDrop, result); loop->Quit(); }, &run_loop); @@ -208,9 +277,8 @@ TEST_P(WaylandDataDragControllerTest, StartDragWithText) { TEST_P(WaylandDataDragControllerTest, ReceiveDrag) { auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer( - kMimeTypeText, - ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); + data_offer->OnOffer(kMimeTypeText, + ToClipboardData(std::string(kSampleTextForDragAndDrop))); gfx::Point entered_point(10, 10); // The server sends an enter event. @@ -229,11 +297,11 @@ TEST_P(WaylandDataDragControllerTest, ReceiveDrag) { Sync(); - auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) { + auto callback = base::BindOnce([](PlatformClipboard::Data contents) { std::string result; - result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]), - contents.size()); - EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); + EXPECT_TRUE(contents); + result.assign(contents->front_as<char>(), contents->size()); + EXPECT_EQ(kSampleTextForDragAndDrop, result); }); // The client requests the data and gets callback with it. @@ -246,9 +314,8 @@ TEST_P(WaylandDataDragControllerTest, ReceiveDrag) { TEST_P(WaylandDataDragControllerTest, DropSeveralMimeTypes) { auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer( - kMimeTypeText, - ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); + data_offer->OnOffer(kMimeTypeText, + ToClipboardData(std::string(kSampleTextForDragAndDrop))); data_offer->OnOffer(kMimeTypeMozillaURL, ToClipboardData(base::UTF8ToUTF16( "https://sample.com/\r\n" "Sample"))); @@ -256,7 +323,7 @@ TEST_P(WaylandDataDragControllerTest, DropSeveralMimeTypes) { kMimeTypeURIList, ToClipboardData(std::string("file:///home/user/file\r\n"))); - EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1); + EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1); gfx::Point entered_point(10, 10); data_device_manager_->data_device()->OnEnter( 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), @@ -303,7 +370,7 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedUriList) { auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); data_offer->OnOffer(kMimeTypeURIList, ToClipboardData(kCase.content)); - EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1); + EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1); gfx::Point entered_point(10, 10); data_device_manager_->data_device()->OnEnter( 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), @@ -358,7 +425,7 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) { data_offer->OnOffer(kMimeTypeMozillaURL, ToClipboardData(base::UTF8ToUTF16(kCase.content))); - EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1); + EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1); gfx::Point entered_point(10, 10); data_device_manager_->data_device()->OnEnter( 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), @@ -395,6 +462,35 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) { } } +// Verifies the correct delegate functions are called when a drag session is +// started and cancelled within the same surface. +TEST_P(WaylandDataDragControllerTest, StartAndCancel) { + const bool restored_focus = window_->has_pointer_focus(); + window_->SetPointerFocus(true); + + ASSERT_EQ(PlatformWindowType::kWindow, window_->type()); + OSExchangeData os_exchange_data; + os_exchange_data.SetString(sample_text_for_dnd()); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&WaylandDataDragControllerTest::ScheduleDragCancel, + base::Unretained(this))); + + // DnD handlers expect DragLeave to be sent before DragFinished when drag + // sessions end up with no data transfer (cancelled). Otherwise, it might lead + // to issues like https://crbug.com/1109324. + EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); + EXPECT_CALL(*drag_handler_delegate_, OnDragFinished(_)).Times(1); + + static_cast<WaylandToplevelWindow*>(window_.get()) + ->StartDrag(os_exchange_data, DragDropTypes::DRAG_COPY, {}, true, + drag_handler_delegate_.get()); + Sync(); + + window_->SetPointerFocus(restored_focus); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandDataDragControllerTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc index 8656f41412c..cbdd82a25d0 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc @@ -9,14 +9,6 @@ namespace ui { -namespace { - -const char kString[] = "STRING"; -const char kText[] = "TEXT"; -const char kUtf8String[] = "UTF8_STRING"; - -} // namespace - WaylandDataOfferBase::WaylandDataOfferBase() = default; WaylandDataOfferBase::~WaylandDataOfferBase() = default; @@ -26,9 +18,10 @@ void WaylandDataOfferBase::EnsureTextMimeTypeIfNeeded() { if (std::any_of(mime_types_.begin(), mime_types_.end(), [](const std::string& mime_type) { - return mime_type == kString || mime_type == kText || + return mime_type == kMimeTypeLinuxString || + mime_type == kMimeTypeLinuxText || mime_type == kMimeTypeTextUtf8 || - mime_type == kUtf8String; + mime_type == kMimeTypeLinuxUtf8String; })) { mime_types_.push_back(kMimeTypeText); text_plain_mime_type_inserted_ = true; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc index 48d500ae5fb..41f186362f9 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc @@ -161,13 +161,18 @@ void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window, } void WaylandEventSource::OnPointerButtonEvent(EventType type, - int changed_button) { + int changed_button, + WaylandWindow* window) { DCHECK(type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED); DCHECK(HasAnyPointerButtonFlag(changed_button)); if (!pointer_) return; + auto* prev_focused_window = window_with_pointer_focus_; + if (window) + HandlePointerFocusChange(window); + pointer_flags_ = type == ET_MOUSE_PRESSED ? (pointer_flags_ | changed_button) : (pointer_flags_ & ~changed_button); @@ -177,6 +182,9 @@ void WaylandEventSource::OnPointerButtonEvent(EventType type, MouseEvent event(type, pointer_location_, pointer_location_, EventTimeForNow(), flags, changed_button); DispatchEvent(&event); + + if (window) + HandlePointerFocusChange(prev_focused_window); } void WaylandEventSource::OnPointerMotionEvent(const gfx::PointF& location) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h index 7c9c409e702..e646df4fd4d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h @@ -59,6 +59,8 @@ class WaylandEventSource : public PlatformEventSource, return last_pointer_button_pressed_; } + int keyboard_modifiers() const { return keyboard_modifiers_; } + // Starts polling for events from the wayland connection file descriptor. // This method assumes connection is already estabilished and input objects // are already bound and properly initialized. @@ -92,7 +94,9 @@ class WaylandEventSource : public PlatformEventSource, void OnPointerDestroyed(WaylandPointer* pointer) override; void OnPointerFocusChanged(WaylandWindow* window, const gfx::PointF& location) override; - void OnPointerButtonEvent(EventType evtype, int changed_button) override; + void OnPointerButtonEvent(EventType evtype, + int changed_button, + WaylandWindow* window = nullptr) override; void OnPointerMotionEvent(const gfx::PointF& location) override; void OnPointerAxisEvent(const gfx::Vector2d& offset) override; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc index 9cec9ccbd4b..34bf114fa03 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc @@ -79,7 +79,9 @@ TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) { uint32_t serial = 0; uint32_t tstamp = 0; wl_resource* surface_res = - server_.GetObject<wl::MockSurface>(window1->GetWidget())->resource(); + server_ + .GetObject<wl::MockSurface>(window1->root_surface()->GetSurfaceId()) + ->resource(); wl_resource* pointer_res = server_.seat()->pointer()->resource(); wl_pointer_send_enter(pointer_res, serial++, surface_res, 0, 0); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc index 30325c45884..52f5eb0e870 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc @@ -9,7 +9,7 @@ #include "base/bind.h" #include "base/check.h" -#include "base/message_loop/message_loop_current.h" +#include "base/task/current_thread.h" #include "ui/events/event.h" namespace ui { @@ -38,7 +38,7 @@ bool WaylandEventWatcher::StopProcessingEvents() { if (!watching_) return false; - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); watching_ = false; return controller_.StopWatchingFileDescriptor(); } @@ -85,9 +85,9 @@ bool WaylandEventWatcher::StartWatchingFd( DCHECK(!watching_); } - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); int display_fd = wl_display_get_fd(display_); - watching_ = base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor( + watching_ = base::CurrentUIThread::Get()->WatchFileDescriptor( display_fd, true, mode, &controller_, this); return watching_; } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc index 7de0fe694d7..a4c905b7895 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc @@ -20,6 +20,7 @@ #include "ui/events/ozone/layout/keyboard_layout_engine.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/events/types/event_type.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -101,7 +102,7 @@ void WaylandKeyboard::Enter(void* data, wl_surface* surface, wl_array* keys) { // wl_surface might have been destroyed by this time. - if (auto* window = WaylandWindow::FromSurface(surface)) { + if (auto* window = wl::RootWindowFromWlSurface(surface)) { auto* self = static_cast<WaylandKeyboard*>(data); self->delegate_->OnKeyboardFocusChanged(window, /*focused=*/true); } @@ -113,7 +114,7 @@ void WaylandKeyboard::Leave(void* data, wl_surface* surface) { // wl_surface might have been destroyed by this time. auto* self = static_cast<WaylandKeyboard*>(data); - if (auto* window = WaylandWindow::FromSurface(surface)) + if (auto* window = wl::RootWindowFromWlSurface(surface)) self->delegate_->OnKeyboardFocusChanged(window, /*focused=*/false); // Upon window focus lose, reset the key repeat timers. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output.cc b/chromium/ui/ozone/platform/wayland/host/wayland_output.cc index 701f1678b19..7a4067df952 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_output.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_output.cc @@ -11,7 +11,7 @@ namespace ui { -WaylandOutput::WaylandOutput(const uint32_t output_id, wl_output* output) +WaylandOutput::WaylandOutput(uint32_t output_id, wl_output* output) : output_id_(output_id), output_(output), scale_factor_(kDefaultScaleFactor), @@ -31,7 +31,7 @@ void WaylandOutput::Initialize(Delegate* delegate) { wl_output_add_listener(output_.get(), &output_listener, this); } -void WaylandOutput::TriggerDelegateNotification() const { +void WaylandOutput::TriggerDelegateNotifications() const { DCHECK(!rect_in_physical_pixels_.IsEmpty()); delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_, scale_factor_); @@ -67,9 +67,8 @@ void WaylandOutput::OutputHandleMode(void* data, // static void WaylandOutput::OutputHandleDone(void* data, struct wl_output* wl_output) { - WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data); - if (wayland_output) - wayland_output->TriggerDelegateNotification(); + if (auto* output = static_cast<WaylandOutput*>(data)) + output->TriggerDelegateNotifications(); } // static diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output.h b/chromium/ui/ozone/platform/wayland/host/wayland_output.h index 36f8c89fc1e..3bda676a179 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_output.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_output.h @@ -20,23 +20,23 @@ class WaylandOutput { public: class Delegate { public: - virtual ~Delegate() {} - virtual void OnOutputHandleMetrics(uint32_t output_id, const gfx::Rect& new_bounds, int32_t scale_factor) = 0; + + protected: + virtual ~Delegate() = default; }; - WaylandOutput(const uint32_t output_id, wl_output* output); + WaylandOutput(uint32_t output_id, wl_output* output); ~WaylandOutput(); void Initialize(Delegate* delegate); - void TriggerDelegateNotification() const; - uint32_t output_id() const { return output_id_; } bool has_output(wl_output* output) const { return output_.get() == output; } int32_t scale_factor() const { return scale_factor_; } + gfx::Rect bounds() const { return rect_in_physical_pixels_; } // Tells if the output has already received physical screen dimensions in the // global compositor space. @@ -45,6 +45,8 @@ class WaylandOutput { private: static constexpr int32_t kDefaultScaleFactor = 1; + void TriggerDelegateNotifications() const; + // Callback functions used for setting geometric properties of the output // and available modes. static void OutputHandleGeometry(void* data, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc index 1f403f4928a..4d71d142fe7 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc @@ -4,6 +4,8 @@ #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" +#include <algorithm> +#include <cstdint> #include <memory> #include "ui/ozone/platform/wayland/host/wayland_connection.h" @@ -15,13 +17,16 @@ WaylandOutputManager::WaylandOutputManager() = default; WaylandOutputManager::~WaylandOutputManager() = default; +// Output is considered ready when at least one wl_output is fully configured +// (i.e: wl_output::done received), so that WaylandOutputManager is able to +// instantiate a valid WaylandScreen when requested by the upper layer. bool WaylandOutputManager::IsOutputReady() const { - if (output_list_.empty()) - return false; - return output_list_.front()->is_ready(); + return std::find_if(output_list_.begin(), output_list_.end(), + [](const auto& output) { return output->is_ready(); }) != + output_list_.end(); } -void WaylandOutputManager::AddWaylandOutput(const uint32_t output_id, +void WaylandOutputManager::AddWaylandOutput(uint32_t output_id, wl_output* output) { // Make sure an output with |output_id| has not been added yet. It's very // unlikely to happen, unless a compositor has a bug in the numeric names @@ -29,26 +34,26 @@ void WaylandOutputManager::AddWaylandOutput(const uint32_t output_id, auto output_it = GetOutputItById(output_id); DCHECK(output_it == output_list_.end()); auto wayland_output = std::make_unique<WaylandOutput>(output_id, output); - WaylandOutput* wayland_output_ptr = wayland_output.get(); - output_list_.push_back(std::move(wayland_output)); - - OnWaylandOutputAdded(output_id); // Even if WaylandScreen has not been created, the output still must be // initialized, which results in setting up a wl_listener and getting the // geometry and the scaling factor from the Wayland Compositor. - wayland_output_ptr->Initialize(this); + wayland_output->Initialize(this); + DCHECK(!wayland_output->is_ready()); + + output_list_.push_back(std::move(wayland_output)); } -void WaylandOutputManager::RemoveWaylandOutput(const uint32_t output_id) { +void WaylandOutputManager::RemoveWaylandOutput(uint32_t output_id) { auto output_it = GetOutputItById(output_id); // Check the comment in the WaylandConnetion::GlobalRemove. if (output_it == output_list_.end()) return; + if (wayland_screen_) + wayland_screen_->OnOutputRemoved(output_id); output_list_.erase(output_it); - OnWaylandOutputRemoved(output_id); } std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen( @@ -64,10 +69,10 @@ std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen( // OutOutputHandleScale. All the other hot geometry and scale changes are done // automatically, and the |wayland_screen_| is notified immediately about the // changes. - if (!output_list_.empty()) { - for (auto& output : output_list_) { - OnWaylandOutputAdded(output->output_id()); - output->TriggerDelegateNotification(); + for (const auto& output : output_list_) { + if (output->is_ready()) { + wayland_screen->OnOutputAddedOrUpdated( + output->output_id(), output->bounds(), output->scale_factor()); } } @@ -90,22 +95,13 @@ WaylandOutput* WaylandOutputManager::GetOutput(uint32_t id) const { return output_it->get(); } -void WaylandOutputManager::OnWaylandOutputAdded(uint32_t output_id) { - if (wayland_screen_) - wayland_screen_->OnOutputAdded(output_id); -} - -void WaylandOutputManager::OnWaylandOutputRemoved(uint32_t output_id) { - if (wayland_screen_) - wayland_screen_->OnOutputRemoved(output_id); -} - void WaylandOutputManager::OnOutputHandleMetrics(uint32_t output_id, const gfx::Rect& new_bounds, int32_t scale_factor) { - if (wayland_screen_) - wayland_screen_->OnOutputMetricsChanged(output_id, new_bounds, + if (wayland_screen_) { + wayland_screen_->OnOutputAddedOrUpdated(output_id, new_bounds, scale_factor); + } } WaylandOutputManager::OutputList::const_iterator diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h index f05828a6d90..e02f10974c5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h @@ -44,9 +44,6 @@ class WaylandOutputManager : public WaylandOutput::Delegate { WaylandScreen* wayland_screen() const { return wayland_screen_.get(); } private: - void OnWaylandOutputAdded(uint32_t output_id); - void OnWaylandOutputRemoved(uint32_t output_id); - // WaylandOutput::Delegate: void OnOutputHandleMetrics(uint32_t output_id, const gfx::Rect& new_bounds, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc index 1658a68cc93..13cb8c0a3fc 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc @@ -10,6 +10,7 @@ #include "ui/events/event.h" #include "ui/events/types/event_type.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -45,7 +46,7 @@ void WaylandPointer::Enter(void* data, wl_fixed_t surface_y) { DCHECK(data); WaylandPointer* pointer = static_cast<WaylandPointer*>(data); - WaylandWindow* window = WaylandWindow::FromSurface(surface); + WaylandWindow* window = wl::RootWindowFromWlSurface(surface); gfx::PointF location{wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)}; pointer->delegate_->OnPointerFocusChanged(window, location); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h index b3f3a6ccbfa..c5cf9f7dccc 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h @@ -74,7 +74,9 @@ class WaylandPointer::Delegate { virtual void OnPointerDestroyed(WaylandPointer* pointer) = 0; virtual void OnPointerFocusChanged(WaylandWindow* window, const gfx::PointF& location) = 0; - virtual void OnPointerButtonEvent(EventType evtype, int changed_button) = 0; + virtual void OnPointerButtonEvent(EventType evtype, + int changed_button, + WaylandWindow* window = nullptr) = 0; virtual void OnPointerMotionEvent(const gfx::PointF& location) = 0; virtual void OnPointerAxisEvent(const gfx::Vector2d& offset) = 0; }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc index cf12f91f1b7..41dfb730784 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc @@ -85,8 +85,8 @@ TEST_P(WaylandPointerTest, Leave) { Sync(); - wl::MockSurface* other_surface = - server_.GetObject<wl::MockSurface>(other_widget); + wl::MockSurface* other_surface = server_.GetObject<wl::MockSurface>( + other_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(other_surface); wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), 0, 0); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc index ba52979990a..f1f7c8d43e5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc @@ -24,10 +24,12 @@ bool WaylandPopup::CreateShellPopup() { DCHECK(parent_window() && !shell_popup_); - auto bounds_px = AdjustPopupWindowPosition(); + auto subsurface_bounds_dip = + wl::TranslateWindowBoundsToParentDIP(this, parent_window()); ShellObjectFactory factory; - shell_popup_ = factory.CreateShellPopupWrapper(connection(), this, bounds_px); + shell_popup_ = factory.CreateShellPopupWrapper(connection(), this, + subsurface_bounds_dip); if (!shell_popup_) { LOG(ERROR) << "Failed to create Wayland shell popup"; return false; @@ -64,7 +66,7 @@ void WaylandPopup::Hide() { // Detach buffer from surface in order to completely shutdown popups and // tooltips, and release resources. - connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget()); + connection()->buffer_manager_host()->ResetSurfaceContents(root_surface()); } bool WaylandPopup::IsVisible() const { @@ -75,7 +77,7 @@ void WaylandPopup::HandlePopupConfigure(const gfx::Rect& bounds_dip) { DCHECK(shell_popup()); DCHECK(parent_window()); - SetBufferScale(parent_window()->buffer_scale(), true); + root_surface()->SetBufferScale(parent_window()->buffer_scale(), true); gfx::Rect new_bounds_dip = bounds_dip; @@ -138,25 +140,9 @@ bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) { return false; } // If parent window is known in advanced, we may set the scale early. - SetBufferScale(parent_window()->buffer_scale(), false); + root_surface()->SetBufferScale(parent_window()->buffer_scale(), false); set_ui_scale(parent_window()->ui_scale()); return true; } -gfx::Rect WaylandPopup::AdjustPopupWindowPosition() { - auto* top_level_parent = GetRootParentWindow(); - DCHECK(top_level_parent); - DCHECK(buffer_scale() == top_level_parent->buffer_scale()); - DCHECK(ui_scale() == top_level_parent->ui_scale()); - - // Chromium positions windows in screen coordinates, but Wayland requires them - // to be in local surface coordinates a.k.a relative to parent window. - const gfx::Rect parent_bounds_dip = - gfx::ScaleToRoundedRect(parent_window()->GetBounds(), 1.0 / ui_scale()); - gfx::Rect new_bounds_dip = wl::TranslateBoundsToParentCoordinates( - gfx::ScaleToRoundedRect(GetBounds(), 1.0 / ui_scale()), - parent_bounds_dip); - return gfx::ScaleToRoundedRect(new_bounds_dip, ui_scale() / buffer_scale()); -} - } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc index 81af6275b27..fc4d4fac5ca 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc @@ -10,8 +10,10 @@ #include "base/stl_util.h" #include "ui/display/display.h" #include "ui/display/display_finder.h" +#include "ui/display/display_list.h" #include "ui/display/display_observer.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" @@ -26,9 +28,10 @@ WaylandScreen::WaylandScreen(WaylandConnection* connection) WaylandScreen::~WaylandScreen() = default; -void WaylandScreen::OnOutputAdded(uint32_t output_id) { - display_list_.AddDisplay(display::Display(output_id), - display::DisplayList::Type::NOT_PRIMARY); +void WaylandScreen::OnOutputAddedOrUpdated(uint32_t output_id, + const gfx::Rect& bounds, + int32_t scale) { + AddOrUpdateDisplay(output_id, bounds, scale); } void WaylandScreen::OnOutputRemoved(uint32_t output_id) { @@ -49,41 +52,32 @@ void WaylandScreen::OnOutputRemoved(uint32_t output_id) { display_list_.RemoveDisplay(output_id); } -void WaylandScreen::OnOutputMetricsChanged(uint32_t output_id, - const gfx::Rect& new_bounds, - int32_t device_pixel_ratio) { +void WaylandScreen::AddOrUpdateDisplay(uint32_t output_id, + const gfx::Rect& new_bounds, + int32_t scale_factor) { display::Display changed_display(output_id); if (!display::Display::HasForceDeviceScaleFactor()) - changed_display.set_device_scale_factor(device_pixel_ratio); + changed_display.set_device_scale_factor(scale_factor); changed_display.set_bounds(new_bounds); changed_display.set_work_area(new_bounds); - bool is_primary = false; - display::Display display_nearest_origin = - GetDisplayNearestPoint(gfx::Point(0, 0)); - // If bounds of the nearest to origin display are empty, it must have been the - // very first and the same display added before. - if (display_nearest_origin.bounds().IsEmpty()) { - DCHECK_EQ(display_nearest_origin.id(), changed_display.id()); - is_primary = true; - } else if (changed_display.bounds().origin() < - display_nearest_origin.bounds().origin()) { - // If changed display is nearer to the origin than the previous display, - // that one must become a primary display. - is_primary = true; - } else if (changed_display.bounds().OffsetFromOrigin() == - display_nearest_origin.bounds().OffsetFromOrigin()) { - // If changed display has the same origin as the nearest to origin display, - // |changed_display| must become a primary one or it has already been the - // primary one. If a user changed positions of two displays (the second at - // x,x was set to 0,0), the second change will modify geometry of the - // display, which used to be the one nearest to the origin. - is_primary = true; + // There are 2 cases where |changed_display| must be set as primary: + // 1. When it is the first one being added to the |display_list_|. Or + // 2. If it is nearest the origin than the previous primary or has the same + // origin as it. When an user, for example, swaps two side-by-side displays, + // at some point, as the notification come in, both will have the same + // origin. + auto type = display::DisplayList::Type::NOT_PRIMARY; + if (display_list_.displays().empty()) { + type = display::DisplayList::Type::PRIMARY; + } else { + auto nearest_origin = GetDisplayNearestPoint({0, 0}).bounds().origin(); + auto changed_origin = changed_display.bounds().origin(); + if (changed_origin < nearest_origin || changed_origin == nearest_origin) + type = display::DisplayList::Type::PRIMARY; } - display_list_.UpdateDisplay( - changed_display, is_primary ? display::DisplayList::Type::PRIMARY - : display::DisplayList::Type::NOT_PRIMARY); + display_list_.AddOrUpdateDisplay(changed_display, type); auto* wayland_window_manager = connection_->wayland_window_manager(); for (auto* window : wayland_window_manager->GetWindowsOnOutput(output_id)) diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.h b/chromium/ui/ozone/platform/wayland/host/wayland_screen.h index 8d65dd8130e..55993fc2a1a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.h @@ -14,6 +14,10 @@ #include "ui/gfx/geometry/point.h" #include "ui/ozone/public/platform_screen.h" +namespace gfx { +class Rect; +} + namespace ui { class WaylandConnection; @@ -26,11 +30,10 @@ class WaylandScreen : public PlatformScreen { WaylandScreen& operator=(const WaylandScreen&) = delete; ~WaylandScreen() override; - void OnOutputAdded(uint32_t output_id); - void OnOutputRemoved(uint32_t output_id); - void OnOutputMetricsChanged(uint32_t output_id, + void OnOutputAddedOrUpdated(uint32_t output_id, const gfx::Rect& bounds, int32_t output_scale); + void OnOutputRemoved(uint32_t output_id); base::WeakPtr<WaylandScreen> GetWeakPtr(); @@ -53,6 +56,10 @@ class WaylandScreen : public PlatformScreen { void RemoveObserver(display::DisplayObserver* observer) override; private: + void AddOrUpdateDisplay(uint32_t output_id, + const gfx::Rect& bounds, + int32_t scale); + WaylandConnection* connection_ = nullptr; display::DisplayList display_list_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc index 899fff6445a..46b30f43b54 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc @@ -10,12 +10,15 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/display/display_observer.h" #include "ui/display/display_switches.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_output.h" #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" #include "ui/ozone/platform/wayland/host/wayland_screen.h" #include "ui/ozone/platform/wayland/test/mock_pointer.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/test_output.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -68,15 +71,19 @@ class TestDisplayObserver : public display::DisplayObserver { class WaylandScreenTest : public WaylandTest { public: - WaylandScreenTest() {} - ~WaylandScreenTest() override {} + WaylandScreenTest() = default; + ~WaylandScreenTest() override = default; void SetUp() override { output_ = server_.output(); - output_->SetRect(gfx::Rect(0, 0, kOutputWidth, kOutputHeight)); WaylandTest::SetUp(); + output_->SetRect({kOutputWidth, kOutputHeight}); + output_->SetScale(1); + output_->Flush(); + Sync(); + output_manager_ = connection_->wayland_output_manager(); ASSERT_TRUE(output_manager_); @@ -98,17 +105,6 @@ class WaylandScreenTest : public WaylandTest { std::move(properties)); } - void UpdateOutputGeometry(wl_resource* output_resource, - const gfx::Rect& new_rect) { - wl_output_send_geometry(output_resource, new_rect.x(), new_rect.y(), - 0 /* physical_width */, 0 /* physical_height */, - 0 /* subpixel */, "unknown_make", "unknown_model", - 0 /* transform */); - wl_output_send_mode(output_resource, WL_OUTPUT_MODE_CURRENT, - new_rect.width(), new_rect.height(), 0 /* refresh */); - wl_output_send_done(output_resource); - } - void ValidateTheDisplayForWidget(gfx::AcceleratedWidget widget, int64_t expected_display_id) { display::Display display_for_widget = @@ -146,18 +142,18 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { const int64_t old_primary_display_id = platform_screen_->GetPrimaryDisplay().id(); + gfx::Rect output1_rect = server_.output()->GetRect(); // Add a second display. wl::TestOutput* output2 = server_.CreateAndInitializeOutput(); Sync(); - // Update rect of that display. - gfx::Rect output1_rect = server_.output()->GetRect(); - gfx::Rect output2_rect(output1_rect.width(), 0, 800, 600); // The second display is located to the right of first display like // | || |. - UpdateOutputGeometry(output2->resource(), output2_rect); + gfx::Rect output2_rect(output1_rect.width(), 0, 800, 600); + output2->SetRect(output2_rect); + output2->Flush(); Sync(); @@ -179,7 +175,8 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { Sync(); // Updates rect again. - UpdateOutputGeometry(output2->resource(), output2_rect); + output2->SetRect(output2_rect); + output2->Flush(); Sync(); @@ -187,11 +184,14 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { added_display_id = observer.GetDisplay().id(); EXPECT_NE(platform_screen_->GetPrimaryDisplay().id(), added_display_id); - // Now, rearrange displays so that second display becomes a primary one. + // Now, rearrange displays so that second display becomes the primary one. output1_rect = gfx::Rect(1024, 0, 1024, 768); + output_->SetRect(output1_rect); + output_->Flush(); + output2_rect = gfx::Rect(0, 0, 1024, 768); - UpdateOutputGeometry(server_.output()->resource(), output1_rect); - UpdateOutputGeometry(output2->resource(), output2_rect); + output2->SetRect(output2_rect); + output2->Flush(); Sync(); @@ -215,25 +215,24 @@ TEST_P(WaylandScreenTest, OutputPropertyChanges) { TestDisplayObserver observer; platform_screen_->AddObserver(&observer); - const gfx::Rect new_rect(0, 0, 800, 600); - UpdateOutputGeometry(output_->resource(), new_rect); + gfx::Rect new_rect{100, 100}; + output_->SetRect(new_rect); + output_->Flush(); Sync(); - uint32_t changed_values = 0; - changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS; - changed_values |= display::DisplayObserver::DISPLAY_METRIC_WORK_AREA; + uint32_t changed_values = display::DisplayObserver::DISPLAY_METRIC_BOUNDS | + display::DisplayObserver::DISPLAY_METRIC_WORK_AREA; EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values); EXPECT_EQ(observer.GetDisplay().bounds(), new_rect); const int32_t new_scale_value = 2; output_->SetScale(new_scale_value); + output_->Flush(); Sync(); - changed_values = 0; - changed_values |= - display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; + changed_values = display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values); EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value); @@ -329,7 +328,8 @@ TEST_P(WaylandScreenTest, GetDisplayMatching) { // Place it on the right side of the primary display. const gfx::Rect output2_rect = gfx::Rect(primary_display.bounds().width(), 0, 1024, 768); - UpdateOutputGeometry(output2->resource(), output2_rect); + output2->SetRect(output2_rect); + output2->Flush(); Sync(); @@ -360,10 +360,10 @@ TEST_P(WaylandScreenTest, GetDisplayMatching) { platform_screen_->GetDisplayMatching(gfx::Rect(1019, 0, 10, 10)).id()); // Place second display 700 pixels below along y axis (1024:700,1024x768) - UpdateOutputGeometry( - output2->resource(), + output2->SetRect( gfx::Rect(gfx::Point(output2_rect.x(), output2_rect.y() + 700), output2_rect.size())); + output2->Flush(); Sync(); @@ -388,6 +388,8 @@ TEST_P(WaylandScreenTest, GetDisplayMatching) { platform_screen_->GetDisplayMatching(gfx::Rect(0, 0, 0, 0)).id()); platform_screen_->RemoveObserver(&observer); + output2->DestroyGlobal(); + Sync(); } TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) { @@ -406,7 +408,8 @@ TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) { // display. const gfx::Rect output2_rect = gfx::Rect(primary_display.bounds().width(), 0, 1024, 768); - UpdateOutputGeometry(output2->resource(), output2_rect); + output2->SetRect(output2_rect); + output2->Flush(); Sync(); @@ -419,7 +422,8 @@ TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) { ValidateTheDisplayForWidget(widget, primary_display.id()); // Now, send enter event for the surface, which was created before. - wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(widget); + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); ASSERT_TRUE(surface); wl_surface_send_enter(surface->resource(), output_->resource()); @@ -454,6 +458,9 @@ TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) { // The id of the entered display must correspond to the second output. ValidateTheDisplayForWidget(widget, secondary_display.id()); + + output2->DestroyGlobal(); + Sync(); } TEST_P(WaylandScreenTest, GetCursorScreenPoint) { @@ -463,7 +470,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) { PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget, &delegate); - auto* surface = server_.GetObject<wl::MockSurface>(window_->GetWidget()); + auto* surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); ASSERT_TRUE(surface); // Announce pointer capability so that WaylandPointer is created on the client @@ -488,8 +496,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) { // WaylandScreen must return the last pointer location. EXPECT_EQ(gfx::Point(10, 20), platform_screen_->GetCursorScreenPoint()); - auto* second_surface = - server_.GetObject<wl::MockSurface>(second_window->GetWidget()); + auto* second_surface = server_.GetObject<wl::MockSurface>( + second_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(second_surface); // Now, leave the first surface and enter second one. wl_pointer_send_leave(pointer->resource(), ++serial, surface->resource()); @@ -530,8 +538,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) { Sync(); - auto* menu_surface = - server_.GetObject<wl::MockSurface>(menu_window->GetWidget()); + auto* menu_surface = server_.GetObject<wl::MockSurface>( + menu_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(menu_surface); wl_pointer_send_enter(pointer->resource(), ++serial, menu_surface->resource(), @@ -577,8 +585,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) { Sync(); - auto* nested_menu_surface = - server_.GetObject<wl::MockSurface>(nested_menu_window->GetWidget()); + auto* nested_menu_surface = server_.GetObject<wl::MockSurface>( + nested_menu_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(nested_menu_surface); wl_pointer_send_enter(pointer->resource(), ++serial, @@ -616,6 +624,7 @@ TEST_P(WaylandScreenTest, SetBufferScale) { const int32_t kTripleScale = 3; EXPECT_CALL(*surface_, SetBufferScale(kTripleScale)); output_->SetScale(kTripleScale); + output_->Flush(); Sync(); @@ -637,6 +646,7 @@ TEST_P(WaylandScreenTest, SetBufferScale) { EXPECT_NE(kForcedUIScale, kDoubleScale); EXPECT_CALL(*surface_, SetBufferScale(kDoubleScale)); output_->SetScale(kDoubleScale); + output_->Flush(); Sync(); @@ -646,11 +656,92 @@ TEST_P(WaylandScreenTest, SetBufferScale) { display::Display::ResetForceDeviceScaleFactorForTesting(); } +namespace { + +class LazilyConfiguredScreenTest + : public WaylandTest, + public wl::TestWaylandServerThread::OutputDelegate { + public: + LazilyConfiguredScreenTest() = default; + LazilyConfiguredScreenTest(const LazilyConfiguredScreenTest&) = delete; + LazilyConfiguredScreenTest& operator=(const LazilyConfiguredScreenTest&) = + delete; + ~LazilyConfiguredScreenTest() override = default; + + void SetUp() override { + // Being the server's output delegate allows LazilyConfiguredScreenTest to + // manipulate wl_outputs during the server's global objects initialization + // phase. See SetupOutputs() function below. + server_.set_output_delegate(this); + + WaylandTest::SetUp(); + + output_manager_ = connection_->wayland_output_manager(); + ASSERT_TRUE(output_manager_); + } + + void TearDown() override { + WaylandTest::TearDown(); + server_.set_output_delegate(nullptr); + } + + protected: + // wl::TestWaylandServerThread::OutputDelegate: + void SetupOutputs(wl::TestOutput* primary) override { + // Keep the first wl_output announced "unconfigured" and just caches it for + // now, so we can exercise WaylandOutputManager::IsOutputReady() function + // when wl_output events come in unordered. + primary_output_ = primary; + + // Create/announce a second wl_output object and makes it the first one to + // get configuration events (eg: geometry, done, etc). This is achieved by + // setting its bounds here. + aux_output_ = server_.CreateAndInitializeOutput(); + aux_output_->SetRect({0, 0, 800, 600}); + } + + wl::TestOutput* primary_output_ = nullptr; + wl::TestOutput* aux_output_ = nullptr; + WaylandOutputManager* output_manager_ = nullptr; + bool auto_configure; +}; + +} // namespace + +// Ensures WaylandOutputManager and WaylandScreen properly handle scenarios +// where multiple wl_output objects are announced but not "configured" (ie: +// size, position, mode, etc sent to client) at bind time. +TEST_P(LazilyConfiguredScreenTest, DualOutput) { + // Ensure WaylandScreen got properly created and fed with a single display + // object, ie: |aux_output_| at server side. + EXPECT_TRUE(output_manager_->IsOutputReady()); + EXPECT_TRUE(screen_); + EXPECT_EQ(1u, screen_->GetAllDisplays().size()); + Sync(); + + // Send wl_output configuration events for the first advertised wl_output + // object. ie: |primary_output_| at server side. + primary_output_->SetRect({800, 0, kOutputWidth, kOutputHeight}); + primary_output_->SetScale(1); + primary_output_->Flush(); + Sync(); + + // And make sure it makes its way into the WaylandScreen's display list at + // client side. + EXPECT_EQ(2u, screen_->GetAllDisplays().size()); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandScreenTest, ::testing::Values(kXdgShellStable)); INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, WaylandScreenTest, ::testing::Values(kXdgShellV6)); +INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, + LazilyConfiguredScreenTest, + ::testing::Values(kXdgShellStable)); +INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, + LazilyConfiguredScreenTest, + ::testing::Values(kXdgShellV6)); } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc index e76aa6376dd..11d37ab75e1 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc @@ -1,44 +1,60 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/ozone/platform/wayland/host/wayland_subsurface.h" +#include <wayland-client.h> +#include <cstdint> + #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" -#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" -#include "ui/ozone/platform/wayland/host/wayland_window_manager.h" - -namespace ui { +#include "ui/ozone/platform/wayland/host/wayland_window.h" namespace { gfx::Rect AdjustSubsurfaceBounds(const gfx::Rect& bounds_px, const gfx::Rect& parent_bounds_px, float ui_scale, - int32_t buffer_scale) { + int32_t parent_buffer_scale) { + // TODO(fangzhoug): Verify the correctness of using ui_scale here, and in + // other ozone wayland files. const auto parent_bounds_dip = gfx::ScaleToRoundedRect(parent_bounds_px, 1.0 / ui_scale); + const auto bounds_dip = gfx::ScaleToRoundedRect(bounds_px, 1.0 / ui_scale); auto new_bounds_dip = - wl::TranslateBoundsToParentCoordinates(bounds_px, parent_bounds_dip); - return gfx::ScaleToRoundedRect(new_bounds_dip, ui_scale / buffer_scale); + wl::TranslateBoundsToParentCoordinates(bounds_dip, parent_bounds_dip); + return gfx::ScaleToRoundedRect(new_bounds_dip, + ui_scale / parent_buffer_scale); } } // namespace -WaylandSubsurface::WaylandSubsurface(PlatformWindowDelegate* delegate, - WaylandConnection* connection) - : WaylandWindow(delegate, connection) {} +namespace ui { + +WaylandSubsurface::WaylandSubsurface(WaylandConnection* connection, + WaylandWindow* parent) + : wayland_surface_(connection, parent), + connection_(connection), + parent_(parent) { + DCHECK(parent_); + DCHECK(connection_); + if (!surface()) { + LOG(ERROR) << "Failed to create wl_surface"; + return; + } +} WaylandSubsurface::~WaylandSubsurface() = default; -void WaylandSubsurface::Show(bool inactive) { - if (subsurface_) - return; +gfx::AcceleratedWidget WaylandSubsurface::GetWidget() const { + return wayland_surface_.GetWidget(); +} - CreateSubsurface(); - UpdateBufferScale(false); +void WaylandSubsurface::Show() { + if (!subsurface_) + CreateSubsurface(); } void WaylandSubsurface::Hide() { @@ -46,91 +62,89 @@ void WaylandSubsurface::Hide() { return; subsurface_.reset(); - - // Detach buffer from surface in order to completely shutdown menus and - // tooltips, and release resources. - connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget()); + connection_->buffer_manager_host()->ResetSurfaceContents(wayland_surface()); } bool WaylandSubsurface::IsVisible() const { return !!subsurface_; } -void WaylandSubsurface::SetBounds(const gfx::Rect& bounds) { - auto old_bounds = GetBounds(); - WaylandWindow::SetBounds(bounds); +void WaylandSubsurface::UpdateOpaqueRegion() { + gfx::Size region_size = enable_blend_ ? gfx::Size() : bounds_px_.size(); + wl::Object<wl_region> region( + wl_compositor_create_region(connection_->compositor())); + wl_region_add(region.get(), 0, 0, region_size.width(), region_size.height()); + wl_surface_set_opaque_region(surface(), region.get()); +} - if (old_bounds == bounds || !parent_window()) +void WaylandSubsurface::SetBounds(const gfx::Rect& bounds) { + if (bounds_px_ == bounds) return; - // Translate location from screen to surface coordinates. - auto bounds_px = AdjustSubsurfaceBounds( - GetBounds(), parent_window()->GetBounds(), ui_scale(), buffer_scale()); - wl_subsurface_set_position(subsurface_.get(), bounds_px.x() / buffer_scale(), - bounds_px.y() / buffer_scale()); - wl_surface_commit(surface()); - connection()->ScheduleFlush(); + bounds_px_ = bounds; + if (IsVisible()) { + // Translate location from screen to surface coordinates. + auto bounds_px = + AdjustSubsurfaceBounds(bounds_px_, parent_->GetBounds(), + parent_->ui_scale(), parent_->buffer_scale()); + wl_subsurface_set_position(subsurface_.get(), bounds_px.x(), bounds_px.y()); + } } void WaylandSubsurface::CreateSubsurface() { - auto* parent = parent_window(); - if (!parent) { - // wl_subsurface can be used for several purposes: tooltips and drag arrow - // windows. If we are in a drag process, use the entered window. Otherwise, - // it must be a tooltip. - if (connection()->IsDragInProgress()) { - parent = connection()->data_drag_controller()->entered_window(); - set_parent_window(parent); - } else { - // If Aura does not not provide a reference parent window, needed by - // Wayland, we get the current focused window to place and show the - // tooltips. - parent = - connection()->wayland_window_manager()->GetCurrentFocusedWindow(); - } - } + DCHECK(parent_); - // Tooltip and drag arrow creation is an async operation. By the time Aura - // actually creates them, it is possible that the user has already moved the - // mouse/pointer out of the window that triggered the tooltip, or user is no - // longer in a drag/drop process. In this case, parent is NULL. - if (!parent) - return; - - wl_subcompositor* subcompositor = connection()->subcompositor(); + wl_subcompositor* subcompositor = connection_->subcompositor(); DCHECK(subcompositor); - subsurface_.reset(wl_subcompositor_get_subsurface(subcompositor, surface(), - parent->surface())); + subsurface_ = wayland_surface()->CreateSubsurface(parent_->root_surface()); - // Chromium positions tooltip windows in screen coordinates, but Wayland - // requires them to be in local surface coordinates a.k.a relative to parent - // window. - auto bounds_px = AdjustSubsurfaceBounds(GetBounds(), parent->GetBounds(), - ui_scale(), buffer_scale()); + // Chromium positions quads in display::Display coordinates in physical + // pixels, but Wayland requires them to be in local surface coordinates a.k.a + // relative to parent window. + auto bounds_px = + AdjustSubsurfaceBounds(bounds_px_, parent_->GetBounds(), + parent_->ui_scale(), parent_->buffer_scale()); DCHECK(subsurface_); - // Convert position to DIP. - wl_subsurface_set_position(subsurface_.get(), bounds_px.x() / buffer_scale(), - bounds_px.y() / buffer_scale()); - wl_subsurface_set_desync(subsurface_.get()); - wl_surface_commit(parent->surface()); - connection()->ScheduleFlush(); - - // Notify the observers the window has been configured. Please note that - // subsurface doesn't send ack configure events. Thus, notify the observers as - // soon as the subsurface is created. - connection()->wayland_window_manager()->NotifyWindowConfigured(this); + wl_subsurface_set_position(subsurface_.get(), bounds_px.x(), bounds_px.y()); + wl_subsurface_set_sync(subsurface_.get()); + + // Subsurfaces don't need to trap input events. Its display rect is fully + // contained in |parent_|'s. Setting input_region to empty allows |parent_| to + // dispatch all of the input to platform window. + wl::Object<wl_region> region( + wl_compositor_create_region(connection_->compositor())); + wl_region_add(region.get(), 0, 0, 0, 0); + wl_surface_set_input_region(surface(), region.get()); } -bool WaylandSubsurface::OnInitialize(PlatformWindowInitProperties properties) { - // If we do not have parent window provided, we must always use a focused - // window or a window that entered drag whenever the subsurface is created. - if (properties.parent_widget == gfx::kNullAcceleratedWidget) { - DCHECK(!parent_window()); - return true; +void WaylandSubsurface::ConfigureAndShowSurface( + gfx::OverlayTransform transform, + const gfx::Rect& bounds_rect, + bool enable_blend, + const WaylandSurface* reference_below, + const WaylandSurface* reference_above) { + wayland_surface()->SetBufferScale(parent_->buffer_scale(), false); + + gfx::Rect bounds_px{ + bounds_rect.origin() + parent_->GetBounds().origin().OffsetFromOrigin(), + bounds_rect.size()}; + auto old_bounds = bounds_px_; + SetBounds(bounds_px); + + if (old_bounds != bounds_px_ || enable_blend_ != enable_blend) { + enable_blend_ = enable_blend; + UpdateOpaqueRegion(); + } + + Show(); + + DCHECK(!reference_above || !reference_below); + if (reference_below) { + wl_subsurface_place_above(subsurface_.get(), reference_below->surface()); + } else if (reference_above) { + wl_subsurface_place_below(subsurface_.get(), reference_above->surface()); } - set_parent_window(GetParentWindow(properties.parent_widget)); - return true; } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h index e5c8bed26f8..50098b21715 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h @@ -1,36 +1,72 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // 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_SUBSURFACE_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SUBSURFACE_H_ -#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/overlay_transform.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/wayland_surface.h" namespace ui { +class WaylandConnection; +class WaylandWindow; -class WaylandSubsurface : public WaylandWindow { +// Wraps a wl_surface with a wl_subsurface role assigned. It is used to submit a +// buffer as a sub region of WaylandWindow. +class WaylandSubsurface { public: - WaylandSubsurface(PlatformWindowDelegate* delegate, - WaylandConnection* connection); - ~WaylandSubsurface() override; + WaylandSubsurface(WaylandConnection* connection, WaylandWindow* parent); + WaylandSubsurface(const WaylandSubsurface&) = delete; + WaylandSubsurface& operator=(const WaylandSubsurface&) = delete; + ~WaylandSubsurface(); - // PlatformWindow overrides: - void Show(bool inactive) override; - void Hide() override; - bool IsVisible() const override; - void SetBounds(const gfx::Rect& bounds) override; + wl_surface* surface() const { return wayland_surface_.surface(); } + int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); } + WaylandSurface* wayland_surface() { return &wayland_surface_; } + gfx::Rect bounds_px() { return bounds_px_; } + bool IsOpaque() const { return !enable_blend_; } - private: - // WaylandWindow overrides: - bool OnInitialize(PlatformWindowInitProperties properties) override; + gfx::AcceleratedWidget GetWidget() const; + + // Sets up wl_surface and wl_subsurface. Allows an overlay to be shown + // correctly once a wl_buffer is attached. + void ConfigureAndShowSurface(gfx::OverlayTransform transform, + const gfx::Rect& bounds_rect, + bool enable_blend, + const WaylandSurface* reference_below, + const WaylandSurface* reference_above); + + // Assigns wl_subsurface role to the wl_surface so it is visible when a + // wl_buffer is attached. + void Show(); + // Remove wl_subsurface role to make this invisible. + void Hide(); + bool IsVisible() const; - // Creates (if necessary) and shows a subsurface window. + private: + // Helper of Show(). It does the role-assigning to wl_surface. void CreateSubsurface(); + void SetBounds(const gfx::Rect& bounds); + + // Tells wayland compositor to update the opaque region according to + // |enable_blend_| and |bounds_px_|. + void UpdateOpaqueRegion(); + WaylandSurface wayland_surface_; wl::Object<wl_subsurface> subsurface_; - DISALLOW_COPY_AND_ASSIGN(WaylandSubsurface); + WaylandConnection* const connection_; + // |parent_| refers to the WaylandWindow whose wl_surface is the parent to + // this subsurface. + WaylandWindow* const parent_; + + // Pixel bounds within the display to position this subsurface. + gfx::Rect bounds_px_; + bool enable_blend_ = true; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc index c2cf21baf62..0300a7bcacc 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc @@ -4,21 +4,139 @@ #include "ui/ozone/platform/wayland/host/wayland_surface.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" namespace ui { -WaylandSurface::WaylandSurface() = default; -WaylandSurface::~WaylandSurface() = default; +WaylandSurface::WaylandSurface(WaylandConnection* connection, + WaylandWindow* root_window) + : connection_(connection), + root_window_(root_window), + surface_(connection->CreateSurface()) {} -gfx::AcceleratedWidget WaylandSurface::GetWidget() const { +WaylandSurface::~WaylandSurface() { + if (surface_) { + wl_surface_add_listener(surface_.get(), nullptr, nullptr); + wl_surface_set_user_data(surface_.get(), nullptr); + } +} + +uint32_t WaylandSurface::GetSurfaceId() const { if (!surface_) - return gfx::kNullAcceleratedWidget; + return 0u; return surface_.id(); } -gfx::AcceleratedWidget WaylandSurface::GetRootWidget() const { +gfx::AcceleratedWidget WaylandSurface::GetWidget() const { return root_window_->GetWidget(); } +bool WaylandSurface::Initialize() { + if (!surface_) + return false; + + wl_surface_set_user_data(surface_.get(), this); + + static struct wl_surface_listener surface_listener = { + &WaylandSurface::Enter, + &WaylandSurface::Leave, + }; + wl_surface_add_listener(surface_.get(), &surface_listener, this); + + return true; +} + +void WaylandSurface::AttachBuffer(wl_buffer* buffer) { + // The logic in DamageBuffer currently relies on attachment coordinates of + // (0, 0). If this changes, then the calculation in DamageBuffer will also + // need to be updated. + wl_surface_attach(surface_.get(), buffer, 0, 0); + connection_->ScheduleFlush(); +} + +void WaylandSurface::Damage(const gfx::Rect& pending_damage_region) { + if (connection_->compositor_version() >= + WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { + // wl_surface_damage_buffer relies on compositor API version 4. See + // https://bit.ly/2u00lv6 for details. + // We don't need to apply any scaling because pending_damage_region is + // already in buffer coordinates. + wl_surface_damage_buffer( + surface_.get(), pending_damage_region.x(), pending_damage_region.y(), + pending_damage_region.width(), pending_damage_region.height()); + } else { + // The calculation for damage region relies on two assumptions: + // 1) The buffer is always attached at surface location (0, 0) + // 2) The API wl_surface::set_buffer_transform is not used. + // It's possible to write logic that accounts for both cases above, but + // it's currently unnecessary. + // + // Note: The damage region may not be an integer multiple of scale. To + // keep the implementation simple, the x() and y() coordinates round down, + // and the width() and height() calculations always add an extra pixel. + wl_surface_damage(surface_.get(), pending_damage_region.x() / buffer_scale_, + pending_damage_region.y() / buffer_scale_, + pending_damage_region.width() / buffer_scale_ + 1, + pending_damage_region.height() / buffer_scale_ + 1); + } + connection_->ScheduleFlush(); +} + +void WaylandSurface::Commit() { + wl_surface_commit(surface_.get()); + connection_->ScheduleFlush(); +} + +void WaylandSurface::SetBufferScale(int32_t new_scale, bool update_bounds) { + DCHECK_GT(new_scale, 0); + + if (new_scale == buffer_scale_) + return; + + buffer_scale_ = new_scale; + wl_surface_set_buffer_scale(surface_.get(), buffer_scale_); + connection_->ScheduleFlush(); +} + +void WaylandSurface::SetBounds(const gfx::Rect& bounds_px) { + // It's important to set opaque region for opaque windows (provides + // optimization hint for the Wayland compositor). + if (!root_window_->IsOpaqueWindow()) + return; + + wl::Object<wl_region> region( + wl_compositor_create_region(connection_->compositor())); + wl_region_add(region.get(), 0, 0, bounds_px.width(), bounds_px.height()); + + wl_surface_set_opaque_region(surface_.get(), region.get()); + + connection_->ScheduleFlush(); +} + +wl::Object<wl_subsurface> WaylandSurface::CreateSubsurface( + WaylandSurface* parent) { + DCHECK(parent); + wl_subcompositor* subcompositor = connection_->subcompositor(); + DCHECK(subcompositor); + wl::Object<wl_subsurface> subsurface(wl_subcompositor_get_subsurface( + subcompositor, surface_.get(), parent->surface_.get())); + return subsurface; +} + +// static +void WaylandSurface::Enter(void* data, + struct wl_surface* wl_surface, + struct wl_output* output) { + static_cast<WaylandSurface*>(data)->root_window_->AddEnteredOutputId(output); +} + +// static +void WaylandSurface::Leave(void* data, + struct wl_surface* wl_surface, + struct wl_output* output) { + static_cast<WaylandSurface*>(data)->root_window_->RemoveEnteredOutputId( + output); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h index e432ceb7e7d..37755f9ac3d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h @@ -5,17 +5,21 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_ +#include <cstdint> + +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" namespace ui { +class WaylandConnection; class WaylandWindow; // Wrapper of a wl_surface, owned by a WaylandWindow or a WlSubsurface. class WaylandSurface { public: - WaylandSurface(); + WaylandSurface(WaylandConnection* connection, WaylandWindow* root_window); WaylandSurface(const WaylandSurface&) = delete; WaylandSurface& operator=(const WaylandSurface&) = delete; ~WaylandSurface(); @@ -23,20 +27,55 @@ class WaylandSurface { WaylandWindow* root_window() const { return root_window_; } wl_surface* surface() const { return surface_.get(); } int32_t buffer_scale() const { return buffer_scale_; } + void set_buffer_scale(int32_t scale) { buffer_scale_ = scale; } - // gfx::AcceleratedWidget identifies a wl_surface or a ui::WaylandWindow. Note - // that GetWidget() and GetRootWidget() do not necessarily return the same - // result. + // Returns an id that identifies the |wl_surface_|. + uint32_t GetSurfaceId() const; + // Returns a gfx::AcceleratedWidget that identifies the WaylandWindow that + // this WaylandSurface belongs to. gfx::AcceleratedWidget GetWidget() const; - gfx::AcceleratedWidget GetRootWidget() 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(); + + // Attaches the given wl_buffer to the underlying wl_surface at (0, 0). + void AttachBuffer(wl_buffer* buffer); + + // Damages the surface according to |pending_damage_region|, which should be + // in surface coordinates (dp). + void Damage(const gfx::Rect& pending_damage_region); + + // Commits the underlying wl_surface. + void Commit(); + + // Sets the buffer scale for this surface. + void SetBufferScale(int32_t scale, bool update_bounds); + + // Sets the bounds on this surface. This is used for determining the opaque + // region. + void SetBounds(const gfx::Rect& bounds_px); + + // 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); private: - WaylandWindow* root_window_ = nullptr; + WaylandConnection* const connection_; + WaylandWindow* const root_window_; wl::Object<wl_surface> surface_; + // Wayland's scale factor for the output that this window currently belongs // to. int32_t buffer_scale_ = 1; - friend class WaylandWindow; + + // 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); }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index 10ecbcf7b47..86bab661269 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc @@ -4,6 +4,9 @@ #include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" +#include "base/run_loop.h" +#include "base/unguessable_token.h" +#include "build/lacros_buildflags.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/hit_test.h" @@ -14,8 +17,10 @@ #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_event_source.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h" -#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "ui/platform_window/extensions/wayland_extension.h" +#include "ui/platform_window/wm/wm_drop_handler.h" namespace ui { @@ -36,6 +41,7 @@ WaylandToplevelWindow::~WaylandToplevelWindow() { drag_handler_delegate_->OnDragFinished( DragDropTypes::DragOperation::DRAG_NONE); } + CancelDrag(); } bool WaylandToplevelWindow::CreateShellSurface() { @@ -46,7 +52,11 @@ bool WaylandToplevelWindow::CreateShellSurface() { return false; } - shell_surface_->SetAppId(app_id_); +#if BUILDFLAG(IS_LACROS) + shell_surface_->SetAppId(window_unique_id_); +#else + shell_surface_->SetAppId(wm_class_class_); +#endif shell_surface_->SetTitle(window_title_); SetSizeConstraints(); TriggerStateChanges(); @@ -78,13 +88,29 @@ void WaylandToplevelWindow::DispatchHostWindowDragMovement( connection()->ScheduleFlush(); } -void WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data, +bool WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data, int operation, gfx::NativeCursor cursor, + bool can_grab_pointer, WmDragHandler::Delegate* delegate) { DCHECK(!drag_handler_delegate_); drag_handler_delegate_ = delegate; connection()->data_drag_controller()->StartSession(data, operation); + + base::RunLoop drag_loop(base::RunLoop::Type::kNestableTasksAllowed); + drag_loop_quit_closure_ = drag_loop.QuitClosure(); + + auto alive = weak_ptr_factory_.GetWeakPtr(); + drag_loop.Run(); + if (!alive) + return false; + return true; +} + +void WaylandToplevelWindow::CancelDrag() { + if (drag_loop_quit_closure_.is_null()) + return; + std::move(drag_loop_quit_closure_).Run(); } void WaylandToplevelWindow::Show(bool inactive) { @@ -113,7 +139,7 @@ void WaylandToplevelWindow::Hide() { // Detach buffer from surface in order to completely shutdown menus and // tooltips, and release resources. - connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget()); + connection()->buffer_manager_host()->ResetSurfaceContents(root_surface()); } bool WaylandToplevelWindow::IsVisible() const { @@ -182,6 +208,14 @@ void WaylandToplevelWindow::SizeConstraintsChanged() { SetSizeConstraints(); } +std::string WaylandToplevelWindow::GetWindowUniqueId() const { +#if BUILDFLAG(IS_LACROS) + return window_unique_id_; +#else + return std::string(); +#endif +} + void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width, int32_t height, bool is_maximized, @@ -254,9 +288,11 @@ void WaylandToplevelWindow::OnDragEnter(const gfx::PointF& point, // Wayland sends locations in DIP so they need to be translated to // physical pixels. + // TODO(crbug.com/1102857): get the real event modifier here. drop_handler->OnDragEnter( gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data), - operation); + operation, + /*modifiers=*/0); } int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point, @@ -267,15 +303,18 @@ int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point, // Wayland sends locations in DIP so they need to be translated to // physical pixels. + // TODO(crbug.com/1102857): get the real event modifier here. return drop_handler->OnDragMotion( - gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation); + gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation, + /*modifiers=*/0); } void WaylandToplevelWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) { WmDropHandler* drop_handler = GetWmDropHandler(*this); if (!drop_handler) return; - drop_handler->OnDragDrop(std::move(data)); + // TODO(crbug.com/1102857): get the real event modifier here. + drop_handler->OnDragDrop(std::move(data), /*modifiers=*/0); } void WaylandToplevelWindow::OnDragLeave() { @@ -290,11 +329,18 @@ void WaylandToplevelWindow::OnDragSessionClose(uint32_t dnd_action) { drag_handler_delegate_->OnDragFinished(dnd_action); drag_handler_delegate_ = nullptr; connection()->event_source()->ResetPointerFlags(); + std::move(drag_loop_quit_closure_).Run(); } bool WaylandToplevelWindow::OnInitialize( PlatformWindowInitProperties properties) { - app_id_ = properties.wm_class_class; +#if BUILDFLAG(IS_LACROS) + auto token = base::UnguessableToken::Create(); + window_unique_id_ = "org.chromium.lacros." + token.ToString(); +#else + wm_class_class_ = properties.wm_class_class; +#endif + SetWaylandExtension(this, static_cast<WaylandExtension*>(this)); SetWmMoveLoopHandler(this, static_cast<WmMoveLoopHandler*>(this)); return true; } @@ -309,6 +355,11 @@ void WaylandToplevelWindow::EndMoveLoop() { connection()->window_drag_controller()->StopDragging(); } +void WaylandToplevelWindow::StartWindowDraggingSessionIfNeeded() { + DCHECK(connection()->window_drag_controller()); + connection()->window_drag_controller()->StartDragSession(); +} + void WaylandToplevelWindow::TriggerStateChanges() { if (!shell_surface_) return; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h index 0455078ee38..4c0a6a7d013 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h @@ -5,11 +5,13 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ +#include "build/lacros_buildflags.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" -#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" -#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h" -#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" +#include "ui/platform_window/extensions/wayland_extension.h" +#include "ui/platform_window/wm/wm_drag_handler.h" +#include "ui/platform_window/wm/wm_move_loop_handler.h" +#include "ui/platform_window/wm/wm_move_resize_handler.h" namespace ui { @@ -18,7 +20,8 @@ class ShellSurfaceWrapper; class WaylandToplevelWindow : public WaylandWindow, public WmMoveResizeHandler, public WmDragHandler, - public WmMoveLoopHandler { + public WmMoveLoopHandler, + public WaylandExtension { public: WaylandToplevelWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection); @@ -38,10 +41,12 @@ class WaylandToplevelWindow : public WaylandWindow, const gfx::Point& pointer_location_in_px) override; // WmDragHandler - void StartDrag(const ui::OSExchangeData& data, + bool StartDrag(const ui::OSExchangeData& data, int operation, gfx::NativeCursor cursor, + bool can_grab_pointer, WmDragHandler::Delegate* delegate) override; + void CancelDrag() override; // PlatformWindow void Show(bool inactive) override; @@ -54,6 +59,7 @@ class WaylandToplevelWindow : public WaylandWindow, void Restore() override; PlatformWindowState GetPlatformWindowState() const override; void SizeConstraintsChanged() override; + std::string GetWindowUniqueId() const override; private: // WaylandWindow overrides: @@ -75,6 +81,9 @@ class WaylandToplevelWindow : public WaylandWindow, bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; void EndMoveLoop() override; + // WaylandExtension: + void StartWindowDraggingSessionIfNeeded() override; + void TriggerStateChanges(); void SetWindowState(PlatformWindowState state); @@ -112,11 +121,17 @@ class WaylandToplevelWindow : public WaylandWindow, bool is_active_ = false; +#if BUILDFLAG(IS_LACROS) + // Unique ID for this window. May be shared over non-Wayland IPC transports + // (e.g. mojo) to identify the window. + std::string window_unique_id_; +#else // Id of the chromium app passed through // PlatformWindowInitProperties::wm_class_class. This is used by Wayland // compositor to identify the app, unite it's windows into the same stack of // windows and find *.desktop file to set various preferences including icons. - std::string app_id_; + std::string wm_class_class_; +#endif // Title of the ShellSurface. base::string16 window_title_; @@ -124,6 +139,10 @@ class WaylandToplevelWindow : public WaylandWindow, // Max and min sizes of the WaylandToplevelWindow window. base::Optional<gfx::Size> min_size_; base::Optional<gfx::Size> max_size_; + + base::OnceClosure drag_loop_quit_closure_; + + base::WeakPtrFactory<WaylandToplevelWindow> weak_ptr_factory_{this}; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc index aff5c24befa..9298a43d67d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc @@ -8,6 +8,7 @@ #include "base/time/time.h" #include "ui/gfx/geometry/point_f.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -47,7 +48,7 @@ void WaylandTouch::Down(void* data, DCHECK(touch); touch->connection_->set_serial(serial); - WaylandWindow* window = WaylandWindow::FromSurface(surface); + WaylandWindow* window = wl::RootWindowFromWlSurface(surface); gfx::PointF location(wl_fixed_to_double(x), wl_fixed_to_double(y)); base::TimeTicks timestamp = base::TimeTicks() + base::TimeDelta::FromMilliseconds(time); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc index 21ebb192e5a..2e436462afb 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc @@ -6,6 +6,7 @@ #include <wayland-client.h> +#include <algorithm> #include <memory> #include "base/bind.h" @@ -21,30 +22,44 @@ #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" #include "ui/ozone/platform/wayland/host/wayland_pointer.h" +#include "ui/ozone/platform/wayland/host/wayland_subsurface.h" +#include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h" + +namespace { + +bool OverlayStackOrderCompare( + const ui::ozone::mojom::WaylandOverlayConfigPtr& i, + const ui::ozone::mojom::WaylandOverlayConfigPtr& j) { + return i->z_order < j->z_order; +} + +} // namespace namespace ui { WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection) - : delegate_(delegate), connection_(connection) {} + : delegate_(delegate), + connection_(connection), + accelerated_widget_( + connection->wayland_window_manager()->AllocateAcceleratedWidget()) {} WaylandWindow::~WaylandWindow() { + shutting_down_ = true; + PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - if (surface()) + + for (const auto& widget_subsurface : wayland_subsurfaces()) { + connection_->wayland_window_manager()->RemoveSubsurface( + GetWidget(), widget_subsurface.get()); + } + if (root_surface_) connection_->wayland_window_manager()->RemoveWindow(GetWidget()); if (parent_window_) parent_window_->set_child_window(nullptr); } -// static -WaylandWindow* WaylandWindow::FromSurface(wl_surface* surface) { - if (!surface) - return nullptr; - return static_cast<WaylandWindow*>( - wl_proxy_get_user_data(reinterpret_cast<wl_proxy*>(surface))); -} - void WaylandWindow::OnWindowLostCapture() { delegate_->OnLostCapture(); } @@ -76,12 +91,17 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) { else ui_scale_ = display.device_scale_factor(); } - SetBufferScale(new_scale, update_bounds); + // At this point, buffer_scale() still returns the old scale. + if (update_bounds) + SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / buffer_scale())); + + root_surface_->SetBufferScale(new_scale, update_bounds); } gfx::AcceleratedWidget WaylandWindow::GetWidget() const { - return wayland_surface_.GetWidget(); + return accelerated_widget_; } + void WaylandWindow::SetPointerFocus(bool focus) { has_pointer_focus_ = focus; @@ -116,10 +136,7 @@ void WaylandWindow::SetBounds(const gfx::Rect& bounds_px) { return; bounds_px_ = bounds_px; - // Opaque region is based on the size of the window. Thus, update the region - // on each update. - MaybeUpdateOpaqueRegion(); - + root_surface_->SetBounds(bounds_px); delegate_->OnBoundsChanged(bounds_px_); } @@ -316,24 +333,19 @@ void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) { } bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { + root_surface_ = std::make_unique<WaylandSurface>(connection_, this); + if (!root_surface_->Initialize()) { + LOG(ERROR) << "Failed to create wl_surface"; + return false; + } + // Properties contain DIP bounds but the buffer scale is initially 1 so it's // OK to assign. The bounds will be recalculated when the buffer scale // changes. - DCHECK_EQ(buffer_scale(), 1); bounds_px_ = properties.bounds; opacity_ = properties.opacity; type_ = properties.type; - wayland_surface_.surface_.reset( - wl_compositor_create_surface(connection_->compositor())); - wayland_surface_.root_window_ = this; - if (!surface()) { - LOG(ERROR) << "Failed to create wl_surface"; - return false; - } - wl_surface_set_user_data(surface(), this); - AddSurfaceListener(); - connection_->wayland_window_manager()->AddWindow(GetWidget(), this); if (!OnInitialize(std::move(properties))) @@ -346,27 +358,11 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { // Will do nothing for menus because they have got their scale above. UpdateBufferScale(false); + root_surface_->SetBounds(bounds_px_); - MaybeUpdateOpaqueRegion(); return true; } -void WaylandWindow::SetBufferScale(int32_t new_scale, bool update_bounds) { - DCHECK_GT(new_scale, 0); - - if (new_scale == buffer_scale()) - return; - - auto old_scale = buffer_scale(); - wayland_surface_.buffer_scale_ = new_scale; - if (update_bounds) - SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / old_scale)); - - DCHECK(surface()); - wl_surface_set_buffer_scale(surface(), buffer_scale()); - connection_->ScheduleFlush(); -} - WaylandWindow* WaylandWindow::GetParentWindow( gfx::AcceleratedWidget parent_widget) { auto* parent_window = @@ -393,14 +389,6 @@ WaylandWindow* WaylandWindow::GetRootParentWindow() { return parent_window_ ? parent_window_->GetRootParentWindow() : this; } -void WaylandWindow::AddSurfaceListener() { - static struct wl_surface_listener surface_listener = { - &WaylandWindow::Enter, - &WaylandWindow::Leave, - }; - wl_surface_add_listener(surface(), &surface_listener, this); -} - void WaylandWindow::AddEnteredOutputId(struct wl_output* output) { // Wayland does weird things for menus so instead of tracking outputs that // we entered or left, we take that from the parent window and ignore this @@ -477,18 +465,6 @@ WaylandWindow* WaylandWindow::GetTopMostChildWindow() { return child_window_ ? child_window_->GetTopMostChildWindow() : this; } -void WaylandWindow::MaybeUpdateOpaqueRegion() { - if (!IsOpaqueWindow()) - return; - - wl::Object<wl_region> region( - wl_compositor_create_region(connection_->compositor())); - wl_region_add(region.get(), 0, 0, bounds_px_.width(), bounds_px_.height()); - wl_surface_set_opaque_region(surface(), region.get()); - - connection_->ScheduleFlush(); -} - bool WaylandWindow::IsOpaqueWindow() const { return opacity_ == ui::PlatformWindowOpacity::kOpaqueWindow; } @@ -505,26 +481,145 @@ uint32_t WaylandWindow::DispatchEventToDelegate( return handled ? POST_DISPATCH_STOP_PROPAGATION : POST_DISPATCH_NONE; } -// static -void WaylandWindow::Enter(void* data, - struct wl_surface* wl_surface, - struct wl_output* output) { - auto* window = static_cast<WaylandWindow*>(data); - if (window) { - DCHECK(window->surface() == wl_surface); - window->AddEnteredOutputId(output); +std::unique_ptr<WaylandSurface> WaylandWindow::TakeWaylandSurface() { + DCHECK(shutting_down_); + DCHECK(root_surface_); + return std::move(root_surface_); +} + +bool WaylandWindow::RequestSubsurface() { + auto subsurface = std::make_unique<WaylandSubsurface>(connection_, this); + if (!subsurface->surface()) + return false; + connection_->wayland_window_manager()->AddSubsurface(GetWidget(), + subsurface.get()); + subsurface_stack_above_.push_back(subsurface.get()); + auto result = wayland_subsurfaces_.emplace(std::move(subsurface)); + DCHECK(result.second); + return true; +} + +bool WaylandWindow::ArrangeSubsurfaceStack(size_t above, size_t below) { + while (wayland_subsurfaces_.size() < above + below) { + if (!RequestSubsurface()) + return false; } + + DCHECK(subsurface_stack_below_.size() + subsurface_stack_above_.size() >= + above + below); + + if (subsurface_stack_above_.size() < above) { + auto splice_start = subsurface_stack_below_.begin(); + for (size_t i = 0; i < below; ++i) + ++splice_start; + subsurface_stack_above_.splice(subsurface_stack_above_.end(), + subsurface_stack_below_, splice_start, + subsurface_stack_below_.end()); + + } else if (subsurface_stack_below_.size() < below) { + auto splice_start = subsurface_stack_above_.end(); + for (size_t i = 0; i < below - subsurface_stack_below_.size(); ++i) + --splice_start; + subsurface_stack_below_.splice(subsurface_stack_below_.end(), + subsurface_stack_above_, splice_start, + subsurface_stack_above_.end()); + } + + DCHECK(subsurface_stack_below_.size() >= below); + DCHECK(subsurface_stack_above_.size() >= above); + return true; } -// static -void WaylandWindow::Leave(void* data, - struct wl_surface* wl_surface, - struct wl_output* output) { - auto* window = static_cast<WaylandWindow*>(data); - if (window) { - DCHECK(window->surface() == wl_surface); - window->RemoveEnteredOutputId(output); +bool WaylandWindow::CommitOverlays( + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr>& overlays) { + // |overlays| is sorted from bottom to top. + std::sort(overlays.begin(), overlays.end(), OverlayStackOrderCompare); + + // Find the location where z_oder becomes non-negative. + ozone::mojom::WaylandOverlayConfigPtr value = + ozone::mojom::WaylandOverlayConfig::New(); + auto split = std::lower_bound(overlays.begin(), overlays.end(), value, + OverlayStackOrderCompare); + CHECK((*split)->z_order >= 0); + size_t num_primary_planes = (*split)->z_order == 0 ? 1 : 0; + + size_t above = (overlays.end() - split) - num_primary_planes; + size_t below = split - overlays.begin(); + // Re-arrange the list of subsurfaces to fit the |overlays|. Request extra + // subsurfaces if needed. + if (!ArrangeSubsurfaceStack(above, below)) + return false; + + { + // Iterate through |subsurface_stack_below_|, setup subsurfaces and place + // them in corresponding order. Commit wl_buffers once a subsurface is + // configured. + auto overlay_iter = split - 1; + for (auto iter = subsurface_stack_below_.begin(); + iter != subsurface_stack_below_.end(); ++iter, --overlay_iter) { + if (overlay_iter >= overlays.begin()) { + WaylandSurface* reference_above = nullptr; + if (overlay_iter == split - 1) { + // It's possible that |overlays| does not contain primary plane, we + // still want to place relative to the surface with z_order=0. + reference_above = root_surface(); + } else { + reference_above = (*std::next(iter))->wayland_surface(); + } + (*iter)->ConfigureAndShowSurface( + (*overlay_iter)->transform, (*overlay_iter)->bounds_rect, + (*overlay_iter)->enable_blend, nullptr, reference_above); + connection_->buffer_manager_host()->CommitBufferInternal( + (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, + gfx::Rect()); + } else { + // If there're more subsurfaces requested that we don't need at the + // moment, hide them. + (*iter)->Hide(); + } + } + + // Iterate through |subsurface_stack_above_|, setup subsurfaces and place + // them in corresponding order. Commit wl_buffers once a subsurface is + // configured. + overlay_iter = split + num_primary_planes; + for (auto iter = subsurface_stack_above_.begin(); + iter != subsurface_stack_above_.end(); ++iter, ++overlay_iter) { + if (overlay_iter < overlays.end()) { + WaylandSurface* reference_below = nullptr; + if (overlay_iter == split + num_primary_planes) { + // It's possible that |overlays| does not contain primary plane, we + // still want to place relative to the surface with z_order=0. + reference_below = root_surface(); + } else { + reference_below = (*std::prev(iter))->wayland_surface(); + } + (*iter)->ConfigureAndShowSurface( + (*overlay_iter)->transform, (*overlay_iter)->bounds_rect, + (*overlay_iter)->enable_blend, reference_below, nullptr); + connection_->buffer_manager_host()->CommitBufferInternal( + (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, + gfx::Rect()); + } else { + // If there're more subsurfaces requested that we don't need at the + // moment, hide them. + (*iter)->Hide(); + } + } } + + if (num_primary_planes) { + // TODO: forward fence. + connection_->buffer_manager_host()->CommitBufferInternal( + root_surface(), (*split)->buffer_id, (*split)->damage_region); + } else { + // Subsurfaces are set to desync, above operations will only take effects + // when root_surface is committed. + root_surface()->Commit(); + } + + // commit all; + return true; } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_window.h index 9c42eb59c80..cd9f1e9fb35 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.h @@ -5,6 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_H_ +#include <list> #include <memory> #include <set> #include <vector> @@ -18,6 +19,7 @@ #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/wayland_surface.h" +#include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom-forward.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -31,20 +33,22 @@ namespace ui { class BitmapCursorOzone; class OSExchangeData; class WaylandConnection; +class WaylandSubsurface; +class WaylandWindowDragController; + +using WidgetSubsurfaceSet = base::flat_set<std::unique_ptr<WaylandSubsurface>>; class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { public: ~WaylandWindow() override; // A factory method that can create any of the derived types of WaylandWindow - // (WaylandToplevelWindow, WaylandPopup and WaylandSubsurface). + // (WaylandToplevelWindow, WaylandPopup and WaylandAuxiliaryWindow). static std::unique_ptr<WaylandWindow> Create( PlatformWindowDelegate* delegate, WaylandConnection* connection, PlatformWindowInitProperties properties); - static WaylandWindow* FromSurface(wl_surface* surface); - void OnWindowLostCapture(); // Updates the surface buffer scale of the window. Top level windows take @@ -54,8 +58,10 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // to do so (this is not needed upon window initialization). void UpdateBufferScale(bool update_bounds); - WaylandSurface* wayland_surface() { return &wayland_surface_; } - wl_surface* surface() const { return wayland_surface_.surface(); } + WaylandSurface* root_surface() const { return root_surface_.get(); } + const WidgetSubsurfaceSet& wayland_subsurfaces() const { + return wayland_subsurfaces_; + } void set_parent_window(WaylandWindow* parent_window) { parent_window_ = parent_window; @@ -64,6 +70,16 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { gfx::AcceleratedWidget GetWidget() const; + // Creates a WaylandSubsurface to put into |wayland_subsurfaces_|. Called if + // more subsurfaces are needed when a frame arrives. + bool RequestSubsurface(); + // Re-arrange the |subsurface_stack_above_| and |subsurface_stack_below_| s.t. + // subsurface_stack_above_.size() >= above and + // subsurface_stack_below_.size() >= below. + bool ArrangeSubsurfaceStack(size_t above, size_t below); + bool CommitOverlays( + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr>& overlays); + // Set whether this window has pointer focus and should dispatch mouse events. void SetPointerFocus(bool focus); bool has_pointer_focus() const { return has_pointer_focus_; } @@ -82,7 +98,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { void set_child_window(WaylandWindow* window) { child_window_ = window; } WaylandWindow* child_window() const { return child_window_; } - int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); } + int32_t buffer_scale() const { return root_surface_->buffer_scale(); } int32_t ui_scale() const { return ui_scale_; } const base::flat_set<uint32_t>& entered_outputs_ids() const { @@ -145,8 +161,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { virtual void OnDragEnter(const gfx::PointF& point, std::unique_ptr<OSExchangeData> data, int operation); - virtual int OnDragMotion(const gfx::PointF& point, - int operation); + virtual int OnDragMotion(const gfx::PointF& point, int operation); virtual void OnDragDrop(std::unique_ptr<OSExchangeData> data); virtual void OnDragLeave(); virtual void OnDragSessionClose(uint32_t dnd_action); @@ -157,6 +172,17 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Returns a top most child window within the same hierarchy. WaylandWindow* GetTopMostChildWindow(); + // This should be called when a WaylandSurface part of this window becomes + // partially or fully within the scanout region of |output|. + void AddEnteredOutputId(struct wl_output* output); + + // This should be called when a WaylandSurface part of this window becomes + // fully outside of the scanout region of |output|. + void RemoveEnteredOutputId(struct wl_output* output); + + // Returns true iff this window is opaque. + bool IsOpaqueWindow() const; + protected: WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection); @@ -170,10 +196,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Gets a parent window for this window. WaylandWindow* GetParentWindow(gfx::AcceleratedWidget parent_widget); - // Sets the buffer scale. - void SetBufferScale(int32_t scale, bool update_bounds); - - // Sets the ui scale. void set_ui_scale(int32_t ui_scale) { ui_scale_ = ui_scale; } private: @@ -182,41 +204,38 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Initializes the WaylandWindow with supplied properties. bool Initialize(PlatformWindowInitProperties properties); - // Install a surface listener and start getting wl_output enter/leave events. - void AddSurfaceListener(); - - void AddEnteredOutputId(struct wl_output* output); - void RemoveEnteredOutputId(struct wl_output* output); - void UpdateCursorPositionFromEvent(std::unique_ptr<Event> event); WaylandWindow* GetTopLevelWindow(); - // It's important to set opaque region for opaque windows (provides - // optimization hint for the Wayland compositor). - void MaybeUpdateOpaqueRegion(); - - bool IsOpaqueWindow() const; - uint32_t DispatchEventToDelegate(const PlatformEvent& native_event); // Additional initialization of derived classes. virtual bool OnInitialize(PlatformWindowInitProperties properties) = 0; - // 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); + // WaylandWindowDragController might need to take ownership of the wayland + // surface whether the window that originated the DND session gets destroyed + // in the middle of that session (e.g: when it is snapped into a tab strip). + // Surface ownership is allowed to be taken only when the window is under + // destruction, i.e: |shutting_down_| is set. This can be done, for example, + // by implementing |WaylandWindowObserver::OnWindowRemoved|. + friend WaylandWindowDragController; + std::unique_ptr<WaylandSurface> TakeWaylandSurface(); PlatformWindowDelegate* delegate_; WaylandConnection* connection_; WaylandWindow* parent_window_ = nullptr; WaylandWindow* child_window_ = nullptr; - WaylandSurface wayland_surface_; + std::unique_ptr<WaylandSurface> root_surface_; + WidgetSubsurfaceSet wayland_subsurfaces_; + + // The stack of sub-surfaces to take effect when Commit() is called. + // |subsurface_stack_above_| refers to subsurfaces that are stacked above the + // parent. + // Subsurface at the front of the list is the closest to the parent. + std::list<WaylandSubsurface*> subsurface_stack_above_; + std::list<WaylandSubsurface*> subsurface_stack_below_; // The current cursor bitmap (immutable). scoped_refptr<BitmapCursorOzone> bitmap_; @@ -250,6 +269,12 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // The type of the current WaylandWindow object. ui::PlatformWindowType type_ = ui::PlatformWindowType::kWindow; + // Set when the window enters in shutdown process. + bool shutting_down_ = false; + + // AcceleratedWidget for this window. This will be unique even over time. + gfx::AcceleratedWidget accelerated_widget_; + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc index 08d5cb633ca..64ffd8b4845 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc @@ -12,9 +12,9 @@ #include "base/callback.h" #include "base/check.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop_current.h" #include "base/notreached.h" #include "base/run_loop.h" +#include "base/task/current_thread.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" @@ -64,19 +64,48 @@ WaylandWindowDragController::WaylandWindowDragController( WaylandWindowDragController::~WaylandWindowDragController() = default; -bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window, - const gfx::Vector2d& offset) { - DCHECK_LE(state_, State::kAttached); - DCHECK(window); +bool WaylandWindowDragController::StartDragSession() { + if (state_ != State::kIdle) + return true; - if (!OfferWindow()) + origin_window_ = window_manager_->GetCurrentFocusedWindow(); + if (!origin_window_) { + LOG(ERROR) << "Failed to get origin window."; return false; + } + + VLOG(1) << "Starting DND session."; + state_ = State::kAttached; + + DCHECK(!data_source_); + data_source_ = data_device_manager_->CreateSource(this); + data_source_->Offer({kMimeTypeChromiumWindow}); + data_source_->SetAction(DragDropTypes::DRAG_MOVE); + + // TODO(crbug.com/1099418): Use dragged window's surface as icon surface + // once "immediate drag" protocol extensions are available. + data_device_->StartDrag(*data_source_, *origin_window_, + /*icon_surface=*/nullptr, this); + + pointer_grab_owner_ = origin_window_; + + // Observe window so we can take ownership of the origin surface in case it + // is destroyed during the DND session. + window_manager_->AddObserver(this); + return true; +} +bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window, + const gfx::Vector2d& offset) { DCHECK_EQ(state_, State::kAttached); + DCHECK(window); dragged_window_ = window; drag_offset_ = offset; + RunLoop(); + dragged_window_ = nullptr; + DCHECK(state_ == State::kAttached || state_ == State::kDropped); bool dropped = state_ == State::kDropped; if (dropped) @@ -91,9 +120,11 @@ void WaylandWindowDragController::StopDragging() { VLOG(1) << "End drag loop requested. state=" << state_; // This function is supposed to be called to indicate that the window was just - // snapped into a tab strip. So switch to |kAttached| state and ask to quit - // the nested loop. + // snapped into a tab strip. So switch to |kAttached| state, store the focused + // window as the pointer grabber and ask to quit the nested loop. state_ = State::kAttached; + pointer_grab_owner_ = window_manager_->GetCurrentFocusedWindow(); + DCHECK(pointer_grab_owner_); QuitLoop(); } @@ -118,6 +149,8 @@ void WaylandWindowDragController::OnDragEnter(WaylandWindow* window, uint32_t serial) { DCHECK_GE(state_, State::kAttached); DCHECK(window); + DCHECK(data_source_); + DCHECK(data_offer_); // Forward focus change event to the input delegate, so other components, such // as WaylandScreen, are able to properly retrieve focus related info during @@ -126,8 +159,13 @@ void WaylandWindowDragController::OnDragEnter(WaylandWindow* window, VLOG(1) << "OnEnter. widget=" << window->GetWidget(); + // TODO(crbug.com/1102946): Exo does not support custom mime types. In this + // case, |data_offer_| will hold an empty mime_types list and, at this point, + // it's safe just to skip the offer checks and requests here. + if (data_offer_->mime_types().empty()) + return; + // Ensure this is a valid "window drag" offer. - DCHECK(data_offer_); DCHECK_EQ(data_offer_->mime_types().size(), 1u); DCHECK_EQ(data_offer_->mime_types().front(), kMimeTypeChromiumWindow); @@ -161,16 +199,39 @@ void WaylandWindowDragController::OnDragLeave() { // which would require hacky workarounds in HandleDropAndResetState function // to properly detect and handle such cases. - VLOG(1) << "OnLeave"; + if (!data_offer_) + return; + VLOG(1) << "OnLeave"; data_offer_.reset(); + + // As Wayland clients are only aware of surface-local coordinates and there is + // no implicit grab during DND sessions, a fake motion event with negative + // coordinates must be used here to make it possible for higher level UI + // components to detect when a window should be detached. E.g: On Chrome, + // dragging a tab all the way up to the top edge of the window won't work + // without this fake motion event upon wl_data_device::leave events. + if (state_ == State::kAttached) + pointer_delegate_->OnPointerMotionEvent({-1, -1}); } void WaylandWindowDragController::OnDragDrop() { - // Not used for window dragging sessions. Handling of drop events is fully - // done at OnDataSourceFinish function, i.e: wl_data_source::{cancel,finish}. + DCHECK_GE(state_, State::kAttached); + VLOG(1) << "Dropped. state=" << state_; + + // Some compositors, e.g: Exo, may delay the wl_data_source::cancelled event + // delivery for some seconds, when the drop happens within a toplevel surface. + // Such event is handled by OnDataSourceFinish() function below, which is the + // single entry point for the drop event in window drag controller. In order + // to prevent such delay, the current data offer must be destroyed here. + DCHECK(data_offer_); + data_offer_.reset(); } +// This function is called when either 'cancelled' or 'finished' data source +// events is received during a window dragging session. It is used to detect +// when drop happens, since it is the only event sent by the server regardless +// where it happens, inside or outside toplevel surfaces. void WaylandWindowDragController::OnDataSourceFinish(bool completed) { DCHECK_GE(state_, State::kAttached); DCHECK(data_source_); @@ -180,7 +241,8 @@ void WaylandWindowDragController::OnDataSourceFinish(bool completed) { // Release DND objects. data_offer_.reset(); data_source_.reset(); - icon_surface_.reset(); + origin_surface_.reset(); + origin_window_ = nullptr; dragged_window_ = nullptr; // Transition to |kDropped| state and determine the next action to take. If @@ -193,6 +255,7 @@ void WaylandWindowDragController::OnDataSourceFinish(bool completed) { HandleDropAndResetState(); data_device_->ResetDragDelegate(); + window_manager_->RemoveObserver(this); } void WaylandWindowDragController::OnDataSourceSend(const std::string& mime_type, @@ -208,7 +271,7 @@ bool WaylandWindowDragController::CanDispatchEvent(const PlatformEvent& event) { uint32_t WaylandWindowDragController::DispatchEvent( const PlatformEvent& event) { DCHECK_EQ(state_, State::kDetached); - DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(base::CurrentUIThread::IsSet()); VLOG(2) << "Dispatch. event=" << event->GetName(); @@ -219,30 +282,10 @@ uint32_t WaylandWindowDragController::DispatchEvent( return POST_DISPATCH_PERFORM_DEFAULT; } -bool WaylandWindowDragController::OfferWindow() { - DCHECK_LE(state_, State::kAttached); - - auto* window = window_manager_->GetCurrentFocusedWindow(); - if (!window) { - LOG(ERROR) << "Failed to get focused window."; - return false; - } - - if (!data_source_) - data_source_ = data_device_manager_->CreateSource(this); - - if (state_ == State::kIdle) { - DCHECK(!icon_surface_); - icon_surface_.reset( - wl_compositor_create_surface(connection_->compositor())); - - VLOG(1) << "Starting DND session."; - state_ = State::kAttached; - data_source_->Offer({kMimeTypeChromiumWindow}); - data_source_->SetAction(DragDropTypes::DRAG_MOVE); - data_device_->StartDrag(*data_source_, *window, icon_surface_.get(), this); - } - return true; +void WaylandWindowDragController::OnWindowRemoved(WaylandWindow* window) { + DCHECK_NE(state_, State::kIdle); + if (window == origin_window_) + origin_surface_ = origin_window_->TakeWaylandSurface(); } void WaylandWindowDragController::HandleMotionEvent(MouseEvent* event) { @@ -273,14 +316,15 @@ void WaylandWindowDragController::HandleMotionEvent(MouseEvent* event) { // about to finish. void WaylandWindowDragController::HandleDropAndResetState() { DCHECK_EQ(state_, State::kDropped); - DCHECK(window_manager_->GetCurrentFocusedWindow()); - VLOG(1) << "Notifying drop. window=" - << window_manager_->GetCurrentFocusedWindow(); + DCHECK(pointer_grab_owner_); + VLOG(1) << "Notifying drop. window=" << pointer_grab_owner_; EventFlags pointer_button = EF_LEFT_MOUSE_BUTTON; DCHECK(connection_->event_source()->IsPointerButtonPressed(pointer_button)); - pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button); + pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button, + pointer_grab_owner_); + pointer_grab_owner_ = nullptr; state_ = State::kIdle; } @@ -288,7 +332,8 @@ void WaylandWindowDragController::RunLoop() { DCHECK_EQ(state_, State::kAttached); DCHECK(dragged_window_); - VLOG(1) << "Starting drag loop. widget=" << dragged_window_->GetWidget(); + VLOG(1) << "Starting drag loop. widget=" << dragged_window_->GetWidget() + << " offset=" << drag_offset_.ToString(); // TODO(crbug.com/896640): Handle cursor auto old_dispatcher = std::move(nested_dispatcher_); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h index 071cc9672bc..fa3ed531119 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h @@ -21,6 +21,7 @@ #include "ui/ozone/platform/wayland/host/wayland_data_source.h" #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" +#include "ui/ozone/platform/wayland/host/wayland_window_observer.h" namespace ui { @@ -29,6 +30,7 @@ class WaylandDataDeviceManager; class WaylandDataOffer; class WaylandWindow; class WaylandWindowManager; +class WaylandSurface; // Drag controller implementation that drives window moving sessions (aka: tab // dragging). Wayland Drag and Drop protocol is used, under the hood, to keep @@ -37,7 +39,8 @@ class WaylandWindowManager; // TODO(crbug.com/896640): Use drag icon to emulate window moving. class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, public WaylandDataSource::Delegate, - public PlatformEventDispatcher { + public PlatformEventDispatcher, + public WaylandWindowObserver { public: // Constants used to keep track of the drag controller state. enum class State { @@ -55,7 +58,12 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, delete; ~WaylandWindowDragController() override; - bool Drag(WaylandToplevelWindow* surface, const gfx::Vector2d& offset); + // Starts a new Wayland DND session for window dragging, if not done yet. A + // new data source is setup and the focused window is used as the origin + // surface. + bool StartDragSession(); + + bool Drag(WaylandToplevelWindow* window, const gfx::Vector2d& offset); void StopDragging(); State state() const { return state_; } @@ -81,9 +89,9 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; - // Offers the focused window as available to be dragged. A new data source is - // setup and the underlying DnD session is started, if not done yet. - bool OfferWindow(); + // WaylandWindowObserver: + void OnWindowRemoved(WaylandWindow* window) override; + // Handles drag/move mouse |event|, while in |kDetached| mode, forwarding it // as a bounds change event to the upper layer handlers. void HandleMotionEvent(MouseEvent* event); @@ -103,12 +111,26 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, WaylandPointer::Delegate* const pointer_delegate_; State state_ = State::kIdle; - WaylandToplevelWindow* dragged_window_ = nullptr; gfx::Vector2d drag_offset_; std::unique_ptr<WaylandDataSource> data_source_; std::unique_ptr<WaylandDataOffer> data_offer_; - wl::Object<wl_surface> icon_surface_; + + // The current toplevel window being dragged, when in detached mode. + WaylandToplevelWindow* dragged_window_ = nullptr; + + // Keeps track of the window that holds the pointer grab. i.e: the owner of + // the surface that must receive the mouse release event upon drop. + WaylandWindow* pointer_grab_owner_ = nullptr; + + // The window where the DND session originated from. i.e: which had the + // pointer focus when the session was initiated. + WaylandWindow* origin_window_ = nullptr; + + // The |origin_window_| can be destroyed during the DND session. If this + // happens, |origin_surface_| takes ownership of its surface and ensure it + // is kept alive until the end of the session. + std::unique_ptr<WaylandSurface> origin_surface_; std::unique_ptr<ScopedEventDispatcher> nested_dispatcher_; base::OnceClosure quit_loop_closure_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc index 56a13b46e48..4f24aadffb4 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc @@ -21,7 +21,6 @@ #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_window_manager.h" -#include "ui/ozone/platform/wayland/test/constants.h" #include "ui/ozone/platform/wayland/test/mock_pointer.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/test_data_device.h" @@ -31,8 +30,9 @@ #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" #include "ui/ozone/test/mock_platform_window_delegate.h" +#include "ui/platform_window/extensions/wayland_extension.h" #include "ui/platform_window/platform_window_delegate.h" -#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h" +#include "ui/platform_window/wm/wm_move_loop_handler.h" using testing::_; using testing::Mock; @@ -109,7 +109,8 @@ class WaylandWindowDragControllerTest : public WaylandTest, void SendDndEnter(WaylandWindow* window) { EXPECT_TRUE(window); - OfferAndEnter(server_.GetObject<wl::MockSurface>(window->GetWidget())); + OfferAndEnter(server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId())); } void SendDndLeave() { @@ -124,7 +125,8 @@ class WaylandWindowDragControllerTest : public WaylandTest, void SendPointerEnter(WaylandWindow* window, MockPlatformWindowDelegate* delegate) { - auto* surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + auto* surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); wl_pointer_send_enter(pointer_->resource(), NextSerial(), surface->resource(), 0, 0); EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1); @@ -133,6 +135,18 @@ class WaylandWindowDragControllerTest : public WaylandTest, EXPECT_EQ(window, window_manager()->GetCurrentFocusedWindow()); } + void SendPointerLeave(WaylandWindow* window, + MockPlatformWindowDelegate* delegate) { + auto* surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); + wl_pointer_send_leave(pointer_->resource(), NextSerial(), + surface->resource()); + EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1); + Sync(); + + EXPECT_EQ(nullptr, window_manager()->GetCurrentFocusedWindow()); + } + void SendPointerPress(WaylandWindow* window, MockPlatformWindowDelegate* delegate, int button) { @@ -195,10 +209,14 @@ TEST_P(WaylandWindowDragControllerTest, DragInsideWindowAndDrop) { SendPointerPress(window_.get(), &delegate_, BTN_LEFT); SendPointerMotion(window_.get(), &delegate_, {10, 10}); - // Set up an "interaction flow" and RunMoveLoop: + // Set up an "interaction flow", start the drag session and run move loop: // - Event dispatching and bounds changes are monitored // - At each event, emulates a new event at server side and proceeds to the // next test step. + auto* wayland_extension = GetWaylandExtension(*window_); + wayland_extension->StartWindowDraggingSessionIfNeeded(); + EXPECT_EQ(State::kAttached, drag_controller()->state()); + auto* move_loop_handler = GetWmMoveLoopHandler(*window_); DCHECK(move_loop_handler); @@ -281,21 +299,20 @@ TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) { SendPointerEnter(window_.get(), &delegate_); SendPointerPress(window_.get(), &delegate_, BTN_LEFT); SendPointerMotion(window_.get(), &delegate_, {10, 10}); + Sync(); - // Sets up an "interaction flow" and RunMoveLoop: + // Sets up an "interaction flow", start the drag session and run move loop: // - Event dispatching and bounds changes are monitored // - At each event, emulates a new event on server side and proceeds to the // next test step. + auto* wayland_extension = GetWaylandExtension(*window_); + wayland_extension->StartWindowDraggingSessionIfNeeded(); + EXPECT_EQ(State::kAttached, drag_controller()->state()); + auto* move_loop_handler = GetWmMoveLoopHandler(*window_); DCHECK(move_loop_handler); - enum { - kStarted, - kDragging, - kExitedWindow, - kDropping, - kDone - } test_step = kStarted; + enum { kStarted, kDragging, kExitedDropping, kDone } test_step = kStarted; EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) { EXPECT_TRUE(event->IsMouseEvent()); @@ -310,13 +327,7 @@ TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) { SendDndMotion({20, 20}); test_step = kDragging; break; - case kExitedWindow: - EXPECT_EQ(ET_MOUSE_EXITED, event->type()); - // Release mouse button with no window foucsed. - SendDndDrop(); - test_step = kDropping; - break; - case kDropping: + case kExitedDropping: EXPECT_EQ(ET_MOUSE_RELEASED, event->type()); EXPECT_EQ(State::kDropped, drag_controller()->state()); // Ensure PlatformScreen keeps consistent. @@ -343,8 +354,9 @@ TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) { EXPECT_EQ(kDragging, test_step); EXPECT_EQ(gfx::Point(20, 20), bounds.origin()); + SendDndLeave(); SendDndDrop(); - test_step = kDropping; + test_step = kExitedDropping; }); // RunMoveLoop() blocks until the dragging sessions ends, so resume test @@ -394,10 +406,14 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) { SendPointerPress(source_window, &delegate_, BTN_LEFT); SendPointerMotion(source_window, &delegate_, {10, 10}); - // Sets up an "interaction flow" and RunMoveLoop: + // Sets up an "interaction flow", start the drag session and run move loop: // - Event dispatching and bounds changes are monitored // - At each event, emulates a new event on server side and proceeds to the // next test step. + auto* wayland_extension = GetWaylandExtension(*window_); + wayland_extension->StartWindowDraggingSessionIfNeeded(); + EXPECT_EQ(State::kAttached, drag_controller()->state()); + auto* move_loop_handler = GetWmMoveLoopHandler(*window_); DCHECK(move_loop_handler); @@ -479,20 +495,22 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) { EXPECT_EQ(target_window->GetWidget(), screen_->GetLocalProcessWidgetAtPoint({50, 50}, {})); + // Emulates a pointer::leave event being sent before data_source::cancelled, + // what happens with some compositors, e.g: Exosphere. Even in these cases, + // WaylandWindowDragController must guarantee the mouse button release event + // (aka: drop) is delivered to the upper layer listeners. + SendPointerLeave(target_window, &delegate_); + SendDndDrop(); - EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) { + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce([&](Event* event) { EXPECT_TRUE(event->IsMouseEvent()); switch (test_step) { case kSnapped: EXPECT_EQ(ET_MOUSE_RELEASED, event->type()); EXPECT_EQ(State::kDropped, drag_controller()->state()); + EXPECT_EQ(target_window, window_manager()->GetCurrentFocusedWindow()); test_step = kDone; break; - case kDone: - EXPECT_EQ(ET_MOUSE_EXITED, event->type()); - EXPECT_EQ(target_window->GetWidget(), - screen_->GetLocalProcessWidgetAtPoint({30, 42}, {})); - break; default: FAIL() << " event=" << event->GetName() << " state=" << drag_controller()->state() @@ -508,6 +526,50 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) { screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); } +// Verifies wl_data_device::leave events are properly handled and propagated +// while in window dragging "attached" mode. +TEST_P(WaylandWindowDragControllerTest, DragExitAttached) { + // Ensure there is no window currently focused + EXPECT_FALSE(window_manager()->GetCurrentFocusedWindow()); + EXPECT_EQ(gfx::kNullAcceleratedWidget, + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + + SendPointerEnter(window_.get(), &delegate_); + SendPointerPress(window_.get(), &delegate_, BTN_LEFT); + SendPointerMotion(window_.get(), &delegate_, {10, 10}); + Sync(); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + + auto* wayland_extension = GetWaylandExtension(*window_); + wayland_extension->StartWindowDraggingSessionIfNeeded(); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(1); + Sync(); + Sync(); + EXPECT_EQ(State::kAttached, drag_controller()->state()); + + // Emulate a wl_data_device::leave and make sure a motion event is dispatched + // in response. + SendDndLeave(); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce([&](Event* event) { + EXPECT_EQ(ET_MOUSE_DRAGGED, event->type()); + EXPECT_EQ(gfx::Point(-1, -1).ToString(), + event->AsMouseEvent()->location().ToString()); + }); + Sync(); + + SendDndDrop(); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(1); + Sync(); + + SendPointerEnter(window_.get(), &delegate_); + Sync(); + + EXPECT_EQ(window_.get(), window_manager()->GetCurrentFocusedWindow()); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandWindowDragControllerTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc index 29d70dbdc39..7854b05b999 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc @@ -5,9 +5,9 @@ #include <memory> #include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/host/wayland_auxiliary_window.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_popup.h" -#include "ui/ozone/platform/wayland/host/wayland_subsurface.h" #include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -30,13 +30,13 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create( } else if (connection->IsDragInProgress()) { // We are in the process of drag and requested a popup. Most probably, // it is an arrow window. - window.reset(new WaylandSubsurface(delegate, connection)); + window.reset(new WaylandAuxiliaryWindow(delegate, connection)); } else { window.reset(new WaylandPopup(delegate, connection)); } break; case PlatformWindowType::kTooltip: - window.reset(new WaylandSubsurface(delegate, connection)); + window.reset(new WaylandAuxiliaryWindow(delegate, connection)); break; case PlatformWindowType::kWindow: case PlatformWindowType::kBubble: diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc index 51f07bbc27a..0c9b86e81fc 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc @@ -110,6 +110,28 @@ void WaylandWindowManager::RemoveWindow(gfx::AcceleratedWidget widget) { observer.OnWindowRemoved(window); } +void WaylandWindowManager::AddSubsurface(gfx::AcceleratedWidget widget, + WaylandSubsurface* subsurface) { + auto* window = window_map_[widget]; + DCHECK(window); + + for (WaylandWindowObserver& observer : observers_) + observer.OnSubsurfaceAdded(window, subsurface); +} + +void WaylandWindowManager::RemoveSubsurface(gfx::AcceleratedWidget widget, + WaylandSubsurface* subsurface) { + auto* window = window_map_[widget]; + DCHECK(window); + + for (WaylandWindowObserver& observer : observers_) + observer.OnSubsurfaceRemoved(window, subsurface); +} + +gfx::AcceleratedWidget WaylandWindowManager::AllocateAcceleratedWidget() { + return ++last_accelerated_widget_; +} + std::vector<WaylandWindow*> WaylandWindowManager::GetAllWindows() const { std::vector<WaylandWindow*> result; for (auto entry : window_map_) diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h index 1f4cad045ca..ca32c24833f 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h @@ -16,6 +16,7 @@ namespace ui { class WaylandWindow; +class WaylandSubsurface; // Stores and returns WaylandWindows. Clients that are interested in knowing // when a new window is added or removed, but set self as an observer. @@ -63,6 +64,13 @@ class WaylandWindowManager { void AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window); void RemoveWindow(gfx::AcceleratedWidget widget); + void AddSubsurface(gfx::AcceleratedWidget widget, + WaylandSubsurface* subsurface); + void RemoveSubsurface(gfx::AcceleratedWidget widget, + WaylandSubsurface* subsurface); + + // Creates a new unique gfx::AcceleratedWidget. + gfx::AcceleratedWidget AllocateAcceleratedWidget(); private: base::ObserverList<WaylandWindowObserver> observers_; @@ -71,6 +79,10 @@ class WaylandWindowManager { WaylandWindow* located_events_grabber_ = nullptr; + // Stores strictly monotonically increasing counter for allocating unique + // AccelerateWidgets. + gfx::AcceleratedWidget last_accelerated_widget_ = gfx::kNullAcceleratedWidget; + DISALLOW_COPY_AND_ASSIGN(WaylandWindowManager); }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc index 73b6d2c187e..8237298cde9 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc @@ -100,8 +100,8 @@ TEST_P(WaylandWindowManagerTest, GetCurrentFocusedWindow) { auto* pointer = server_.seat()->pointer(); ASSERT_TRUE(pointer); - wl::MockSurface* surface = - server_.GetObject<wl::MockSurface>(window1->GetWidget()); + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window1->root_surface()->GetSurfaceId()); wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); Sync(); @@ -137,8 +137,8 @@ TEST_P(WaylandWindowManagerTest, GetCurrentKeyboardFocusedWindow) { auto* keyboard = server_.seat()->keyboard(); ASSERT_TRUE(keyboard); - wl::MockSurface* surface = - server_.GetObject<wl::MockSurface>(window1->GetWidget()); + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window1->root_surface()->GetSurfaceId()); struct wl_array empty; wl_array_init(&empty); @@ -159,7 +159,15 @@ TEST_P(WaylandWindowManagerTest, GetCurrentKeyboardFocusedWindow) { TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) { MockPlatformWindowDelegate delegate; + // Create a second wl_output. wl::TestOutput* output = server_.CreateAndInitializeOutput(); + Sync(); + + // Place it at the right of the first output. + int x = server_.output()->GetRect().x(); + output->SetRect({x, 0, 800, 600}); + output->Flush(); + Sync(); auto window1 = CreateWaylandWindowWithParams(PlatformWindowType::kWindow, kDefaultBounds, &delegate); @@ -169,8 +177,8 @@ TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) { Sync(); - wl::MockSurface* surface = - server_.GetObject<wl::MockSurface>(window1->GetWidget()); + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window1->root_surface()->GetSurfaceId()); ASSERT_TRUE(surface); wl_surface_send_enter(surface->resource(), output->resource()); @@ -186,7 +194,8 @@ TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) { EXPECT_TRUE(window1.get() == *windows_on_output.begin()); - surface = server_.GetObject<wl::MockSurface>(window2->GetWidget()); + surface = server_.GetObject<wl::MockSurface>( + window2->root_surface()->GetSurfaceId()); ASSERT_TRUE(surface); wl_surface_send_enter(surface->resource(), output->resource()); @@ -194,6 +203,9 @@ TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) { windows_on_output = manager_->GetWindowsOnOutput(output_id); EXPECT_EQ(2u, windows_on_output.size()); + + output->DestroyGlobal(); + Sync(); } TEST_P(WaylandWindowManagerTest, GetAllWindows) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc index e289ea88e3d..5ce43f062f3 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc @@ -14,4 +14,11 @@ void WaylandWindowObserver::OnWindowRemoved(WaylandWindow* window) {} void WaylandWindowObserver::OnWindowConfigured(WaylandWindow* window) {} +void WaylandWindowObserver::OnSubsurfaceAdded(WaylandWindow* window, + WaylandSubsurface* subsurface) {} + +void WaylandWindowObserver::OnSubsurfaceRemoved(WaylandWindow* window, + WaylandSubsurface* subsurface) { +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h index c461b03114e..2434eb779a4 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h @@ -10,6 +10,7 @@ namespace ui { class WaylandWindow; +class WaylandSubsurface; // Observers for window management notifications. class WaylandWindowObserver : public base::CheckedObserver { @@ -23,6 +24,14 @@ class WaylandWindowObserver : public base::CheckedObserver { // Called when |window| has been ack configured. virtual void OnWindowConfigured(WaylandWindow* window); + // Called when |window| adds |subsurface|. + virtual void OnSubsurfaceAdded(WaylandWindow* window, + WaylandSubsurface* subsurface); + + // Called when |window| removes |subsurface|. + virtual void OnSubsurfaceRemoved(WaylandWindow* window, + WaylandSubsurface* subsurface); + protected: ~WaylandWindowObserver() override; }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index d7c533edc09..2626222a427 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc @@ -19,7 +19,9 @@ #include "ui/base/hit_test.h" #include "ui/events/base_event_utils.h" #include "ui/events/event.h" +#include "ui/gfx/overlay_transform.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" +#include "ui/ozone/platform/wayland/host/wayland_subsurface.h" #include "ui/ozone/platform/wayland/test/mock_pointer.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/test_keyboard.h" @@ -28,8 +30,8 @@ #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" #include "ui/ozone/test/mock_platform_window_delegate.h" -#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" #include "ui/platform_window/platform_window_init_properties.h" +#include "ui/platform_window/wm/wm_move_resize_handler.h" using ::testing::_; using ::testing::Eq; @@ -98,9 +100,9 @@ class WaylandWindowTest : public WaylandTest { } protected: - void SendConfigureEventPopup(gfx::AcceleratedWidget menu_widget, + void SendConfigureEventPopup(WaylandWindow* menu_window, const gfx::Rect bounds) { - auto* popup = GetPopupByWidget(menu_widget); + auto* popup = GetPopupByWindow(menu_window); ASSERT_TRUE(popup); if (GetParam() == kXdgShellV6) { zxdg_popup_v6_send_configure(popup->resource(), bounds.x(), bounds.y(), @@ -164,9 +166,9 @@ class WaylandWindowTest : public WaylandTest { Mock::VerifyAndClearExpectations(&delegate_); } - void VerifyXdgPopupPosition(gfx::AcceleratedWidget menu_widget, + void VerifyXdgPopupPosition(WaylandWindow* menu_window, const PopupPosition& position) { - auto* popup = GetPopupByWidget(menu_widget); + auto* popup = GetPopupByWindow(menu_window); ASSERT_TRUE(popup); EXPECT_EQ(popup->anchor_rect(), position.anchor_rect); @@ -207,8 +209,9 @@ class WaylandWindowTest : public WaylandTest { EXPECT_FALSE(window->CanDispatchEvent(&test_key_event)); } - wl::TestXdgPopup* GetPopupByWidget(gfx::AcceleratedWidget widget) { - wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(widget); + wl::TestXdgPopup* GetPopupByWindow(WaylandWindow* window) { + wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); if (mock_surface) { auto* mock_xdg_surface = mock_surface->xdg_surface(); if (mock_xdg_surface) @@ -384,7 +387,8 @@ TEST_P(WaylandWindowTest, StartWithFullscreen) { // The state must not be changed to the fullscreen before the surface is // activated. - auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); EXPECT_FALSE(mock_surface->xdg_surface()); EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0); window->ToggleFullscreen(); @@ -428,7 +432,8 @@ TEST_P(WaylandWindowTest, StartMaximized) { // The state must not be changed to the fullscreen before the surface is // activated. - auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); EXPECT_FALSE(mock_surface->xdg_surface()); EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); @@ -1196,8 +1201,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) { uint32_t serial = 0; // Test that CanDispatchEvent is set correctly. - wl::MockSurface* toplevel_surface = - server_.GetObject<wl::MockSurface>(widget_); + wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); wl_pointer_send_enter(server_.seat()->pointer()->resource(), ++serial, toplevel_surface->resource(), 0, 0); @@ -1240,8 +1245,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) { VerifyCanDispatchKeyEvents({window_.get()}, {menu_window.get(), nested_menu_window.get()}); - wl::MockSurface* menu_window_surface = - server_.GetObject<wl::MockSurface>(menu_window->GetWidget()); + wl::MockSurface* menu_window_surface = server_.GetObject<wl::MockSurface>( + menu_window->root_surface()->GetSurfaceId()); wl_pointer_send_leave(server_.seat()->pointer()->resource(), ++serial, toplevel_surface->resource()); @@ -1263,7 +1268,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) { {}, {window_.get(), menu_window.get(), nested_menu_window.get()}); wl::MockSurface* nested_menu_window_surface = - server_.GetObject<wl::MockSurface>(nested_menu_window->GetWidget()); + server_.GetObject<wl::MockSurface>( + nested_menu_window->root_surface()->GetSurfaceId()); wl_pointer_send_leave(server_.seat()->pointer()->resource(), ++serial, menu_window_surface->resource()); @@ -1351,11 +1357,10 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { Sync(); - gfx::AcceleratedWidget menu_window_widget = menu_window->GetWidget(); - VerifyXdgPopupPosition(menu_window_widget, menu_window_positioner); + VerifyXdgPopupPosition(menu_window.get(), menu_window_positioner); EXPECT_CALL(menu_window_delegate, OnBoundsChanged(_)).Times(0); - SendConfigureEventPopup(menu_window_widget, menu_window_bounds); + SendConfigureEventPopup(menu_window.get(), menu_window_bounds); Sync(); @@ -1367,15 +1372,13 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { nested_menu_window_positioner.size); std::unique_ptr<WaylandWindow> nested_menu_window = CreateWaylandWindowWithParams( - PlatformWindowType::kMenu, menu_window_widget, + PlatformWindowType::kMenu, menu_window->GetWidget(), nested_menu_window_bounds, &nested_menu_window_delegate); EXPECT_TRUE(nested_menu_window); Sync(); - gfx::AcceleratedWidget nested_menu_window_widget = - nested_menu_window->GetWidget(); - VerifyXdgPopupPosition(nested_menu_window_widget, + VerifyXdgPopupPosition(nested_menu_window.get(), nested_menu_window_positioner); EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0); @@ -1384,7 +1387,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { nested_menu_window_positioner.anchor_rect.y()); gfx::Rect calculated_nested_bounds = nested_menu_window_bounds; calculated_nested_bounds.set_origin(origin); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1402,7 +1405,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { nested_menu_window_delegate, OnBoundsChanged(gfx::Rect({139, 156}, nested_menu_window_bounds.size()))); calculated_nested_bounds.set_origin({-301, 80}); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1417,7 +1420,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { gfx::Rect({0, 363}, window_->GetBounds().size()))); EXPECT_CALL(menu_window_delegate, OnBoundsChanged(gfx::Rect({440, 0}, menu_window_bounds.size()))); - SendConfigureEventPopup(menu_window_widget, + SendConfigureEventPopup(menu_window.get(), gfx::Rect({440, -363}, menu_window_bounds.size())); Sync(); @@ -1428,23 +1431,22 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { nested_menu_window.reset(); nested_menu_window_bounds.set_origin({723, 258}); nested_menu_window = CreateWaylandWindowWithParams( - PlatformWindowType::kMenu, menu_window_widget, nested_menu_window_bounds, - &nested_menu_window_delegate); + PlatformWindowType::kMenu, menu_window->GetWidget(), + nested_menu_window_bounds, &nested_menu_window_delegate); EXPECT_TRUE(nested_menu_window); Sync(); - nested_menu_window_widget = nested_menu_window->GetWidget(); // We must get the anchor on gfx::Point(4, 258). nested_menu_window_positioner.anchor_rect.set_origin({4, 258}); - VerifyXdgPopupPosition(nested_menu_window_widget, + VerifyXdgPopupPosition(nested_menu_window.get(), nested_menu_window_positioner); Sync(); EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0); calculated_nested_bounds.set_origin({283, 258}); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1459,7 +1461,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { nested_menu_window_delegate, OnBoundsChanged(gfx::Rect({149, 258}, nested_menu_window_bounds.size()))); calculated_nested_bounds.set_origin({-291, 258}); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1472,7 +1474,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { OnBoundsChanged(gfx::Rect({0, 0}, window_->GetBounds().size()))); EXPECT_CALL(menu_window_delegate, OnBoundsChanged(gfx::Rect({440, 76}, menu_window_bounds.size()))); - SendConfigureEventPopup(menu_window_widget, + SendConfigureEventPopup(menu_window.get(), gfx::Rect({440, 76}, menu_window_bounds.size())); Sync(); @@ -1486,8 +1488,8 @@ ACTION_P(VerifyRegion, ptr) { } TEST_P(WaylandWindowTest, SetOpaqueRegion) { - wl::MockSurface* mock_surface = - server_.GetObject<wl::MockSurface>(window_->GetWidget()); + wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); gfx::Rect new_bounds(0, 0, 500, 600); auto state_array = MakeStateArray({XDG_TOPLEVEL_STATE_ACTIVATED}); @@ -1525,7 +1527,7 @@ TEST_P(WaylandWindowTest, OnCloseRequest) { Sync(); } -TEST_P(WaylandWindowTest, SubsurfaceSimpleParent) { +TEST_P(WaylandWindowTest, AuxiliaryWindowSimpleParent) { VerifyAndClearExpectations(); std::unique_ptr<WaylandWindow> second_window = CreateWaylandWindowWithParams( @@ -1536,60 +1538,64 @@ TEST_P(WaylandWindowTest, SubsurfaceSimpleParent) { // Test case 1: if the subsurface is provided with a parent widget, it must // always use that as a parent. gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10)); - std::unique_ptr<WaylandWindow> subsuface_window = + std::unique_ptr<WaylandWindow> auxiliary_window = CreateWaylandWindowWithParams(PlatformWindowType::kTooltip, window_->GetWidget(), subsurface_bounds, &delegate_); - EXPECT_TRUE(subsuface_window); + EXPECT_TRUE(auxiliary_window); // The subsurface mustn't take the focused window as a parent, but use the // provided one. second_window->SetPointerFocus(true); - subsuface_window->Show(false); + auxiliary_window->Show(false); Sync(); - auto* mock_surface_subsurface = - server_.GetObject<wl::MockSurface>(subsuface_window->GetWidget()); + auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>( + auxiliary_window->root_surface()->GetSurfaceId()); auto* test_subsurface = mock_surface_subsurface->sub_surface(); EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin()); EXPECT_FALSE(test_subsurface->sync()); auto* parent_resource = - server_.GetObject<wl::MockSurface>(window_->GetWidget())->resource(); + server_ + .GetObject<wl::MockSurface>(window_->root_surface()->GetSurfaceId()) + ->resource(); EXPECT_EQ(parent_resource, test_subsurface->parent_resource()); // Test case 2: the subsurface must use the focused window as its parent. - subsuface_window = CreateWaylandWindowWithParams( + auxiliary_window = CreateWaylandWindowWithParams( PlatformWindowType::kTooltip, gfx::kNullAcceleratedWidget, subsurface_bounds, &delegate_); - EXPECT_TRUE(subsuface_window); + EXPECT_TRUE(auxiliary_window); // The tooltip must take the focused window. second_window->SetPointerFocus(true); - subsuface_window->Show(false); + auxiliary_window->Show(false); Sync(); - // Get new surface after recreating the WaylandSubsurface. - mock_surface_subsurface = - server_.GetObject<wl::MockSurface>(subsuface_window->GetWidget()); + // Get new surface after recreating the WaylandAuxiliaryWindow. + mock_surface_subsurface = server_.GetObject<wl::MockSurface>( + auxiliary_window->root_surface()->GetSurfaceId()); test_subsurface = mock_surface_subsurface->sub_surface(); auto* second_parent_resource = - server_.GetObject<wl::MockSurface>(second_window->GetWidget()) + server_ + .GetObject<wl::MockSurface>( + second_window->root_surface()->GetSurfaceId()) ->resource(); EXPECT_EQ(second_parent_resource, test_subsurface->parent_resource()); - subsuface_window->Hide(); + auxiliary_window->Hide(); Sync(); // The subsurface must take the focused window. second_window->SetPointerFocus(false); window_->SetPointerFocus(true); - subsuface_window->Show(false); + auxiliary_window->Show(false); Sync(); @@ -1616,27 +1622,26 @@ TEST_P(WaylandWindowTest, NestedPopupMenu) { VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 40, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width(); EXPECT_EQ(4, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } // Case 2: When the menu bounds are positive and there is a negative or @@ -1653,27 +1658,26 @@ TEST_P(WaylandWindowTest, NestedPopupMenu1) { VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 10, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width(); EXPECT_EQ(1, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } // Case 3: When the menu bounds are negative and there is a positive, @@ -1690,27 +1694,26 @@ TEST_P(WaylandWindowTest, NestedPopupMenu2) { VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width(); EXPECT_EQ(8, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } // Case 4: When the menu bounds are negative and there is a negative, @@ -1727,20 +1730,19 @@ TEST_P(WaylandWindowTest, NestedPopupMenu3) { VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); @@ -1748,10 +1750,10 @@ TEST_P(WaylandWindowTest, NestedPopupMenu3) { EXPECT_EQ(1, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } -TEST_P(WaylandWindowTest, SubsurfaceNestedParent) { +TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) { VerifyAndClearExpectations(); gfx::Rect menu_window_bounds(gfx::Point(10, 10), gfx::Size(100, 100)); @@ -1763,22 +1765,22 @@ TEST_P(WaylandWindowTest, SubsurfaceNestedParent) { VerifyAndClearExpectations(); gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10)); - std::unique_ptr<WaylandWindow> subsuface_window = + std::unique_ptr<WaylandWindow> auxiliary_window = CreateWaylandWindowWithParams(PlatformWindowType::kTooltip, menu_window->GetWidget(), subsurface_bounds, &delegate_); - EXPECT_TRUE(subsuface_window); + EXPECT_TRUE(auxiliary_window); VerifyAndClearExpectations(); menu_window->SetPointerFocus(true); - subsuface_window->Show(false); + auxiliary_window->Show(false); Sync(); - auto* mock_surface_subsurface = - server_.GetObject<wl::MockSurface>(subsuface_window->GetWidget()); + auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>( + auxiliary_window->root_surface()->GetSurfaceId()); auto* test_subsurface = mock_surface_subsurface->sub_surface(); auto new_origin = subsurface_bounds.origin() - @@ -1825,7 +1827,8 @@ TEST_P(WaylandWindowTest, DestroysCreatesSurfaceOnHideShow) { Sync(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); EXPECT_TRUE(mock_surface->xdg_surface()); EXPECT_TRUE(mock_surface->xdg_surface()->xdg_toplevel()); @@ -1854,7 +1857,8 @@ TEST_P(WaylandWindowTest, DestroysCreatesPopupsOnHideShow) { Sync(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); EXPECT_TRUE(mock_surface->xdg_surface()); EXPECT_TRUE(mock_surface->xdg_surface()->xdg_popup()); @@ -1893,7 +1897,8 @@ TEST_P(WaylandWindowTest, SetsPropertiesOnShow) { Sync(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); auto* mock_xdg_toplevel = mock_surface->xdg_surface()->xdg_toplevel(); // Only app id must be set now. @@ -1952,8 +1957,8 @@ TEST_P(WaylandWindowTest, CreatesPopupOnButtonPressSerial) { constexpr uint32_t button_press_serial = 2; constexpr uint32_t button_release_serial = 3; - wl::MockSurface* toplevel_surface = - server_.GetObject<wl::MockSurface>(window_->GetWidget()); + wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); struct wl_array empty; wl_array_init(&empty); wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial, @@ -1977,7 +1982,7 @@ TEST_P(WaylandWindowTest, CreatesPopupOnButtonPressSerial) { Sync(); - auto* test_popup = GetPopupByWidget(popup->GetWidget()); + auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); EXPECT_NE(test_popup->grab_serial(), button_release_serial); EXPECT_EQ(test_popup->grab_serial(), button_press_serial); @@ -1996,8 +2001,8 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) { constexpr uint32_t touch_down_serial = 2; constexpr uint32_t touch_up_serial = 3; - wl::MockSurface* toplevel_surface = - server_.GetObject<wl::MockSurface>(window_->GetWidget()); + wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); struct wl_array empty; wl_array_init(&empty); wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial, @@ -2021,7 +2026,7 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) { Sync(); - auto* test_popup = GetPopupByWidget(popup->GetWidget()); + auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); EXPECT_NE(test_popup->grab_serial(), touch_up_serial); EXPECT_EQ(test_popup->grab_serial(), touch_down_serial); @@ -2075,11 +2080,48 @@ TEST_P(WaylandWindowTest, DoesNotGrabPopupIfNoSeat) { Sync(); - auto* test_popup = GetPopupByWidget(popup->GetWidget()); + auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); EXPECT_EQ(test_popup->grab_serial(), 0u); } +TEST_P(WaylandWindowTest, OneWaylandSubsurface) { + VerifyAndClearExpectations(); + + std::unique_ptr<WaylandWindow> window = CreateWaylandWindowWithParams( + PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget, + gfx::Rect(0, 0, 640, 480), &delegate_); + EXPECT_TRUE(window); + + gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10)); + bool result = window->RequestSubsurface(); + EXPECT_TRUE(result); + + WaylandSubsurface* wayland_subsurface = + window->wayland_subsurfaces().begin()->get(); + + Sync(); + + auto* mock_surface_root_window = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); + auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>( + wayland_subsurface->wayland_surface()->GetSurfaceId()); + EXPECT_TRUE(mock_surface_subsurface); + wayland_subsurface->ConfigureAndShowSurface( + gfx::OVERLAY_TRANSFORM_NONE, subsurface_bounds, true, nullptr, nullptr); + connection_->ScheduleFlush(); + + Sync(); + + auto* test_subsurface = mock_surface_subsurface->sub_surface(); + EXPECT_TRUE(test_subsurface); + auto* parent_resource = mock_surface_root_window->resource(); + EXPECT_EQ(parent_resource, test_subsurface->parent_resource()); + + EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin()); + EXPECT_TRUE(test_subsurface->sync()); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandWindowTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc index 758ae1f222e..99eb2e7012a 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc +++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc @@ -329,7 +329,7 @@ bool XDGPopupWrapperImpl::InitializeStable( } xdg_popup_add_listener(xdg_popup_.get(), &xdg_popup_listener, this); - wl_surface_commit(wayland_window_->surface()); + wayland_window_->root_surface()->Commit(); return true; } @@ -393,7 +393,7 @@ bool XDGPopupWrapperImpl::InitializeV6( zxdg_popup_v6_add_listener(zxdg_popup_v6_.get(), &zxdg_popup_v6_listener, this); - wl_surface_commit(wayland_window_->surface()); + wayland_window_->root_surface()->Commit(); return true; } diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc index ae1ca159fd4..f1a03831363 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc +++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc @@ -4,6 +4,7 @@ #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" +#include <aura-shell-client-protocol.h> #include <xdg-shell-client-protocol.h> #include <xdg-shell-unstable-v6-client-protocol.h> @@ -267,8 +268,8 @@ bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) { // configuration acknowledgement on each configure event. surface_for_popup_ = !with_toplevel; - xdg_surface_.reset(xdg_wm_base_get_xdg_surface(connection_->shell(), - wayland_window_->surface())); + xdg_surface_.reset(xdg_wm_base_get_xdg_surface( + connection_->shell(), wayland_window_->root_surface()->surface())); if (!xdg_surface_) { LOG(ERROR) << "Failed to create xdg_surface"; return false; @@ -287,8 +288,7 @@ bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) { return false; } xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, this); - wl_surface_commit(wayland_window_->surface()); - + wayland_window_->root_surface()->Commit(); connection_->ScheduleFlush(); return true; } @@ -307,7 +307,7 @@ bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) { surface_for_popup_ = !with_toplevel; zxdg_surface_v6_.reset(zxdg_shell_v6_get_xdg_surface( - connection_->shell_v6(), wayland_window_->surface())); + connection_->shell_v6(), wayland_window_->root_surface()->surface())); if (!zxdg_surface_v6_) { LOG(ERROR) << "Failed to create zxdg_surface"; return false; @@ -328,8 +328,15 @@ bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) { } zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(), &zxdg_toplevel_v6_listener, this); - wl_surface_commit(wayland_window_->surface()); + if (connection_->aura_shell()) { + aura_surface_.reset(zaura_shell_get_aura_surface( + connection_->aura_shell(), wayland_window_->root_surface()->surface())); + zaura_surface_set_fullscreen_mode(aura_surface_.get(), + ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE); + } + + wayland_window_->root_surface()->Commit(); connection_->ScheduleFlush(); return true; } diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h index ea1e81b44dc..2381aecfd9c 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h +++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h @@ -87,6 +87,7 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { wl::Object<zxdg_toplevel_v6> zxdg_toplevel_v6_; wl::Object<struct xdg_surface> xdg_surface_; wl::Object<xdg_toplevel> xdg_toplevel_; + wl::Object<zaura_surface> aura_surface_; bool surface_for_popup_ = false; diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc b/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc index 7b72165b4ff..b1c021ad330 100644 --- a/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc +++ b/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc @@ -57,7 +57,7 @@ void ZWPTextInputWrapperV1::Reset() { void ZWPTextInputWrapperV1::Activate(WaylandWindow* window) { zwp_text_input_v1_activate(obj_.get(), connection_->seat(), - window->surface()); + window->root_surface()->surface()); } void ZWPTextInputWrapperV1::Deactivate() { |