diff options
Diffstat (limited to 'chromium/ui/ozone')
175 files changed, 5202 insertions, 3440 deletions
diff --git a/chromium/ui/ozone/BUILD.gn b/chromium/ui/ozone/BUILD.gn index a3e07b47d27..e3eb1e17596 100644 --- a/chromium/ui/ozone/BUILD.gn +++ b/chromium/ui/ozone/BUILD.gn @@ -66,8 +66,6 @@ constructor_list_cc_file = "$target_gen_dir/constructor_list.cc" jumbo_component("ozone_base") { sources = [ - "public/cursor_factory_ozone.cc", - "public/cursor_factory_ozone.h", "public/gl_ozone.h", "public/gpu_platform_support_host.cc", "public/gpu_platform_support_host.h", @@ -105,7 +103,6 @@ jumbo_component("ozone_base") { "//ipc", "//skia", "//ui/base/clipboard:clipboard_types", - "//ui/base/cursor/mojom:cursor_type", "//ui/display", "//ui/display/types", "//ui/display/util", @@ -125,11 +122,13 @@ jumbo_component("ozone_base") { public_deps += [ "//gpu/vulkan" ] } + if (is_fuchsia) { + public_deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.images" ] + } + visibility += [ # Everyone should depend on //ui/ozone instead except a handful of # things that would otherwise create a cycle. - "//ui/base", - "//ui/base/cursor", "//ui/events/ozone/*", "//ui/ozone/common/*", "//ui/ozone/public/mojom", diff --git a/chromium/ui/ozone/demo/gl_renderer.cc b/chromium/ui/ozone/demo/gl_renderer.cc index c5812189846..c25b8f9c15d 100644 --- a/chromium/ui/ozone/demo/gl_renderer.cc +++ b/chromium/ui/ozone/demo/gl_renderer.cc @@ -46,7 +46,7 @@ bool GlRenderer::Initialize() { gl_surface_->Resize(size_, 1.f, gfx::ColorSpace(), true); // Schedule the initial render. - PostRenderFrameTask(gfx::SwapResult::SWAP_ACK, nullptr); + PostRenderFrameTask(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK)); return true; } @@ -69,16 +69,14 @@ void GlRenderer::RenderFrame() { weak_ptr_factory_.GetWeakPtr())); } else { PostRenderFrameTask( - gl_surface_->SwapBuffers(base::BindOnce( - &GlRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())), - nullptr); + gfx::SwapCompletionResult(gl_surface_->SwapBuffers(base::BindOnce( + &GlRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())))); } } -void GlRenderer::PostRenderFrameTask(gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - if (gpu_fence) - gpu_fence->Wait(); +void GlRenderer::PostRenderFrameTask(gfx::SwapCompletionResult result) { + if (result.gpu_fence) + result.gpu_fence->Wait(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, diff --git a/chromium/ui/ozone/demo/gl_renderer.h b/chromium/ui/ozone/demo/gl_renderer.h index c5a6a3b2ada..62d391b0f1d 100644 --- a/chromium/ui/ozone/demo/gl_renderer.h +++ b/chromium/ui/ozone/demo/gl_renderer.h @@ -14,7 +14,6 @@ #include "ui/ozone/demo/renderer_base.h" namespace gfx { -class GpuFence; struct PresentationFeedback; } // namespace gfx @@ -39,8 +38,7 @@ class GlRenderer : public RendererBase { private: void RenderFrame(); - void PostRenderFrameTask(gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence); + void PostRenderFrameTask(gfx::SwapCompletionResult result); void OnPresentation(const gfx::PresentationFeedback& feedback); std::unique_ptr<PlatformWindowSurface> window_surface_; diff --git a/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc b/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc index ceb5cc4f5f2..c5be0bb4632 100644 --- a/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc +++ b/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc @@ -76,7 +76,7 @@ bool SkiaGlRenderer::Initialize() { gr_context_ = GrContext::MakeGL(std::move(native_interface), options); DCHECK(gr_context_); - PostRenderFrameTask(gfx::SwapResult::SWAP_ACK, nullptr); + PostRenderFrameTask(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK)); return true; } @@ -106,7 +106,7 @@ void SkiaGlRenderer::RenderFrame() { } else { Draw(sk_surface_->getCanvas(), NextFraction()); } - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); if (gl_surface_->SupportsAsyncSwap()) { @@ -117,17 +117,14 @@ void SkiaGlRenderer::RenderFrame() { weak_ptr_factory_.GetWeakPtr())); } else { PostRenderFrameTask( - gl_surface_->SwapBuffers(base::BindOnce( - &SkiaGlRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())), - nullptr); + gfx::SwapCompletionResult(gl_surface_->SwapBuffers(base::BindOnce( + &SkiaGlRenderer::OnPresentation, weak_ptr_factory_.GetWeakPtr())))); } } -void SkiaGlRenderer::PostRenderFrameTask( - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - if (gpu_fence) - gpu_fence->Wait(); +void SkiaGlRenderer::PostRenderFrameTask(gfx::SwapCompletionResult result) { + if (result.gpu_fence) + result.gpu_fence->Wait(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&SkiaGlRenderer::RenderFrame, diff --git a/chromium/ui/ozone/demo/skia/skia_gl_renderer.h b/chromium/ui/ozone/demo/skia/skia_gl_renderer.h index 8abcc67d6cf..08195eebf87 100644 --- a/chromium/ui/ozone/demo/skia/skia_gl_renderer.h +++ b/chromium/ui/ozone/demo/skia/skia_gl_renderer.h @@ -21,7 +21,6 @@ #include "ui/ozone/demo/renderer_base.h" namespace gfx { -class GpuFence; struct PresentationFeedback; } // namespace gfx @@ -47,8 +46,7 @@ class SkiaGlRenderer : public RendererBase, protected: virtual void RenderFrame(); - virtual void PostRenderFrameTask(gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence>); + virtual void PostRenderFrameTask(gfx::SwapCompletionResult result); void Draw(SkCanvas* canvas, float fraction); void StartDDLRenderThreadIfNecessary(SkSurface* sk_surface); diff --git a/chromium/ui/ozone/demo/skia/skia_renderer_factory.h b/chromium/ui/ozone/demo/skia/skia_renderer_factory.h index 192c12d67af..029211cad06 100644 --- a/chromium/ui/ozone/demo/skia/skia_renderer_factory.h +++ b/chromium/ui/ozone/demo/skia/skia_renderer_factory.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/macros.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/demo/renderer_factory.h" diff --git a/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc b/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc index e2ee9432d98..ec083066e53 100644 --- a/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc +++ b/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc @@ -244,7 +244,7 @@ void SurfacelessSkiaGlRenderer::RenderFrame() { } else { Draw(sk_surface->getCanvas(), NextFraction()); } - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); if (!disable_primary_plane_) { @@ -270,9 +270,8 @@ void SurfacelessSkiaGlRenderer::RenderFrame() { } void SurfacelessSkiaGlRenderer::PostRenderFrameTask( - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - switch (result) { + gfx::SwapCompletionResult result) { + switch (result.swap_result) { case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS: for (size_t i = 0; i < base::size(buffers_); ++i) { buffers_[i] = std::make_unique<BufferWrapper>(); @@ -282,7 +281,7 @@ void SurfacelessSkiaGlRenderer::PostRenderFrameTask( } FALLTHROUGH; // We want to render a new frame anyways. case gfx::SwapResult::SWAP_ACK: - SkiaGlRenderer::PostRenderFrameTask(result, std::move(gpu_fence)); + SkiaGlRenderer::PostRenderFrameTask(std::move(result)); break; case gfx::SwapResult::SWAP_FAILED: LOG(FATAL) << "Failed to swap buffers"; diff --git a/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h b/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h index e24652c26a5..469083f14dd 100644 --- a/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h +++ b/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h @@ -30,8 +30,7 @@ class SurfacelessSkiaGlRenderer : public SkiaGlRenderer { private: // SkiaGlRenderer: void RenderFrame() override; - void PostRenderFrameTask(gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence>) override; + void PostRenderFrameTask(gfx::SwapCompletionResult result) override; class BufferWrapper; diff --git a/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc b/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc index 6117ac4f0f3..bc45833e24b 100644 --- a/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc +++ b/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc @@ -202,7 +202,7 @@ bool SurfacelessGlRenderer::Initialize() { use_gpu_fences_ = gl_surface_->SupportsPlaneGpuFences(); // Schedule the initial render. - PostRenderFrameTask(gfx::SwapResult::SWAP_ACK, nullptr); + PostRenderFrameTask(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK)); return true; } @@ -288,12 +288,11 @@ void SurfacelessGlRenderer::RenderFrame() { } void SurfacelessGlRenderer::PostRenderFrameTask( - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - if (gpu_fence) - gpu_fence->Wait(); + gfx::SwapCompletionResult result) { + if (result.gpu_fence) + result.gpu_fence->Wait(); - switch (result) { + switch (result.swap_result) { case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS: for (size_t i = 0; i < base::size(buffers_); ++i) { buffers_[i] = std::make_unique<BufferWrapper>(); diff --git a/chromium/ui/ozone/demo/surfaceless_gl_renderer.h b/chromium/ui/ozone/demo/surfaceless_gl_renderer.h index ba6d12a24e5..af3a3b2c690 100644 --- a/chromium/ui/ozone/demo/surfaceless_gl_renderer.h +++ b/chromium/ui/ozone/demo/surfaceless_gl_renderer.h @@ -34,8 +34,7 @@ class SurfacelessGlRenderer : public RendererBase { private: void RenderFrame(); - void PostRenderFrameTask(gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence); + void PostRenderFrameTask(gfx::SwapCompletionResult result); void OnPresentation(const gfx::PresentationFeedback& feedback); class BufferWrapper { diff --git a/chromium/ui/ozone/demo/vulkan_renderer.cc b/chromium/ui/ozone/demo/vulkan_renderer.cc index 6d778ce8e98..04235e74ee3 100644 --- a/chromium/ui/ozone/demo/vulkan_renderer.cc +++ b/chromium/ui/ozone/demo/vulkan_renderer.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/location.h" +#include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "gpu/vulkan/init/vulkan_factory.h" diff --git a/chromium/ui/ozone/ozone.gni b/chromium/ui/ozone/ozone.gni index 13fecf9038a..05d59e73fad 100644 --- a/chromium/ui/ozone/ozone.gni +++ b/chromium/ui/ozone/ozone.gni @@ -4,6 +4,7 @@ import("//build/config/chromecast_build.gni") import("//build/config/ui.gni") +import("//build/toolchain/kythe.gni") import("//build/toolchain/toolchain.gni") declare_args() { @@ -65,6 +66,12 @@ declare_args() { ozone_platform = "x11" ozone_platform_gbm = true ozone_platform_x11 = true + + # Enable the Ozone Wayland platform for ChromiumOS codesearch-gen bots + # so we get cross-references in codesearch. We can remove this once + # we can compile both x11 and Ozone on desktop Linux Chrome. + # TODO(crbug.com/1085700): Remove enable_kythe_annotations here. + ozone_platform_wayland = enable_kythe_annotations } else if (is_desktop_linux) { ozone_platform = "x11" ozone_platform_wayland = true diff --git a/chromium/ui/ozone/platform/cast/BUILD.gn b/chromium/ui/ozone/platform/cast/BUILD.gn index 54d03308b82..22ef134ffb2 100644 --- a/chromium/ui/ozone/platform/cast/BUILD.gn +++ b/chromium/ui/ozone/platform/cast/BUILD.gn @@ -43,6 +43,7 @@ source_set("cast") { "//chromecast:chromecast_buildflags", "//chromecast/base:base", "//chromecast/graphics:libcast_graphics_1.0", + "//ui/base/cursor:cursor_base", "//ui/base/ime", "//ui/events/ozone", "//ui/events/ozone/evdev", diff --git a/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc b/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc index a2ce0266582..b5702ad228d 100644 --- a/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc +++ b/chromium/ui/ozone/platform/cast/ozone_platform_cast.cc @@ -14,6 +14,7 @@ #include "chromecast/chromecast_buildflags.h" #include "chromecast/public/cast_egl_platform.h" #include "chromecast/public/cast_egl_platform_shlib.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/ime/input_method_minimal.h" #include "ui/display/types/native_display_delegate.h" #include "ui/events/ozone/device/device_manager.h" @@ -24,7 +25,6 @@ #include "ui/ozone/platform/cast/overlay_manager_cast.h" #include "ui/ozone/platform/cast/platform_window_cast.h" #include "ui/ozone/platform/cast/surface_factory_cast.h" -#include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/ozone_platform.h" @@ -83,9 +83,7 @@ class OzonePlatformCast : public OzonePlatform { OverlayManagerOzone* GetOverlayManager() override { return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } InputController* GetInputController() override { return event_factory_ozone_->input_controller(); } @@ -123,7 +121,7 @@ class OzonePlatformCast : public OzonePlatform { void InitializeUI(const InitParams& params) override { device_manager_ = CreateDeviceManager(); - cursor_factory_ = std::make_unique<CursorFactoryOzone>(); + cursor_factory_ = std::make_unique<CursorFactory>(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); // Enable dummy software rendering support if GPU process disabled @@ -157,7 +155,7 @@ class OzonePlatformCast : public OzonePlatform { std::unique_ptr<DeviceManager> device_manager_; std::unique_ptr<CastEglPlatform> egl_platform_; std::unique_ptr<SurfaceFactoryCast> surface_factory_; - std::unique_ptr<CursorFactoryOzone> cursor_factory_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; std::unique_ptr<OverlayManagerOzone> overlay_manager_; std::unique_ptr<EventFactoryEvdev> event_factory_ozone_; diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 0ef7cd7e786..01ed60608fd 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -127,6 +127,8 @@ source_set("gbm") { "//third_party/libsync", "//third_party/minigbm", "//ui/base", + "//ui/base/cursor", + "//ui/base/cursor:cursor_base", "//ui/base/ime", "//ui/display", "//ui/display/types", @@ -178,6 +180,7 @@ source_set("gbm_unittests") { testonly = true sources = [ "common/drm_util_unittest.cc", + "gpu/drm_display_unittest.cc", "gpu/drm_overlay_manager_unittest.cc", "gpu/drm_overlay_validator_unittest.cc", "gpu/drm_thread_unittest.cc", diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.cc b/chromium/ui/ozone/platform/drm/common/drm_util.cc index 7ac144c6bdf..656330d42bf 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util.cc @@ -18,6 +18,7 @@ #include "base/containers/flat_map.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" +#include "base/notreached.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/display_mode.h" #include "ui/display/util/display_util.h" @@ -210,7 +211,7 @@ display::PanelOrientation GetPanelOrientation(int fd, int index = GetDrmProperty(fd, connector, "panel orientation", &property); if (index < 0) return display::PanelOrientation::kNormal; - DCHECK_LT(connector->prop_values[index], display::PanelOrientation::kLast); + DCHECK_LE(connector->prop_values[index], display::PanelOrientation::kLast); return static_cast<display::PanelOrientation>(connector->prop_values[index]); } diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.h b/chromium/ui/ozone/platform/drm/gpu/drm_device.h index 0cbcca2fe96..fdc7f2d0a38 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.h @@ -240,6 +240,7 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { protected: friend class base::RefCountedThreadSafe<DrmDevice>; + friend class DrmDisplayTest; virtual ~DrmDevice(); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc index 5d879fa9853..9b1a8b2a4a6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/file_descriptor_posix.h" +#include "base/logging.h" #include "base/single_thread_task_runner.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_device_generator.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc index 89b9ea98bb4..0866e1989ed 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc @@ -7,10 +7,13 @@ #include <xf86drmMode.h> #include <memory> +#include "base/logging.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" +#include "ui/display/display_features.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" +#include "ui/gfx/color_space.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/screen_manager.h" @@ -82,12 +85,24 @@ std::vector<drmModeModeInfo> GetDrmModeVector(drmModeConnector* connector) { return modes; } +void FillLinearValues(std::vector<display::GammaRampRGBEntry>* table, + size_t table_size, + float max_value) { + for (size_t i = 0; i < table_size; i++) { + const uint16_t v = + max_value * std::numeric_limits<uint16_t>::max() * i / (table_size - 1); + struct display::GammaRampRGBEntry gamma_entry = {v, v, v}; + table->push_back(gamma_entry); + } +} + } // namespace DrmDisplay::DrmDisplay(ScreenManager* screen_manager, const scoped_refptr<DrmDevice>& drm) - : screen_manager_(screen_manager), drm_(drm) { -} + : screen_manager_(screen_manager), + drm_(drm), + current_color_space_(gfx::ColorSpace::CreateSRGB()) {} DrmDisplay::~DrmDisplay() { } @@ -112,6 +127,14 @@ std::unique_ptr<display::DisplaySnapshot> DrmDisplay::Update( display_id_ = params->display_id(); modes_ = GetDrmModeVector(info->connector()); + is_hdr_capable_ = + params->bits_per_channel() > 8 && params->color_space().IsHDR(); +#if defined(OS_CHROMEOS) + is_hdr_capable_ = + is_hdr_capable_ && + base::FeatureList::IsEnabled(display::features::kUseHDRTransferFunction); +#endif + return params; } @@ -202,10 +225,14 @@ void DrmDisplay::SetBackgroundColor(const uint64_t background_color) { void DrmDisplay::SetGammaCorrection( const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut) { - if (!drm_->plane_manager()->SetGammaCorrection(crtc_, degamma_lut, - gamma_lut)) { - LOG(ERROR) << "Failed to set gamma tables for display: crtc_id = " << crtc_; - } + // When both |degamma_lut| and |gamma_lut| are empty they are interpreted as + // "linear/pass-thru" [1]. If the display |is_hdr_capable_| we have to make + // sure the |current_color_space_| is considered properly. + // [1] https://www.kernel.org/doc/html/v4.19/gpu/drm-kms.html#color-management-properties + if (degamma_lut.empty() && gamma_lut.empty() && is_hdr_capable_) + SetColorSpace(current_color_space_); + else + CommitGammaCorrection(degamma_lut, gamma_lut); } // TODO(gildekel): consider reformatting this to use the new DRM API or cache @@ -229,4 +256,35 @@ void DrmDisplay::SetPrivacyScreen(bool enabled) { } } +void DrmDisplay::SetColorSpace(const gfx::ColorSpace& color_space) { + // There's only something to do if the display supports HDR. + if (!is_hdr_capable_) + return; + current_color_space_ = color_space; + + // When |color_space| is HDR we can simply leave the gamma tables empty, which + // is interpreted as "linear/pass-thru", see [1]. However when we have an SDR + // |color_space|, we need to write a scaled down |gamma| function to prevent + // the mode change brightness to be visible. + std::vector<display::GammaRampRGBEntry> degamma; + std::vector<display::GammaRampRGBEntry> gamma; + if (current_color_space_.IsHDR()) + return CommitGammaCorrection(degamma, gamma); + + // TODO(mcasas) This should be the same value as in DisplayChangeObservers's + // FillDisplayColorSpaces, move to a common place. + constexpr float kHDRLevel = 2.0; + // TODO(mcasas): Retrieve this from the |drm_| HardwareDisplayPlaneManager. + constexpr size_t kNumGammaSamples = 16ul; + FillLinearValues(&gamma, kNumGammaSamples, 1.0 / kHDRLevel); + CommitGammaCorrection(degamma, gamma); +} + +void DrmDisplay::CommitGammaCorrection( + const std::vector<display::GammaRampRGBEntry>& degamma_lut, + const std::vector<display::GammaRampRGBEntry>& gamma_lut) { + if (!drm_->plane_manager()->SetGammaCorrection(crtc_, degamma_lut, gamma_lut)) + LOG(ERROR) << "Failed to set gamma tables for display: crtc_id = " << crtc_; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.h b/chromium/ui/ozone/platform/drm/gpu/drm_display.h index de721282b28..a658477fc2d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.h @@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "ui/display/types/display_constants.h" +#include "ui/gfx/color_space.h" #include "ui/gfx/geometry/point.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" @@ -24,7 +25,6 @@ struct GammaRampRGBEntry; } namespace ui { - class DrmDevice; class HardwareDisplayControllerInfo; class ScreenManager; @@ -54,8 +54,15 @@ class DrmDisplay { const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut); void SetPrivacyScreen(bool enabled); + void SetColorSpace(const gfx::ColorSpace& color_space); + + void set_is_hdr_capable_for_testing(bool value) { is_hdr_capable_ = value; } private: + void CommitGammaCorrection( + const std::vector<display::GammaRampRGBEntry>& degamma_lut, + const std::vector<display::GammaRampRGBEntry>& gamma_lut); + ScreenManager* screen_manager_; // Not owned. int64_t display_id_ = -1; @@ -64,6 +71,8 @@ class DrmDisplay { ScopedDrmConnectorPtr connector_; std::vector<drmModeModeInfo> modes_; gfx::Point origin_; + bool is_hdr_capable_ = false; + gfx::ColorSpace current_color_space_; DISALLOW_COPY_AND_ASSIGN(DrmDisplay); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc new file mode 100644 index 00000000000..30e8c7ef8bc --- /dev/null +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc @@ -0,0 +1,192 @@ +// 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/drm/gpu/drm_display.h" + +#include <utility> + +#include "base/test/task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/linux/test/mock_gbm_device.h" +#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" +#include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" +#include "ui/ozone/platform/drm/gpu/mock_drm_device.h" +#include "ui/ozone/platform/drm/gpu/screen_manager.h" + +using ::testing::_; +using ::testing::SizeIs; + +// Verifies that the argument goes from 0 to the maximum uint16_t times |scale|. +MATCHER_P(MatchesLinearRamp, scale, "") { + EXPECT_FALSE(arg.empty()); + + EXPECT_EQ(arg.front().r, 0); + EXPECT_EQ(arg.front().g, 0); + EXPECT_EQ(arg.front().b, 0); + + const uint16_t max_value = std::numeric_limits<uint16_t>::max() * scale; + + const auto middle_element = arg[arg.size() / 2]; + const uint16_t middle_value = max_value * (arg.size() / 2) / (arg.size() - 1); + EXPECT_NEAR(middle_element.r, middle_value, 1); + EXPECT_NEAR(middle_element.g, middle_value, 1); + EXPECT_NEAR(middle_element.b, middle_value, 1); + + const uint16_t last_value = max_value; + EXPECT_EQ(arg.back().r, last_value); + EXPECT_EQ(arg.back().g, last_value); + EXPECT_EQ(arg.back().b, last_value); + + return true; +} + +namespace ui { + +namespace { + +class MockHardwareDisplayPlaneManager : public HardwareDisplayPlaneManager { + public: + explicit MockHardwareDisplayPlaneManager(DrmDevice* drm) + : HardwareDisplayPlaneManager(drm) {} + ~MockHardwareDisplayPlaneManager() override = default; + + MOCK_METHOD3(SetGammaCorrection, + bool(uint32_t crtc_id, + const std::vector<display::GammaRampRGBEntry>& degamma_lut, + const std::vector<display::GammaRampRGBEntry>& gamma_lut)); + + bool Modeset(uint32_t crtc_id, + uint32_t framebuffer_id, + uint32_t connector_id, + const drmModeModeInfo& mode, + const HardwareDisplayPlaneList& plane_list) override { + return false; + } + bool DisableModeset(uint32_t crtc_id, uint32_t connector) override { + return false; + } + bool Commit(HardwareDisplayPlaneList* plane_list, + scoped_refptr<PageFlipRequest> page_flip_request, + std::unique_ptr<gfx::GpuFence>* out_fence) override { + return false; + } + bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override { + return false; + } + bool SetColorCorrectionOnAllCrtcPlanes( + uint32_t crtc_id, + ScopedDrmColorCtmPtr ctm_blob_data) override { + return false; + } + bool ValidatePrimarySize(const DrmOverlayPlane& primary, + const drmModeModeInfo& mode) override { + return false; + } + void RequestPlanesReadyCallback( + DrmOverlayPlaneList planes, + base::OnceCallback<void(DrmOverlayPlaneList planes)> callback) override { + return; + } + bool InitializePlanes() override { return false; } + bool SetPlaneData(HardwareDisplayPlaneList* plane_list, + HardwareDisplayPlane* hw_plane, + const DrmOverlayPlane& overlay, + uint32_t crtc_id, + const gfx::Rect& src_rect) override { + return false; + } + std::unique_ptr<HardwareDisplayPlane> CreatePlane( + uint32_t plane_id) override { + return nullptr; + } + bool IsCompatible(HardwareDisplayPlane* plane, + const DrmOverlayPlane& overlay, + uint32_t crtc_index) const override { + return false; + } + bool CommitColorMatrix(const CrtcProperties& crtc_props) override { + return false; + } + bool CommitGammaCorrection(const CrtcProperties& crtc_props) override { + return false; + } +}; + +} // namespace + +class DrmDisplayTest : public testing::Test { + protected: + DrmDisplayTest() + : mock_drm_device_(base::MakeRefCounted<MockDrmDevice>( + std::make_unique<MockGbmDevice>())), + drm_display_(&screen_manager_, mock_drm_device_) {} + + MockHardwareDisplayPlaneManager* AddMockHardwareDisplayPlaneManager() { + auto mock_hardware_display_plane_manager = + std::make_unique<MockHardwareDisplayPlaneManager>( + mock_drm_device_.get()); + MockHardwareDisplayPlaneManager* pointer = + mock_hardware_display_plane_manager.get(); + mock_drm_device_->plane_manager_ = + std::move(mock_hardware_display_plane_manager); + return pointer; + } + + base::test::TaskEnvironment env_; + scoped_refptr<DrmDevice> mock_drm_device_; + ScreenManager screen_manager_; + DrmDisplay drm_display_; +}; + +TEST_F(DrmDisplayTest, SetColorSpace) { + drm_display_.set_is_hdr_capable_for_testing(true); + MockHardwareDisplayPlaneManager* plane_manager = + AddMockHardwareDisplayPlaneManager(); + + ON_CALL(*plane_manager, SetGammaCorrection(_, SizeIs(0), _)) + .WillByDefault(::testing::Return(true)); + + const auto kHDRColorSpace = gfx::ColorSpace::CreateHDR10(); + EXPECT_CALL(*plane_manager, SetGammaCorrection(_, SizeIs(0), SizeIs(0))); + drm_display_.SetColorSpace(kHDRColorSpace); + + const auto kSDRColorSpace = gfx::ColorSpace::CreateREC709(); + constexpr float kHDRLevel = 2.0; + EXPECT_CALL( + *plane_manager, + SetGammaCorrection(_, SizeIs(0), MatchesLinearRamp(1.0 / kHDRLevel))); + drm_display_.SetColorSpace(kSDRColorSpace); +} + +TEST_F(DrmDisplayTest, SetEmptyGammaCorrectionNonHDRDisplay) { + MockHardwareDisplayPlaneManager* plane_manager = + AddMockHardwareDisplayPlaneManager(); + + ON_CALL(*plane_manager, SetGammaCorrection(_, _, _)) + .WillByDefault(::testing::Return(true)); + + EXPECT_CALL(*plane_manager, SetGammaCorrection(_, SizeIs(0), SizeIs(0))); + drm_display_.SetGammaCorrection(std::vector<display::GammaRampRGBEntry>(), + std::vector<display::GammaRampRGBEntry>()); +} + +TEST_F(DrmDisplayTest, SetEmptyGammaCorrectionHDRDisplay) { + drm_display_.set_is_hdr_capable_for_testing(true); + MockHardwareDisplayPlaneManager* plane_manager = + AddMockHardwareDisplayPlaneManager(); + + ON_CALL(*plane_manager, SetGammaCorrection(_, _, _)) + .WillByDefault(::testing::Return(true)); + + constexpr float kHDRLevel = 2.0; + EXPECT_CALL( + *plane_manager, + SetGammaCorrection(_, SizeIs(0), MatchesLinearRamp(1.0 / kHDRLevel))); + drm_display_.SetGammaCorrection(std::vector<display::GammaRampRGBEntry>(), + std::vector<display::GammaRampRGBEntry>()); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc index 772786b42ed..bdf7db0ec19 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/logging.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gfx/linux/drm_util_linux.h" #include "ui/gfx/linux/gbm_buffer.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc index 74a5f19d91c..e7642e41351 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc @@ -8,6 +8,7 @@ #include <memory> #include <utility> +#include "base/logging.h" #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" @@ -250,6 +251,17 @@ void DrmGpuDisplayManager::SetPrivacyScreen(int64_t display_id, bool enabled) { display->SetPrivacyScreen(enabled); } +void DrmGpuDisplayManager::SetColorSpace(int64_t crtc_id, + const gfx::ColorSpace& color_space) { + for (const auto& display : displays_) { + if (display->crtc() == crtc_id) { + display->SetColorSpace(color_space); + return; + } + } + LOG(ERROR) << __func__ << " there is no display with CRTC ID " << crtc_id; +} + DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) { for (const auto& display : displays_) { if (display->display_id() == display_id) diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h index 5efd37fde04..6bc0d2472ea 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h @@ -21,6 +21,10 @@ class DisplayMode; struct GammaRampRGBEntry; } +namespace gfx { +class ColorSpace; +} + namespace ui { class DrmDeviceManager; @@ -61,6 +65,8 @@ class DrmGpuDisplayManager { const std::vector<display::GammaRampRGBEntry>& gamma_lut); void SetPrivacyScreen(int64_t display_id, bool enabled); + void SetColorSpace(int64_t crtc_id, const gfx::ColorSpace& color_space); + private: DrmDisplay* FindDisplay(int64_t display_id); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_util.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_util.cc index bc1ab154808..4e70e45f0c1 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_util.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_util.cc @@ -9,6 +9,7 @@ #include <xf86drmMode.h> #include "base/files/scoped_file.h" +#include "base/logging.h" #include "base/trace_event/trace_event.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index 181fcd86a18..13b3dd7a141 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -5,6 +5,7 @@ #include "ui/ozone/platform/drm/gpu/drm_thread.h" #include <gbm.h> + #include <memory> #include <utility> @@ -22,6 +23,7 @@ #include "ui/gfx/linux/gbm_util.h" #include "ui/gfx/presentation_feedback.h" #include "ui/ozone/platform/drm/common/drm_util.h" +#include "ui/ozone/platform/drm/gpu/crtc_controller.h" #include "ui/ozone/platform/drm/gpu/drm_device_generator.h" #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" #include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" @@ -108,8 +110,8 @@ void DrmThread::Init() { device_manager_ = std::make_unique<DrmDeviceManager>(std::move(device_generator_)); screen_manager_ = std::make_unique<ScreenManager>(); - display_manager_.reset( - new DrmGpuDisplayManager(screen_manager_.get(), device_manager_.get())); + display_manager_ = std::make_unique<DrmGpuDisplayManager>( + screen_manager_.get(), device_manager_.get()); DCHECK(task_runner()) << "DrmThread::Init -- thread doesn't have a task_runner"; @@ -276,8 +278,8 @@ void DrmThread::SetCursor(gfx::AcceleratedWidget widget, const gfx::Point& location, int32_t frame_delay_ms) { TRACE_EVENT0("drm", "DrmThread::SetCursor"); - screen_manager_->GetWindow(widget) - ->SetCursor(bitmaps, location, frame_delay_ms); + screen_manager_->GetWindow(widget)->SetCursor(bitmaps, location, + frame_delay_ms); } void DrmThread::MoveCursor(gfx::AcceleratedWidget widget, @@ -324,20 +326,21 @@ void DrmThread::RefreshNativeDisplays( } void DrmThread::ConfigureNativeDisplay( - int64_t id, - std::unique_ptr<display::DisplayMode> mode, - const gfx::Point& origin, + const display::DisplayConfigurationParams& display_config_params, base::OnceCallback<void(int64_t, bool)> callback) { TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplay"); - std::move(callback).Run( - id, display_manager_->ConfigureDisplay(id, *mode, origin)); -} -void DrmThread::DisableNativeDisplay( - int64_t id, - base::OnceCallback<void(int64_t, bool)> callback) { - TRACE_EVENT0("drm", "DrmThread::DisableNativeDisplay"); - std::move(callback).Run(id, display_manager_->DisableDisplay(id)); + if (display_config_params.mode) { + std::move(callback).Run( + display_config_params.id, + display_manager_->ConfigureDisplay(display_config_params.id, + *display_config_params.mode.value(), + display_config_params.origin)); + } else { + std::move(callback).Run( + display_config_params.id, + display_manager_->DisableDisplay(display_config_params.id)); + } } void DrmThread::TakeDisplayControl(base::OnceCallback<void(bool)> callback) { @@ -418,4 +421,17 @@ void DrmThread::ProcessPendingTasks() { pending_tasks_.clear(); } +void DrmThread::SetColorSpace(gfx::AcceleratedWidget widget, + const gfx::ColorSpace& color_space) { + DCHECK(screen_manager_->GetWindow(widget)); + HardwareDisplayController* controller = + screen_manager_->GetWindow(widget)->GetController(); + if (!controller) + return; + + const auto& crtc_controllers = controller->crtc_controllers(); + for (const auto& crtc_controller : crtc_controllers) + display_manager_->SetColorSpace(crtc_controller->crtc(), color_space); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h index b4ec4298339..71457f24c99 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h @@ -6,6 +6,7 @@ #define UI_OZONE_PLATFORM_DRM_GPU_DRM_THREAD_H_ #include <stdint.h> + #include <memory> #include "base/files/file.h" @@ -17,6 +18,7 @@ #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" +#include "ui/display/types/display_configuration_params.h" #include "ui/gfx/native_pixmap_handle.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/vsync_provider.h" @@ -32,14 +34,13 @@ class FilePath; } namespace display { -class DisplayMode; struct GammaRampRGBEntry; -} +} // namespace display namespace gfx { class Point; class Rect; -} +} // namespace gfx namespace ui { @@ -107,6 +108,9 @@ class DrmThread : public base::Thread, void AddDrmDeviceReceiver( mojo::PendingReceiver<ozone::mojom::DrmDevice> receiver); + void SetColorSpace(gfx::AcceleratedWidget widget, + const gfx::ColorSpace& color_space); + // Verifies if the display controller can successfully scanout the given set // of OverlaySurfaceCandidates and return the status associated with each // candidate. @@ -143,13 +147,8 @@ class DrmThread : public base::Thread, base::OnceCallback<void(MovableDisplaySnapshots)> callback) override; void AddGraphicsDevice(const base::FilePath& path, base::File file) override; void RemoveGraphicsDevice(const base::FilePath& path) override; - void DisableNativeDisplay( - int64_t id, - base::OnceCallback<void(int64_t, bool)> callback) override; void ConfigureNativeDisplay( - int64_t id, - std::unique_ptr<display::DisplayMode> mode, - const gfx::Point& origin, + const display::DisplayConfigurationParams& display_config_params, base::OnceCallback<void(int64_t, bool)> callback) override; void GetHDCPState(int64_t display_id, base::OnceCallback<void(int64_t, bool, display::HDCPState)> diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc index f94428bf86f..64751f39426 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc @@ -52,4 +52,11 @@ bool DrmWindowProxy::SupportsGpuFences() const { switches::kDisableExplicitDmaFences); } +void DrmWindowProxy::SetColorSpace(const gfx::ColorSpace& color_space) const { + drm_thread_->task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&DrmThread::SetColorSpace, base::Unretained(drm_thread_), + widget_, color_space)); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h index 303051c1c25..8bb036e0ef9 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h @@ -12,6 +12,10 @@ #include "ui/gfx/vsync_provider.h" #include "ui/ozone/public/swap_completion_callback.h" +namespace gfx { +class ColorSpace; +} + namespace ui { class DrmThread; @@ -30,6 +34,8 @@ class DrmWindowProxy { bool SupportsGpuFences() const; + void SetColorSpace(const gfx::ColorSpace& color_space) const; + private: const gfx::AcceleratedWidget widget_; diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index 8c32abbafef..29b76be970e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc @@ -79,6 +79,16 @@ bool GbmSurfaceless::ScheduleOverlayPlane( return true; } +bool GbmSurfaceless::Resize(const gfx::Size& size, + float scale_factor, + const gfx::ColorSpace& color_space, + bool has_alpha) { + if (window_) + window_->SetColorSpace(color_space); + + return SurfacelessEGL::Resize(size, scale_factor, color_space, has_alpha); +} + bool GbmSurfaceless::IsOffscreen() { return false; } @@ -111,7 +121,8 @@ void GbmSurfaceless::SwapBuffersAsync( TRACE_EVENT0("drm", "GbmSurfaceless::SwapBuffersAsync"); // If last swap failed, don't try to schedule new ones. if (!last_swap_buffers_result_) { - std::move(completion_callback).Run(gfx::SwapResult::SWAP_FAILED, nullptr); + std::move(completion_callback) + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED)); // Notify the caller, the buffer is never presented on a screen. std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure()); return; @@ -230,6 +241,13 @@ void GbmSurfaceless::SubmitFrame() { DCHECK(!unsubmitted_frames_.empty()); if (unsubmitted_frames_.front()->ready && !submitted_frame_) { + for (auto& overlay : unsubmitted_frames_.front()->overlays) { + if (overlay.z_order() == 0 && overlay.gpu_fence()) { + submitted_frame_gpu_fence_ = std::make_unique<gfx::GpuFence>( + gfx::CloneHandleForIPC(overlay.gpu_fence()->GetGpuFenceHandle())); + break; + } + } submitted_frame_ = std::move(unsubmitted_frames_.front()); unsubmitted_frames_.erase(unsubmitted_frames_.begin()); @@ -270,12 +288,19 @@ void GbmSurfaceless::OnSubmission(gfx::SwapResult result, } void GbmSurfaceless::OnPresentation(const gfx::PresentationFeedback& feedback) { - // Explicitly destroy overlays to free resources (e.g., fences) early. + gfx::PresentationFeedback feedback_copy = feedback; + + if (submitted_frame_gpu_fence_) { + feedback_copy.ready_timestamp = + submitted_frame_gpu_fence_->GetMaxTimestamp(); + } + submitted_frame_gpu_fence_.reset(); submitted_frame_->overlays.clear(); gfx::SwapResult result = submitted_frame_->swap_result; - std::move(submitted_frame_->completion_callback).Run(result, nullptr); - std::move(submitted_frame_->presentation_callback).Run(feedback); + std::move(submitted_frame_->completion_callback) + .Run(gfx::SwapCompletionResult(result)); + std::move(submitted_frame_->presentation_callback).Run(feedback_copy); submitted_frame_.reset(); if (result == gfx::SwapResult::SWAP_FAILED) { diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h index 2bfbf347f2e..9b638ef08ff 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h @@ -17,6 +17,10 @@ #include "ui/gl/scoped_binders.h" #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h" +namespace gfx { +class GpuFence; +} // namespace gfx + namespace ui { class DrmWindowProxy; @@ -44,6 +48,10 @@ class GbmSurfaceless : public gl::SurfacelessEGL { const gfx::RectF& crop_rect, bool enable_blend, std::unique_ptr<gfx::GpuFence> gpu_fence) override; + bool Resize(const gfx::Size& size, + float scale_factor, + const gfx::ColorSpace& color_space, + bool has_alpha) override; bool IsOffscreen() override; bool SupportsAsyncSwap() override; bool SupportsPostSubBuffer() override; @@ -104,6 +112,7 @@ class GbmSurfaceless : public gl::SurfacelessEGL { std::unique_ptr<gfx::VSyncProvider> vsync_provider_; std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_; std::unique_ptr<PendingFrame> submitted_frame_; + std::unique_ptr<gfx::GpuFence> submitted_frame_gpu_fence_; const bool has_implicit_external_sync_; const bool has_image_flush_external_; bool last_swap_buffers_result_ = true; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc index 2d19a1a8b26..6505127b024 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc @@ -126,11 +126,12 @@ std::vector<uint64_t> HardwareDisplayPlane::ModifiersForFormat( uint32_t format) const { std::vector<uint64_t> modifiers; - uint32_t format_index = - std::find(supported_formats_.begin(), supported_formats_.end(), format) - - supported_formats_.begin(); - DCHECK_LT(format_index, supported_formats_.size()); + auto it = + std::find(supported_formats_.begin(), supported_formats_.end(), format); + if (it == supported_formats_.end()) + return modifiers; + uint32_t format_index = it - supported_formats_.begin(); for (const auto& modifier : supported_format_modifiers_) { // modifier.formats is a bitmask of the formats the modifier // applies to, starting at format modifier.offset. That is, if bit diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc index 28d692268d7..1899daf4e7c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc @@ -4,6 +4,7 @@ #include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h" +#include "base/logging.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h index b1070a0d89e..b2443c90b07 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h @@ -84,7 +84,7 @@ class HardwareDisplayPlaneManager { void SetBackgroundColor(uint32_t crtc_id, const uint64_t background_color); // Sets the degamma/gamma luts on the CRTC object with ID |crtc_id|. - bool SetGammaCorrection( + virtual bool SetGammaCorrection( uint32_t crtc_id, const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc index 4ec9db772db..2bdf60aba9d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc @@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/files/platform_file.h" +#include "base/logging.h" #include "base/stl_util.h" #include "base/threading/sequenced_task_runner_handle.h" #include "ui/gfx/gpu_fence.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc index 4bbd89541aa..91926a0d2b2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/bind.h" +#include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc index 42473035751..342bad7004f 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc @@ -9,6 +9,7 @@ #include <utility> #include "base/files/platform_file.h" +#include "base/logging.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/display/types/display_snapshot.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc index 601cdb36873..e3792ee932d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc +++ b/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc @@ -7,6 +7,7 @@ #include <memory> #include "base/files/file_path.h" +#include "base/logging.h" #include "base/native_library.h" #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_image.h" diff --git a/chromium/ui/ozone/platform/drm/host/drm_device_handle.cc b/chromium/ui/ozone/platform/drm/host/drm_device_handle.cc index 7e2a58d4a32..c052a5d1bcb 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_device_handle.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_device_handle.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/files/file_path.h" +#include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/threading/scoped_blocking_call.h" #include "base/time/time.h" diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc index 3c84a92181f..3da0ad8e912 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc @@ -9,7 +9,9 @@ #include "base/bind.h" #include "base/location.h" +#include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" +#include "ui/display/types/display_configuration_params.h" #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" #include "ui/ozone/platform/drm/common/drm_util.h" @@ -44,12 +46,10 @@ void DrmDisplayHost::Configure(const display::DisplayMode* mode, configure_callback_ = std::move(callback); bool status = false; - if (mode) { - status = sender_->GpuConfigureNativeDisplay(snapshot_->display_id(), *mode, - origin); - } else { - status = sender_->GpuDisableNativeDisplay(snapshot_->display_id()); - } + + display::DisplayConfigurationParams display_config_params( + snapshot_->display_id(), origin, mode); + status = sender_->GpuConfigureNativeDisplay(display_config_params); if (!status) OnDisplayConfigured(false); diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h index d6c6804a586..8b9b8a39eea 100644 --- a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h +++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h @@ -6,15 +6,12 @@ #define UI_OZONE_PLATFORM_DRM_HOST_GPU_THREAD_ADAPTER_H_ #include "base/file_descriptor_posix.h" +#include "ui/display/types/display_configuration_params.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" -namespace display { -class DisplayMode; -} // namespace display - namespace ui { class DrmDisplayHostManager; @@ -46,10 +43,8 @@ class GpuThreadAdapter { virtual bool GpuRemoveGraphicsDevice(const base::FilePath& path) = 0; // Services needed by DrmDisplayHost - virtual bool GpuConfigureNativeDisplay(int64_t display_id, - const display::DisplayMode& pmode, - const gfx::Point& point) = 0; - virtual bool GpuDisableNativeDisplay(int64_t display_id) = 0; + virtual bool GpuConfigureNativeDisplay( + const display::DisplayConfigurationParams& display_config_params) = 0; virtual bool GpuGetHDCPState(int64_t display_id) = 0; virtual bool GpuSetHDCPState(int64_t display_id, display::HDCPState state) = 0; diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc index a2ca1364f51..58bd76117d4 100644 --- a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc @@ -128,36 +128,21 @@ bool HostDrmDevice::GpuRefreshNativeDisplays() { return true; } -bool HostDrmDevice::GpuConfigureNativeDisplay(int64_t id, - const display::DisplayMode& pmode, - const gfx::Point& origin) { +bool HostDrmDevice::GpuConfigureNativeDisplay( + const display::DisplayConfigurationParams& display_config_params) { DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); if (!IsConnected()) return false; - auto mode = std::make_unique<display::DisplayMode>( - pmode.size(), pmode.is_interlaced(), pmode.refresh_rate()); auto callback = base::BindOnce(&HostDrmDevice::GpuConfigureNativeDisplayCallback, this); - drm_device_->ConfigureNativeDisplay(id, std::move(mode), origin, + drm_device_->ConfigureNativeDisplay(display_config_params, std::move(callback)); return true; } -bool HostDrmDevice::GpuDisableNativeDisplay(int64_t id) { - DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); - if (!IsConnected()) - return false; - auto callback = - base::BindOnce(&HostDrmDevice::GpuDisableNativeDisplayCallback, this); - - drm_device_->DisableNativeDisplay(id, std::move(callback)); - - return true; -} - bool HostDrmDevice::GpuTakeDisplayControl() { DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); if (!IsConnected()) @@ -278,12 +263,6 @@ void HostDrmDevice::GpuRefreshNativeDisplaysCallback( display_manager_->GpuHasUpdatedNativeDisplays(std::move(displays)); } -void HostDrmDevice::GpuDisableNativeDisplayCallback(int64_t display_id, - bool success) const { - DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); - display_manager_->GpuConfiguredDisplay(display_id, success); -} - void HostDrmDevice::GpuTakeDisplayControlCallback(bool success) const { DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_); display_manager_->GpuTookDisplayControl(success); diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.h b/chromium/ui/ozone/platform/drm/host/host_drm_device.h index fbe3723e419..a21505193c6 100644 --- a/chromium/ui/ozone/platform/drm/host/host_drm_device.h +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.h @@ -65,10 +65,8 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>, bool GpuRemoveGraphicsDevice(const base::FilePath& path) override; // Services needed by DrmDisplayHost - bool GpuConfigureNativeDisplay(int64_t display_id, - const display::DisplayMode& pmode, - const gfx::Point& point) override; - bool GpuDisableNativeDisplay(int64_t display_id) override; + bool GpuConfigureNativeDisplay(const display::DisplayConfigurationParams& + display_config_params) override; bool GpuGetHDCPState(int64_t display_id) override; bool GpuSetHDCPState(int64_t display_id, display::HDCPState state) override; bool GpuSetColorMatrix(int64_t display_id, @@ -98,7 +96,6 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>, bool success) const; void GpuRefreshNativeDisplaysCallback(MovableDisplaySnapshots displays) const; - void GpuDisableNativeDisplayCallback(int64_t display_id, bool success) const; void GpuTakeDisplayControlCallback(bool success) const; void GpuRelinquishDisplayControlCallback(bool success) const; void GpuGetHDCPStateCallback(int64_t display_id, diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc index 23786834484..9e0cc71662e 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc +++ b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc @@ -21,6 +21,7 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "ui/base/buildflags.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/events/ozone/device/device_manager.h" #include "ui/events/ozone/evdev/event_factory_evdev.h" @@ -43,7 +44,6 @@ #include "ui/ozone/platform/drm/host/drm_window_host.h" #include "ui/ozone/platform/drm/host/drm_window_host_manager.h" #include "ui/ozone/platform/drm/host/host_drm_device.h" -#include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/mojom/drm_device.mojom.h" #include "ui/ozone/public/ozone_platform.h" @@ -80,9 +80,7 @@ class OzonePlatformGbm : public OzonePlatform { OverlayManagerOzone* GetOverlayManager() override { return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_ozone_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } InputController* GetInputController() override { return event_factory_ozone_->input_controller(); } @@ -202,7 +200,7 @@ class OzonePlatformGbm : public OzonePlatform { display_manager_ = std::make_unique<DrmDisplayHostManager>( adapter, device_manager_.get(), &host_properties_, event_factory_ozone_->input_controller()); - cursor_factory_ozone_ = std::make_unique<BitmapCursorFactoryOzone>(); + cursor_factory_ = std::make_unique<BitmapCursorFactoryOzone>(); host_drm_device_->SetDisplayManager(display_manager_.get()); } @@ -307,7 +305,7 @@ class OzonePlatformGbm : public OzonePlatform { scoped_refptr<HostDrmDevice> host_drm_device_; base::PlatformThreadRef host_thread_; std::unique_ptr<DeviceManager> device_manager_; - std::unique_ptr<BitmapCursorFactoryOzone> cursor_factory_ozone_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<DrmWindowHostManager> window_manager_; std::unique_ptr<DrmCursor> cursor_; std::unique_ptr<EventFactoryEvdev> event_factory_ozone_; diff --git a/chromium/ui/ozone/platform/headless/BUILD.gn b/chromium/ui/ozone/platform/headless/BUILD.gn index 9465b52ff12..ca02e70c2cd 100644 --- a/chromium/ui/ozone/platform/headless/BUILD.gn +++ b/chromium/ui/ozone/platform/headless/BUILD.gn @@ -26,6 +26,8 @@ source_set("headless") { "//base", "//skia", "//ui/base", + "//ui/base/cursor", + "//ui/base/cursor:cursor_base", "//ui/base/ime", "//ui/events", "//ui/events/ozone/layout", diff --git a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc index 9d356f701e3..707948351e0 100644 --- a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc +++ b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc @@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "build/build_config.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/base/ime/input_method_minimal.h" #include "ui/display/types/native_display_delegate.h" @@ -21,7 +22,6 @@ #include "ui/ozone/platform/headless/headless_surface_factory.h" #include "ui/ozone/platform/headless/headless_window.h" #include "ui/ozone/platform/headless/headless_window_manager.h" -#include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/ozone_platform.h" @@ -63,9 +63,7 @@ class OzonePlatformHeadless : public OzonePlatform { OverlayManagerOzone* GetOverlayManager() override { return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_ozone_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } InputController* GetInputController() override { return input_controller_.get(); } @@ -110,7 +108,7 @@ class OzonePlatformHeadless : public OzonePlatform { overlay_manager_ = std::make_unique<StubOverlayManager>(); input_controller_ = CreateStubInputController(); - cursor_factory_ozone_ = std::make_unique<BitmapCursorFactoryOzone>(); + cursor_factory_ = std::make_unique<BitmapCursorFactoryOzone>(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); } @@ -124,7 +122,7 @@ class OzonePlatformHeadless : public OzonePlatform { std::unique_ptr<HeadlessWindowManager> window_manager_; std::unique_ptr<HeadlessSurfaceFactory> surface_factory_; std::unique_ptr<PlatformEventSource> platform_event_source_; - std::unique_ptr<CursorFactoryOzone> cursor_factory_ozone_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<InputController> input_controller_; std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; std::unique_ptr<OverlayManagerOzone> overlay_manager_; diff --git a/chromium/ui/ozone/platform/scenic/BUILD.gn b/chromium/ui/ozone/platform/scenic/BUILD.gn index ed6dd8a0f29..32792aaf31a 100644 --- a/chromium/ui/ozone/platform/scenic/BUILD.gn +++ b/chromium/ui/ozone/platform/scenic/BUILD.gn @@ -55,6 +55,8 @@ source_set("scenic") { "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", "//ui/base", + "//ui/base/cursor", + "//ui/base/cursor:cursor_base", "//ui/base/ime/fuchsia", "//ui/display/fake", "//ui/events:dom_keycode_converter", diff --git a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc index c9f0f74031d..fcd5d1ccf32 100644 --- a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc @@ -15,6 +15,7 @@ #include "base/message_loop/message_pump_type.h" #include "base/notreached.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/base/ime/fuchsia/input_method_fuchsia.h" #include "ui/display/fake/fake_display_delegate.h" @@ -30,7 +31,6 @@ #include "ui/ozone/platform/scenic/scenic_window_manager.h" #include "ui/ozone/platform/scenic/sysmem_buffer_collection.h" #include "ui/ozone/platform_selection.h" -#include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/mojom/scenic_gpu_service.mojom.h" @@ -77,9 +77,7 @@ class OzonePlatformScenic return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_ozone_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } InputController* GetInputController() override { return input_controller_.get(); @@ -137,7 +135,7 @@ class OzonePlatformScenic window_manager_ = std::make_unique<ScenicWindowManager>(); overlay_manager_ = std::make_unique<StubOverlayManager>(); input_controller_ = CreateStubInputController(); - cursor_factory_ozone_ = std::make_unique<BitmapCursorFactoryOzone>(); + cursor_factory_ = std::make_unique<BitmapCursorFactoryOzone>(); scenic_gpu_host_ = std::make_unique<ScenicGpuHost>(window_manager_.get()); @@ -208,7 +206,7 @@ class OzonePlatformScenic std::unique_ptr<KeyboardLayoutEngine> keyboard_layout_engine_; std::unique_ptr<PlatformEventSource> platform_event_source_; - std::unique_ptr<CursorFactoryOzone> cursor_factory_ozone_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<InputController> input_controller_; std::unique_ptr<OverlayManagerOzone> overlay_manager_; std::unique_ptr<ScenicGpuHost> scenic_gpu_host_; diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface.cc b/chromium/ui/ozone/platform/scenic/scenic_surface.cc index 2d75a091190..dc7fd2ec10f 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_surface.cc @@ -19,11 +19,13 @@ ScenicSurface::ScenicSurface( scenic::SessionPtrAndListenerRequest sesion_and_listener_request) : scenic_session_(std::move(sesion_and_listener_request)), shape_(&scenic_session_), - material_(&scenic_session_), scenic_surface_factory_(scenic_surface_factory), window_(window) { + // Setting alpha to 0 makes this transparent. + scenic::Material transparent_material(&scenic_session_); + transparent_material.SetColor(0, 0, 0, 0); shape_.SetShape(scenic::Rectangle(&scenic_session_, 1.f, 1.f)); - shape_.SetMaterial(material_); + shape_.SetMaterial(transparent_material); scenic_surface_factory->AddSurface(window, this); scenic_session_.SetDebugName("Chromium ScenicSurface"); } @@ -33,23 +35,28 @@ ScenicSurface::~ScenicSurface() { scenic_surface_factory_->RemoveSurface(window_); } -void ScenicSurface::SetTextureToNewImagePipe( +bool ScenicSurface::SetTextureToNewImagePipe( fidl::InterfaceRequest<fuchsia::images::ImagePipe2> image_pipe_request) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); uint32_t image_pipe_id = scenic_session_.AllocResourceId(); scenic_session_.Enqueue(scenic::NewCreateImagePipe2Cmd( image_pipe_id, std::move(image_pipe_request))); - material_.SetTexture(image_pipe_id); + scenic::Material image_material(&scenic_session_); + image_material.SetTexture(image_pipe_id); + shape_.SetMaterial(image_material); scenic_session_.ReleaseResource(image_pipe_id); scenic_session_.Present2( /*requested_presentation_time=*/0, /*requested_prediction_span=*/0, [](fuchsia::scenic::scheduling::FuturePresentationTimes info) {}); + return true; } void ScenicSurface::SetTextureToImage(const scenic::Image& image) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - material_.SetTexture(image); + scenic::Material image_material(&scenic_session_); + image_material.SetTexture(image); + shape_.SetMaterial(image_material); } mojo::PlatformHandle ScenicSurface::CreateView() { diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface.h b/chromium/ui/ozone/platform/scenic/scenic_surface.h index d9b730ae611..3264a594fc0 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface.h +++ b/chromium/ui/ozone/platform/scenic/scenic_surface.h @@ -35,8 +35,9 @@ class ScenicSurface : public ui::PlatformWindowSurface { ~ScenicSurface() override; // Sets the texture of the surface to a new image pipe. - void SetTextureToNewImagePipe( - fidl::InterfaceRequest<fuchsia::images::ImagePipe2> image_pipe_request); + bool SetTextureToNewImagePipe( + fidl::InterfaceRequest<fuchsia::images::ImagePipe2> image_pipe_request) + override; // Sets the texture of the surface to an image resource. void SetTextureToImage(const scenic::Image& image); @@ -58,7 +59,6 @@ class ScenicSurface : public ui::PlatformWindowSurface { scenic::Session scenic_session_; std::unique_ptr<scenic::View> parent_; scenic::ShapeNode shape_; - scenic::Material material_; ScenicSurfaceFactory* const scenic_surface_factory_; const gfx::AcceleratedWidget window_; diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc index 7280c5e154f..7719bac9d88 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc @@ -9,8 +9,8 @@ #include <memory> #include "base/bind.h" -#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "third_party/angle/src/common/fuchsia_egl/fuchsia_egl.h" @@ -43,7 +43,7 @@ struct FuchsiaEGLWindowDeleter { fuchsia::ui::scenic::ScenicPtr ConnectToScenic() { fuchsia::ui::scenic::ScenicPtr scenic = - base::fuchsia::ComponentContextForCurrentProcess() + base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::ui::scenic::Scenic>(); scenic.set_error_handler([](zx_status_t status) { @@ -130,8 +130,7 @@ class GLOzoneEGLScenic : public GLOzoneEGL { fuchsia::sysmem::AllocatorHandle ConnectSysmemAllocator() { fuchsia::sysmem::AllocatorHandle allocator; - base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect( - allocator.NewRequest()); + base::ComponentContextForProcess()->svc()->Connect(allocator.NewRequest()); return allocator; } diff --git a/chromium/ui/ozone/platform/scenic/scenic_window.cc b/chromium/ui/ozone/platform/scenic/scenic_window.cc index 0de00de69aa..81cafe89676 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_window.cc @@ -37,8 +37,7 @@ ScenicWindow::ScenicWindow(ScenicWindowManager* window_manager, "chromium window"), node_(&scenic_session_), input_node_(&scenic_session_), - render_node_(&scenic_session_), - background_node_(&scenic_session_) { + render_node_(&scenic_session_) { scenic_session_.set_error_handler( fit::bind_member(this, &ScenicWindow::OnScenicError)); scenic_session_.set_event_handler( @@ -52,28 +51,8 @@ ScenicWindow::ScenicWindow(ScenicWindowManager* window_manager, // Add input shape. node_.AddChild(input_node_); - // Add rendering subtree, rooted at Z=-2 to make room for background layers in - // the Z-order (lesser values are higher in the visual ordering). - constexpr float kRenderNodeZPosition = -2.; - constexpr float kBackgroundNodeZPosition = kRenderNodeZPosition + 1.; - render_node_.SetTranslation(0., 0., kRenderNodeZPosition); node_.AddChild(render_node_); - // Initialize a black background to be just behind |render_node_|. - scenic::Material background_color(&scenic_session_); - background_color.SetColor(0, 0, 0, 255); // RGBA (0,0,0,255) = opaque black. - background_node_.SetMaterial(background_color); - scenic::Rectangle background_shape(&scenic_session_, 1., 1.); - background_node_.SetShape(background_shape); - background_node_.SetTranslation(0., 0., kBackgroundNodeZPosition); - node_.AddChild(background_node_); - - // Render the background immediately. - scenic_session_.Present2( - /*requested_presentation_time=*/0, - /*requested_prediction_span=*/0, - [](fuchsia::scenic::scheduling::FuturePresentationTimes info) {}); - delegate_->OnAcceleratedWidgetAvailable(window_id_); } @@ -247,9 +226,6 @@ void ScenicWindow::UpdateSize() { size_dips_.height()); input_node_.SetShape(window_rect); - // Resize the input and background nodes to cover the whole surface. - background_node_.SetShape(window_rect); - // This is necessary when using vulkan because ImagePipes are presented // separately and we need to make sure our sizes change is committed. scenic_session_.Present2( diff --git a/chromium/ui/ozone/platform/scenic/scenic_window.h b/chromium/ui/ozone/platform/scenic/scenic_window.h index d70d88b218f..2edac2d4e7e 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window.h +++ b/chromium/ui/ozone/platform/scenic/scenic_window.h @@ -119,10 +119,6 @@ class COMPONENT_EXPORT(OZONE) ScenicWindow // Node in |scenic_session_| for rendering (hit testing disabled). scenic::EntityNode render_node_; - // Node in |scenic_session_| for rendering a solid color, placed just behind - // |render_node_| in the Z order. - scenic::ShapeNode background_node_; - std::unique_ptr<scenic::ViewHolder> surface_view_holder_; // The ratio used for translating device-independent coordinates to absolute diff --git a/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc b/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc index b8a8c539251..6a0a177b10d 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc @@ -7,8 +7,8 @@ #include <lib/sys/cpp/component_context.h> #include <memory> -#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/process_context.h" #include "ui/ozone/platform/scenic/ozone_platform_scenic.h" namespace ui { @@ -33,7 +33,7 @@ std::unique_ptr<PlatformScreen> ScenicWindowManager::CreateScreen() { fuchsia::ui::scenic::Scenic* ScenicWindowManager::GetScenic() { if (!scenic_) { - scenic_ = base::fuchsia::ComponentContextForCurrentProcess() + scenic_ = base::ComponentContextForProcess() ->svc() ->Connect<fuchsia::ui::scenic::Scenic>(); scenic_.set_error_handler( diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc index a1a51db49d0..d320987b58e 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc @@ -116,7 +116,8 @@ bool SysmemBufferCollection::IsNativePixmapConfigSupported( format == gfx::BufferFormat::BGRX_8888; bool usage_supported = usage == gfx::BufferUsage::SCANOUT || usage == gfx::BufferUsage::SCANOUT_CPU_READ_WRITE || - usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE; + usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE || + usage == gfx::BufferUsage::GPU_READ; return format_supported && usage_supported; } @@ -142,7 +143,7 @@ bool SysmemBufferCollection::Initialize( if (vk_device == VK_NULL_HANDLE) return false; - size_ = size; + min_size_ = size; format_ = format; usage_ = usage; vk_device_ = vk_device; @@ -163,15 +164,24 @@ bool SysmemBufferCollection::Initialize( bool SysmemBufferCollection::Initialize( fuchsia::sysmem::Allocator_Sync* allocator, VkDevice vk_device, - zx::channel token_handle) { + zx::channel token_handle, + gfx::BufferFormat format, + gfx::BufferUsage usage, + bool force_protected) { DCHECK(!collection_); DCHECK(!vk_buffer_collection_); - usage_ = gfx::BufferUsage::GPU_READ; - vk_device_ = vk_device; + // Set nominal size of 1x1, which will be used only for + // vkSetBufferCollectionConstraintsFUCHSIA(). The actual size of the allocated + // buffers is determined by constraints set by other sysmem clients for the + // same collection. Size of the Vulkan image is determined by the valus passed + // to CreateVkImage(). + min_size_ = gfx::Size(1, 1); - // Assume that all imported collections are in NV12 format. - format_ = gfx::BufferFormat::YUV_420_BIPLANAR; + vk_device_ = vk_device; + format_ = format; + usage_ = usage; + is_protected_ = force_protected; fuchsia::sysmem::BufferCollectionTokenSyncPtr token; token.Bind(std::move(token_handle)); @@ -206,18 +216,12 @@ scoped_refptr<gfx::NativePixmap> SysmemBufferCollection::CreateNativePixmap( buffers_info_.settings.image_format_constraints; // The logic should match LogicalBufferCollection::Allocate(). - size_t width = - RoundUp(std::max(format.min_coded_width, format.required_max_coded_width), - format.coded_width_divisor); - size_t stride = - RoundUp(std::max(static_cast<size_t>(format.min_bytes_per_row), - gfx::RowSizeForBufferFormat(width, format_, 0)), - format.bytes_per_row_divisor); - size_t height = RoundUp( - std::max(format.min_coded_height, format.required_max_coded_height), - format.coded_height_divisor); + size_t stride = RoundUp( + std::max(static_cast<size_t>(format.min_bytes_per_row), + gfx::RowSizeForBufferFormat(image_size_.width(), format_, 0)), + format.bytes_per_row_divisor); size_t plane_offset = buffers_info_.buffers[buffer_index].vmo_usable_start; - size_t plane_size = stride * height; + size_t plane_size = stride * image_size_.height(); handle.planes.emplace_back(stride, plane_offset, plane_size, std::move(main_plane_vmo)); @@ -421,7 +425,7 @@ bool SysmemBufferCollection::InitializeInternal( ignore_result(token_channel.release()); VkImageCreateInfo image_create_info; - InitializeImageCreateInfo(&image_create_info, size_); + InitializeImageCreateInfo(&image_create_info, min_size_); if (vkSetBufferCollectionConstraintsFUCHSIA(vk_device_, vk_buffer_collection_, &image_create_info) != @@ -446,6 +450,16 @@ bool SysmemBufferCollection::InitializeInternal( DCHECK_GE(buffers_info_.buffer_count, buffers_for_camping); DCHECK(buffers_info_.settings.has_image_format_constraints); + // The logic should match LogicalBufferCollection::Allocate(). + const fuchsia::sysmem::ImageFormatConstraints& format = + buffers_info_.settings.image_format_constraints; + size_t width = + RoundUp(std::max(format.min_coded_width, format.required_max_coded_width), + format.coded_width_divisor); + size_t height = RoundUp( + std::max(format.min_coded_height, format.required_max_coded_height), + format.coded_height_divisor); + image_size_ = gfx::Size(width, height); buffer_size_ = buffers_info_.settings.buffer_settings.size_bytes; is_protected_ = buffers_info_.settings.buffer_settings.is_secure; @@ -469,7 +483,13 @@ void SysmemBufferCollection::InitializeImageCreateInfo( vk_image_info->samples = VK_SAMPLE_COUNT_1_BIT; vk_image_info->tiling = is_mappable() ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; + vk_image_info->usage = VK_IMAGE_USAGE_SAMPLED_BIT; + if (usage_ == gfx::BufferUsage::SCANOUT || + usage_ == gfx::BufferUsage::SCANOUT_CPU_READ_WRITE) { + vk_image_info->usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + vk_image_info->sharingMode = VK_SHARING_MODE_EXCLUSIVE; vk_image_info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; } diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h index 1bfed23b840..34628685327 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h @@ -48,7 +48,10 @@ class SysmemBufferCollection bool Initialize(fuchsia::sysmem::Allocator_Sync* allocator, VkDevice vk_device, - zx::channel token); + zx::channel token, + gfx::BufferFormat format, + gfx::BufferUsage usage, + bool force_protected); // Must not be called more than once. void SetOnDeletedCallback(base::OnceClosure on_deleted); @@ -70,7 +73,7 @@ class SysmemBufferCollection gfx::SysmemBufferCollectionId id() const { return id_; } size_t num_buffers() const { return buffers_info_.buffer_count; } - gfx::Size size() const { return size_; } + gfx::Size size() const { return image_size_; } gfx::BufferFormat format() const { return format_; } size_t buffer_size() const { return buffers_info_.settings.buffer_settings.size_bytes; @@ -96,10 +99,11 @@ class SysmemBufferCollection const gfx::SysmemBufferCollectionId id_; - gfx::Size size_; + // Image size passed to vkSetBufferCollectionConstraintsFUCHSIA(). The actual + // buffers size may be larger depending on constraints set by other + // sysmem clients. Size of the image is passed to CreateVkImage(). + gfx::Size min_size_; - // Valid only for owned buffer collections, i.e. those that that were - // initialized using the first Initialize() methods. gfx::BufferFormat format_ = gfx::BufferFormat::RGBA_8888; gfx::BufferUsage usage_ = gfx::BufferUsage::GPU_READ_CPU_READ_WRITE; @@ -118,6 +122,7 @@ class SysmemBufferCollection // threads. THREAD_CHECKER(vulkan_thread_checker_); + gfx::Size image_size_; size_t buffer_size_ = 0; bool is_protected_ = false; diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc index 2d0a2d5869a..b1bdfb23b55 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc @@ -51,9 +51,13 @@ scoped_refptr<SysmemBufferCollection> SysmemBufferManager::ImportSysmemBufferCollection( VkDevice vk_device, gfx::SysmemBufferCollectionId id, - zx::channel token) { + zx::channel token, + gfx::BufferFormat format, + gfx::BufferUsage usage, + bool force_protected) { auto result = base::MakeRefCounted<SysmemBufferCollection>(id); - if (!result->Initialize(allocator_.get(), vk_device, std::move(token))) { + if (!result->Initialize(allocator_.get(), vk_device, std::move(token), format, + usage, force_protected)) { return nullptr; } RegisterCollection(result.get()); diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h index b726855beb2..9bc6fe9bd35 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h @@ -46,7 +46,10 @@ class SysmemBufferManager { scoped_refptr<SysmemBufferCollection> ImportSysmemBufferCollection( VkDevice vk_device, gfx::SysmemBufferCollectionId id, - zx::channel token); + zx::channel token, + gfx::BufferFormat format, + gfx::BufferUsage usage, + bool force_protected); scoped_refptr<SysmemBufferCollection> GetCollectionById( gfx::SysmemBufferCollectionId id); diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc index ce74e4b9cf8..9152a959c73 100644 --- a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc @@ -269,7 +269,7 @@ VulkanImplementationScenic::CreateImageFromGpuMemoryHandle( auto image = gpu::VulkanImage::Create( device_queue, vk_image, vk_device_memory, size, vk_image_info.format, vk_image_info.tiling, vk_device_size, 0 /* memory_type_index */, - ycbcr_info); + ycbcr_info, vk_image_info.flags); if (image->format() != vk_format) { DLOG(ERROR) << "Unexpected format " << vk_format << " vs " @@ -298,10 +298,16 @@ std::unique_ptr<gpu::SysmemBufferCollection> VulkanImplementationScenic::RegisterSysmemBufferCollection( VkDevice device, gfx::SysmemBufferCollectionId id, - zx::channel token) { + zx::channel token, + gfx::BufferFormat format, + gfx::BufferUsage usage) { + // SCANOUT images must be protected in protected mode. + bool force_protected = + usage == gfx::BufferUsage::SCANOUT && enforce_protected_memory(); + return std::make_unique<SysmemBufferCollectionImpl>( - sysmem_buffer_manager_->ImportSysmemBufferCollection(device, id, - std::move(token))); + sysmem_buffer_manager_->ImportSysmemBufferCollection( + device, id, std::move(token), format, usage, force_protected)); } } // namespace ui diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h index 6b1a1afa1f1..cbc7e0d111b 100644 --- a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h +++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h @@ -56,7 +56,9 @@ class VulkanImplementationScenic : public gpu::VulkanImplementation { std::unique_ptr<gpu::SysmemBufferCollection> RegisterSysmemBufferCollection( VkDevice device, gfx::SysmemBufferCollectionId id, - zx::channel token) override; + zx::channel token, + gfx::BufferFormat format, + gfx::BufferUsage usage) override; private: ScenicSurfaceFactory* const scenic_surface_factory_; diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn index b0ec8a82a5e..e0d30f2575c 100644 --- a/chromium/ui/ozone/platform/wayland/BUILD.gn +++ b/chromium/ui/ozone/platform/wayland/BUILD.gn @@ -18,6 +18,8 @@ source_set("wayland") { sources = [ "client_native_pixmap_factory_wayland.cc", "client_native_pixmap_factory_wayland.h", + "common/data_util.cc", + "common/data_util.h", "common/wayland_object.cc", "common/wayland_object.h", "common/wayland_util.cc", @@ -41,8 +43,6 @@ source_set("wayland") { "host/gtk_primary_selection_device_manager.h", "host/gtk_primary_selection_offer.cc", "host/gtk_primary_selection_offer.h", - "host/gtk_primary_selection_source.cc", - "host/gtk_primary_selection_source.h", "host/shell_object_factory.cc", "host/shell_object_factory.h", "host/shell_popup_wrapper.cc", @@ -67,14 +67,14 @@ source_set("wayland") { "host/wayland_data_device_base.h", "host/wayland_data_device_manager.cc", "host/wayland_data_device_manager.h", + "host/wayland_data_drag_controller.cc", + "host/wayland_data_drag_controller.h", "host/wayland_data_offer.cc", "host/wayland_data_offer.h", "host/wayland_data_offer_base.cc", "host/wayland_data_offer_base.h", "host/wayland_data_source.cc", "host/wayland_data_source.h", - "host/wayland_data_source_base.cc", - "host/wayland_data_source_base.h", "host/wayland_drm.cc", "host/wayland_drm.h", "host/wayland_event_source.cc", @@ -105,10 +105,14 @@ source_set("wayland") { "host/wayland_subsurface.h", "host/wayland_surface.cc", "host/wayland_surface.h", + "host/wayland_toplevel_window.cc", + "host/wayland_toplevel_window.h", "host/wayland_touch.cc", "host/wayland_touch.h", "host/wayland_window.cc", "host/wayland_window.h", + "host/wayland_window_drag_controller.cc", + "host/wayland_window_drag_controller.h", "host/wayland_window_factory.cc", "host/wayland_window_manager.cc", "host/wayland_window_manager.h", @@ -140,6 +144,7 @@ source_set("wayland") { "//skia", "//third_party/wayland:wayland_client", "//third_party/wayland-protocols:gtk_primary_selection_protocol", + "//third_party/wayland-protocols:keyboard_extension_protocol", "//third_party/wayland-protocols:linux_dmabuf_protocol", "//third_party/wayland-protocols:presentation_time_protocol", "//third_party/wayland-protocols:text_input_protocol", @@ -147,6 +152,8 @@ source_set("wayland") { "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/base", "//ui/base:buildflags", + "//ui/base/cursor", + "//ui/base/cursor:cursor_base", "//ui/base/ime/linux", "//ui/events", "//ui/events:dom_keycode_converter", @@ -242,6 +249,8 @@ source_set("test_support") { "test/mock_zwp_linux_dmabuf.h", "test/mock_zwp_text_input.cc", "test/mock_zwp_text_input.h", + "test/scoped_wl_array.cc", + "test/scoped_wl_array.h", "test/server_object.cc", "test/server_object.h", "test/test_compositor.cc", @@ -306,12 +315,14 @@ source_set("wayland_unittests") { "gpu/wayland_surface_factory_unittest.cc", "host/wayland_connection_unittest.cc", "host/wayland_data_device_unittest.cc", + "host/wayland_data_drag_controller_unittest.cc", "host/wayland_event_source_unittest.cc", "host/wayland_input_method_context_unittest.cc", "host/wayland_keyboard_unittest.cc", "host/wayland_pointer_unittest.cc", "host/wayland_screen_unittest.cc", "host/wayland_touch_unittest.cc", + "host/wayland_window_drag_controller_unittest.cc", "host/wayland_window_manager_unittests.cc", "host/wayland_window_unittest.cc", "test/wayland_test.cc", @@ -326,11 +337,13 @@ source_set("wayland_unittests") { "//testing/gmock", "//testing/gtest", "//third_party/wayland:wayland_server", + "//third_party/wayland-protocols:keyboard_extension_protocol", "//third_party/wayland-protocols:linux_dmabuf_protocol", "//third_party/wayland-protocols:text_input_protocol", "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/base", "//ui/base:buildflags", + "//ui/base/cursor", "//ui/base/ime/linux", "//ui/events/ozone/layout", "//ui/gfx/linux:drm", diff --git a/chromium/ui/ozone/platform/wayland/DEPS b/chromium/ui/ozone/platform/wayland/DEPS index d61519af80c..44ef7beabfd 100644 --- a/chromium/ui/ozone/platform/wayland/DEPS +++ b/chromium/ui/ozone/platform/wayland/DEPS @@ -8,5 +8,5 @@ include_rules = [ "+ui/base/dragdrop/drag_drop_types.h", "+ui/base/dragdrop/file_info/file_info.h", "+ui/base/dragdrop/os_exchange_data.h", - "+ui/base/dragdrop/os_exchange_data_provider_aura.h", + "+ui/base/dragdrop/os_exchange_data_provider_non_backed.h", ] diff --git a/chromium/ui/ozone/platform/wayland/OWNERS b/chromium/ui/ozone/platform/wayland/OWNERS index ac018fd66db..1aa8e7e3f6f 100644 --- a/chromium/ui/ozone/platform/wayland/OWNERS +++ b/chromium/ui/ozone/platform/wayland/OWNERS @@ -1,2 +1,3 @@ msisov@igalia.com +nickdiego@igalia.com tonikitoo@igalia.com diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.cc b/chromium/ui/ozone/platform/wayland/common/data_util.cc new file mode 100644 index 00000000000..55e594a07e3 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/common/data_util.cc @@ -0,0 +1,218 @@ +// 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/common/data_util.h" + +#include <vector> + +#include "base/check.h" +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/dragdrop/file_info/file_info.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" +#include "ui/ozone/public/platform_clipboard.h" +#include "url/gurl.h" +#include "url/url_canon.h" +#include "url/url_util.h" + +namespace wl { + +namespace { + +using ui::OSExchangeData; +using ui::PlatformClipboard; + +constexpr ui::FilenameToURLPolicy kFilenameToURLPolicy = + ui::FilenameToURLPolicy::CONVERT_FILENAMES; + +// Converts mime type string to OSExchangeData::Format, if supported, otherwise +// 0 is returned. +int MimeTypeToFormat(const std::string& mime_type) { + if (mime_type == ui::kMimeTypeText || mime_type == ui::kMimeTypeTextUtf8) + return OSExchangeData::STRING; + if (mime_type == ui::kMimeTypeURIList) + return OSExchangeData::FILE_NAME; + if (mime_type == ui::kMimeTypeMozillaURL) + return OSExchangeData::URL; + if (mime_type == ui::kMimeTypeHTML) + return OSExchangeData::HTML; + return 0; +} + +// Converts raw data to either narrow or wide string. +template <typename StringType> +StringType BytesTo(const PlatformClipboard::Data& bytes) { + if (bytes.size() % sizeof(typename StringType::value_type) != 0U) { + // This is suspicious. + LOG(WARNING) + << "Data is possibly truncated, or a wrong conversion is requested."; + } + + StringType result; + result.assign(reinterpret_cast<typename StringType::const_pointer>(&bytes[0]), + bytes.size() / sizeof(typename StringType::value_type)); + return result; +} + +void AddString(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + if (data.empty()) + return; + + os_exchange_data->SetString(base::UTF8ToUTF16(BytesTo<std::string>(data))); +} + +void AddHtml(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + if (data.empty()) + return; + + os_exchange_data->SetHtml(base::UTF8ToUTF16(BytesTo<std::string>(data)), + GURL()); +} + +// Parses |data| as if it had text/uri-list format. Its brief spec is: +// 1. Any lines beginning with the '#' character are comment lines. +// 2. Non-comment lines shall be URIs (URNs or URLs). +// 3. Lines are terminated with a CRLF pair. +// 4. URL encoding is used. +void AddFiles(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + std::string data_as_string = BytesTo<std::string>(data); + + const auto lines = base::SplitString( + data_as_string, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + std::vector<ui::FileInfo> filenames; + for (const auto& line : lines) { + if (line.empty() || line[0] == '#') + continue; + GURL url(line); + if (!url.is_valid() || !url.SchemeIsFile()) { + LOG(WARNING) << "Invalid URI found: " << line; + continue; + } + + std::string url_path = url.path(); + url::RawCanonOutputT<base::char16> unescaped; + url::DecodeURLEscapeSequences(url_path.data(), url_path.size(), + url::DecodeURLMode::kUTF8OrIsomorphic, + &unescaped); + + std::string path8; + base::UTF16ToUTF8(unescaped.data(), unescaped.length(), &path8); + const base::FilePath path(path8); + filenames.push_back({path, path.BaseName()}); + } + if (filenames.empty()) + return; + + os_exchange_data->SetFilenames(filenames); +} + +// Parses |data| as if it had text/x-moz-url format, which is basically +// two lines separated with newline, where the first line is the URL and +// the second one is page title. The unpleasant feature of text/x-moz-url is +// that the URL has UTF-16 encoding. +void AddUrl(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + if (data.empty()) + return; + + base::string16 data_as_string16 = BytesTo<base::string16>(data); + + const auto lines = + base::SplitString(data_as_string16, base::ASCIIToUTF16("\r\n"), + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + if (lines.size() != 2U) { + LOG(WARNING) << "Invalid data passed as text/x-moz-url; it must contain " + << "exactly 2 lines but has " << lines.size() << " instead."; + return; + } + GURL url(lines[0]); + if (!url.is_valid()) { + LOG(WARNING) << "Invalid data passed as text/x-moz-url; the first line " + << "must contain a valid URL but it doesn't."; + return; + } + + os_exchange_data->SetURL(url, lines[1]); +} + +} // namespace + +bool IsMimeTypeSupported(const std::string& mime_type) { + return MimeTypeToFormat(mime_type) != 0; +} + +bool ContainsMimeType(const OSExchangeData& exchange_data, + const std::string& mime_type) { + DCHECK(IsMimeTypeSupported(mime_type)); + return exchange_data.HasAnyFormat(MimeTypeToFormat(mime_type), {}); +} + +void AddToOSExchangeData(const PlatformClipboard::Data& data, + const std::string& mime_type, + OSExchangeData* exchange_data) { + DCHECK(IsMimeTypeSupported(mime_type)); + DCHECK(exchange_data); + int format = MimeTypeToFormat(mime_type); + switch (format) { + case OSExchangeData::STRING: + AddString(data, exchange_data); + break; + case OSExchangeData::HTML: + AddHtml(data, exchange_data); + break; + case OSExchangeData::URL: + AddUrl(data, exchange_data); + break; + case OSExchangeData::FILE_NAME: + AddFiles(data, exchange_data); + break; + } +} + +bool ExtractOSExchangeData(const OSExchangeData& exchange_data, + const std::string& mime_type, + std::string* out_content) { + DCHECK(out_content); + DCHECK(IsMimeTypeSupported(mime_type)); + + if (mime_type == ui::kMimeTypeMozillaURL && + exchange_data.HasURL(kFilenameToURLPolicy)) { + GURL url; + base::string16 title; + exchange_data.GetURLAndTitle(kFilenameToURLPolicy, &url, &title); + out_content->append(url.spec()); + return true; + } + if (mime_type == ui::kMimeTypeHTML && exchange_data.HasHtml()) { + base::string16 data; + GURL base_url; + exchange_data.GetHtml(&data, &base_url); + out_content->append(base::UTF16ToUTF8(data)); + return true; + } + if (exchange_data.HasString()) { + base::string16 data; + exchange_data.GetString(&data); + out_content->append(base::UTF16ToUTF8(data)); + return true; + } + return false; +} + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.h b/chromium/ui/ozone/platform/wayland/common/data_util.h new file mode 100644 index 00000000000..d6c2d10e7f5 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/common/data_util.h @@ -0,0 +1,40 @@ +// 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_COMMON_DATA_UTIL_H_ +#define UI_OZONE_PLATFORM_WAYLAND_COMMON_DATA_UTIL_H_ + +#include <string> + +#include "ui/ozone/public/platform_clipboard.h" + +namespace ui { +class OSExchangeData; +} // namespace ui + +namespace wl { + +// Tells if |mime_type| is supported for Drag and Drop operations. +bool IsMimeTypeSupported(const std::string& mime_type); + +// Tells if |exchange_data| contains |mime_type| content. +bool ContainsMimeType(const ui::OSExchangeData& exchange_data, + const std::string& mime_type); + +// Add clipboard |data| content with |mime_type| format to the |exchange_data|. +// |mime_type| is assumed to be supported (See IsMimeTypeSupported for more). +void AddToOSExchangeData(const ui::PlatformClipboard::Data& data, + const std::string& mime_type, + ui::OSExchangeData* exchange_data); + +// Extract |exchange_data| of type |mime_type| and put it into |buffer|. If such +// mime type is not present, false is returned and |buffer| keeps untouched. +// |mime_type| is assumed to be supported (See IsMimeTypeSupported for more). +bool ExtractOSExchangeData(const ui::OSExchangeData& exchange_data, + const std::string& mime_type, + std::string* buffer); + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_DATA_UTIL_H_ diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc index d0db0924463..9f9efeae72e 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc +++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc @@ -5,6 +5,7 @@ #include "ui/ozone/platform/wayland/common/wayland_object.h" #include <gtk-primary-selection-client-protocol.h> +#include <keyboard-extension-unstable-v1-client-protocol.h> #include <linux-dmabuf-unstable-v1-client-protocol.h> #include <presentation-time-client-protocol.h> #include <text-input-unstable-v1-client-protocol.h> @@ -189,6 +190,16 @@ const wl_interface* ObjectTraits<xdg_positioner>::interface = void (*ObjectTraits<xdg_positioner>::deleter)(xdg_positioner*) = &xdg_positioner_destroy; +const wl_interface* ObjectTraits<zcr_keyboard_extension_v1>::interface = + &zcr_keyboard_extension_v1_interface; +void (*ObjectTraits<zcr_keyboard_extension_v1>::deleter)( + zcr_keyboard_extension_v1*) = &zcr_keyboard_extension_v1_destroy; + +const wl_interface* ObjectTraits<zcr_extended_keyboard_v1>::interface = + &zcr_extended_keyboard_v1_interface; +void (*ObjectTraits<zcr_extended_keyboard_v1>::deleter)( + zcr_extended_keyboard_v1*) = &zcr_extended_keyboard_v1_destroy; + const wl_interface* ObjectTraits<zwp_linux_dmabuf_v1>::interface = &zwp_linux_dmabuf_v1_interface; void (*ObjectTraits<zwp_linux_dmabuf_v1>::deleter)(zwp_linux_dmabuf_v1*) = diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.h b/chromium/ui/ozone/platform/wayland/common/wayland_object.h index 2d048ee3c5b..5c639f2db81 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_object.h +++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.h @@ -39,6 +39,8 @@ struct xdg_surface; struct xdg_toplevel; struct xdg_popup; struct xdg_positioner; +struct zcr_keyboard_extension_v1; +struct zcr_extended_keyboard_v1; struct zwp_linux_dmabuf_v1; struct zxdg_shell_v6; struct zxdg_surface_v6; @@ -246,6 +248,18 @@ struct ObjectTraits<xdg_positioner> { }; template <> +struct ObjectTraits<zcr_keyboard_extension_v1> { + static const wl_interface* interface; + static void (*deleter)(zcr_keyboard_extension_v1*); +}; + +template <> +struct ObjectTraits<zcr_extended_keyboard_v1> { + static const wl_interface* interface; + static void (*deleter)(zcr_extended_keyboard_v1*); +}; + +template <> struct ObjectTraits<zwp_linux_dmabuf_v1> { static const wl_interface* interface; static void (*deleter)(zwp_linux_dmabuf_v1*); diff --git a/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc b/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc index a049ee5be98..87ec9b08f00 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc @@ -7,6 +7,8 @@ #include <fcntl.h> #include <xf86drm.h> +#include "base/logging.h" + namespace ui { DrmRenderNodeHandle::DrmRenderNodeHandle() = default; diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index 95c840cbf77..734e8b2b699 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc @@ -18,12 +18,17 @@ namespace ui { namespace { -void WaitForFence(EGLDisplay display, EGLSyncKHR fence) { +void WaitForEGLFence(EGLDisplay display, EGLSyncKHR fence) { eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); eglDestroySyncKHR(display, fence); } +void WaitForGpuFences(std::vector<std::unique_ptr<gfx::GpuFence>> fences) { + for (auto& fence : fences) + fence->Wait(); +} + } // namespace GbmSurfacelessWayland::GbmSurfacelessWayland( @@ -41,7 +46,7 @@ GbmSurfacelessWayland::GbmSurfacelessWayland( void GbmSurfacelessWayland::QueueOverlayPlane(OverlayPlane plane, uint32_t buffer_id) { - planes_.push_back({std::move(plane), buffer_id}); + unsubmitted_frames_.back()->planes.push_back({std::move(plane), buffer_id}); } bool GbmSurfacelessWayland::ScheduleOverlayPlane( @@ -87,7 +92,8 @@ void GbmSurfacelessWayland::SwapBuffersAsync( TRACE_EVENT0("wayland", "GbmSurfacelessWayland::SwapBuffersAsync"); // If last swap failed, don't try to schedule new ones. if (!last_swap_buffers_result_) { - std::move(completion_callback).Run(gfx::SwapResult::SWAP_FAILED, nullptr); + std::move(completion_callback) + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED)); // Notify the caller, the buffer is never presented on a screen. std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure()); return; @@ -102,21 +108,35 @@ void GbmSurfacelessWayland::SwapBuffersAsync( PendingFrame* frame = unsubmitted_frames_.back().get(); frame->completion_callback = std::move(completion_callback); frame->presentation_callback = std::move(presentation_callback); + frame->ScheduleOverlayPlanes(widget_); + unsubmitted_frames_.push_back(std::make_unique<PendingFrame>()); - if (!use_egl_fence_sync_) { + if (!use_egl_fence_sync_ || !frame->schedule_planes_succeeded) { frame->ready = true; SubmitFrame(); return; } - // TODO: the following should be replaced by a per surface flush as it gets - // implemented in GL drivers. - EGLSyncKHR fence = InsertFence(has_implicit_external_sync_); - CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed"; + std::vector<std::unique_ptr<gfx::GpuFence>> fences; + // Uset in-fences provided in the overlays. If there are none, we insert our + // own fence and wait. + for (auto& plane : frame->planes) { + if (plane.plane.gpu_fence) + fences.push_back(std::move(plane.plane.gpu_fence)); + } + + base::OnceClosure fence_wait_task; + if (!fences.empty()) { + fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences)); + } else { + // TODO: the following should be replaced by a per surface flush as it gets + // implemented in GL drivers. + EGLSyncKHR fence = InsertFence(has_implicit_external_sync_); + CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed"; - base::OnceClosure fence_wait_task = - base::BindOnce(&WaitForFence, GetDisplay(), fence); + fence_wait_task = base::BindOnce(&WaitForEGLFence, GetDisplay(), fence); + } base::OnceClosure fence_retired_callback = base::BindOnce( &GbmSurfacelessWayland::FenceRetired, weak_factory_.GetWeakPtr(), frame); @@ -181,12 +201,14 @@ GbmSurfacelessWayland::PendingFrame::PendingFrame() {} GbmSurfacelessWayland::PendingFrame::~PendingFrame() {} -bool GbmSurfacelessWayland::PendingFrame::ScheduleOverlayPlanes( +void GbmSurfacelessWayland::PendingFrame::ScheduleOverlayPlanes( gfx::AcceleratedWidget widget) { - for (auto& overlay : overlays) + for (auto& overlay : overlays) { if (!overlay.ScheduleOverlayPlane(widget)) - return false; - return true; + return; + } + schedule_planes_succeeded = true; + return; } void GbmSurfacelessWayland::PendingFrame::Flush() { @@ -201,14 +223,11 @@ void GbmSurfacelessWayland::SubmitFrame() { submitted_frame_ = std::move(unsubmitted_frames_.front()); unsubmitted_frames_.erase(unsubmitted_frames_.begin()); - bool schedule_planes_succeeded = - submitted_frame_->ScheduleOverlayPlanes(widget_); - - if (!schedule_planes_succeeded) { + if (!submitted_frame_->schedule_planes_succeeded) { last_swap_buffers_result_ = false; std::move(submitted_frame_->completion_callback) - .Run(gfx::SwapResult::SWAP_FAILED, nullptr); + .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED)); // Notify the caller, the buffer is never presented on a screen. std::move(submitted_frame_->presentation_callback) .Run(gfx::PresentationFeedback::Failure()); @@ -217,11 +236,13 @@ void GbmSurfacelessWayland::SubmitFrame() { return; } - submitted_frame_->buffer_id = planes_.back().buffer_id; - buffer_manager_->CommitBuffer(widget_, submitted_frame_->buffer_id, + DCHECK_EQ(submitted_frame_->planes.size(), 1u); + submitted_frame_->buffer_id = submitted_frame_->planes.back().buffer_id; + buffer_manager_->CommitBuffer(widget_, + submitted_frame_->planes.back().buffer_id, submitted_frame_->damage_region_); - planes_.clear(); + submitted_frame_->planes.clear(); } } @@ -247,7 +268,8 @@ void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id, submitted_frame_->overlays.clear(); DCHECK_EQ(submitted_frame_->buffer_id, buffer_id); - std::move(submitted_frame_->completion_callback).Run(swap_result, nullptr); + std::move(submitted_frame_->completion_callback) + .Run(gfx::SwapCompletionResult(swap_result)); pending_presentation_frames_.push_back(std::move(submitted_frame_)); diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index 991d1718fad..137bea20432 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h @@ -71,11 +71,19 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, void OnPresentation(uint32_t buffer_id, const gfx::PresentationFeedback& feedback) override; + struct PlaneData { + OverlayPlane plane; + // The id of the buffer, which represents buffer that backs this overlay + // plane. + const uint32_t buffer_id; + }; + struct PendingFrame { PendingFrame(); ~PendingFrame(); - bool ScheduleOverlayPlanes(gfx::AcceleratedWidget widget); + // Queues overlay configs to |planes|. + void ScheduleOverlayPlanes(gfx::AcceleratedWidget widget); void Flush(); bool ready = false; @@ -90,11 +98,9 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, std::vector<gl::GLSurfaceOverlay> overlays; SwapCompletionCallback completion_callback; PresentationCallback presentation_callback; - }; - struct PlaneData { - OverlayPlane plane; - const uint32_t buffer_id; + bool schedule_planes_succeeded = false; + std::vector<PlaneData> planes; }; void SubmitFrame(); @@ -106,7 +112,6 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, void SetNoGLFlushForTests(); WaylandBufferManagerGpu* const buffer_manager_; - std::vector<PlaneData> planes_; // The native surface. Deleting this is allowed to free the EGLNativeWindow. gfx::AcceleratedWidget widget_; diff --git a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc index 60c70c7e82e..195d061926c 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc @@ -145,7 +145,8 @@ void GLSurfaceEglReadbackWayland::OnSubmission( in_flight_pixel_buffers_.pop_front(); DCHECK(!completion_callbacks_.empty()); - std::move(completion_callbacks_.front()).Run(swap_result, nullptr); + std::move(completion_callbacks_.front()) + .Run(gfx::SwapCompletionResult(swap_result)); completion_callbacks_.erase(completion_callbacks_.begin()); } diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc index 585e5666283..aa649253a68 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc @@ -21,6 +21,7 @@ #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/scoped_wl_array.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/test_zwp_linux_buffer_params.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" @@ -126,8 +127,7 @@ class CallbacksHelper { // way. void FinishSwapBuffersAsync(uint32_t local_swap_id, scoped_refptr<FakeGLImageNativePixmap> gl_image, - gfx::SwapResult result, - std::unique_ptr<gfx::GpuFence> gpu_fence) { + gfx::SwapCompletionResult result) { last_finish_swap_id_ = pending_local_swap_ids_.front(); pending_local_swap_ids_.pop(); diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc index 23e2eaf306f..1a68f9da34e 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc @@ -8,6 +8,7 @@ #include "ui/ozone/platform/wayland/host/gtk_primary_selection_offer.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" namespace ui { @@ -25,6 +26,14 @@ GtkPrimarySelectionDevice::GtkPrimarySelectionDevice( GtkPrimarySelectionDevice::~GtkPrimarySelectionDevice() = default; +void GtkPrimarySelectionDevice::SetSelectionSource( + GtkPrimarySelectionSource* source) { + DCHECK(source); + gtk_primary_selection_device_set_selection( + data_device_.get(), source->data_source(), connection()->serial()); + connection()->ScheduleFlush(); +} + // static void GtkPrimarySelectionDevice::OnDataOffer( void* data, diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h index fc567936fe3..4bb586bffc3 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h +++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h @@ -13,6 +13,7 @@ #include "base/macros.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" struct gtk_primary_selection_device; @@ -31,6 +32,8 @@ class GtkPrimarySelectionDevice : public WaylandDataDeviceBase { return data_device_.get(); } + void SetSelectionSource(GtkPrimarySelectionSource* source); + private: // gtk_primary_selection_device_listener callbacks static void OnDataOffer(void* data, diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc index 90a8225bd73..3d49d7a4576 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc @@ -6,33 +6,42 @@ #include <gtk-primary-selection-client-protocol.h> -#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h" +#include <memory> + +#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" namespace ui { GtkPrimarySelectionDeviceManager::GtkPrimarySelectionDeviceManager( gtk_primary_selection_device_manager* manager, WaylandConnection* connection) - : gtk_primary_selection_device_manager_(manager), connection_(connection) { + : device_manager_(manager), connection_(connection) { DCHECK(connection_); - DCHECK(gtk_primary_selection_device_manager_); + DCHECK(device_manager_); } GtkPrimarySelectionDeviceManager::~GtkPrimarySelectionDeviceManager() = default; -gtk_primary_selection_device* GtkPrimarySelectionDeviceManager::GetDevice() { +GtkPrimarySelectionDevice* GtkPrimarySelectionDeviceManager::GetDevice() { DCHECK(connection_->seat()); - return gtk_primary_selection_device_manager_get_device( - gtk_primary_selection_device_manager_.get(), connection_->seat()); + if (!device_) { + device_ = std::make_unique<GtkPrimarySelectionDevice>( + connection_, gtk_primary_selection_device_manager_get_device( + device_manager_.get(), connection_->seat())); + } + DCHECK(device_); + return device_.get(); } std::unique_ptr<GtkPrimarySelectionSource> -GtkPrimarySelectionDeviceManager::CreateSource() { - gtk_primary_selection_source* data_source = - gtk_primary_selection_device_manager_create_source( - gtk_primary_selection_device_manager_.get()); - return std::make_unique<GtkPrimarySelectionSource>(data_source, connection_); +GtkPrimarySelectionDeviceManager::CreateSource( + GtkPrimarySelectionSource::Delegate* delegate) { + auto* data_source = + gtk_primary_selection_device_manager_create_source(device_manager_.get()); + return std::make_unique<GtkPrimarySelectionSource>(data_source, connection_, + delegate); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h index ea05aa38882..b059c2c8621 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h @@ -7,34 +7,38 @@ #include <memory> -#include "base/macros.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" - -struct gtk_primary_selection_device_manager; -struct gtk_primary_selection_device; +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" namespace ui { -class GtkPrimarySelectionSource; +class GtkPrimarySelectionDevice; class WaylandConnection; class GtkPrimarySelectionDeviceManager { public: + using DataSource = GtkPrimarySelectionSource; + using DataDevice = GtkPrimarySelectionDevice; + GtkPrimarySelectionDeviceManager( gtk_primary_selection_device_manager* manager, WaylandConnection* connection); + GtkPrimarySelectionDeviceManager(const GtkPrimarySelectionDeviceManager&) = + delete; + GtkPrimarySelectionDeviceManager& operator=( + const GtkPrimarySelectionDeviceManager&) = delete; ~GtkPrimarySelectionDeviceManager(); - gtk_primary_selection_device* GetDevice(); - std::unique_ptr<GtkPrimarySelectionSource> CreateSource(); + GtkPrimarySelectionDevice* GetDevice(); + std::unique_ptr<GtkPrimarySelectionSource> CreateSource( + GtkPrimarySelectionSource::Delegate* delegate); private: - wl::Object<gtk_primary_selection_device_manager> - gtk_primary_selection_device_manager_; + wl::Object<gtk_primary_selection_device_manager> device_manager_; - WaylandConnection* connection_; + WaylandConnection* const connection_; - DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionDeviceManager); + std::unique_ptr<GtkPrimarySelectionDevice> device_; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc deleted file mode 100644 index 7160cdd83fa..00000000000 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc +++ /dev/null @@ -1,76 +0,0 @@ -// 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/gtk_primary_selection_source.h" - -#include <gtk-primary-selection-client-protocol.h> - -#include "base/check.h" -#include "base/files/file_util.h" -#include "ui/base/clipboard/clipboard_constants.h" -#include "ui/ozone/platform/wayland/host/wayland_connection.h" - -namespace ui { - -GtkPrimarySelectionSource::GtkPrimarySelectionSource( - gtk_primary_selection_source* data_source, - WaylandConnection* connection) - : data_source_(data_source), connection_(connection) { - DCHECK(connection_); - DCHECK(data_source_); - - static const struct gtk_primary_selection_source_listener - kDataSourceListener = {GtkPrimarySelectionSource::OnSend, - GtkPrimarySelectionSource::OnCancelled}; - gtk_primary_selection_source_add_listener(data_source_.get(), - &kDataSourceListener, this); -} - -GtkPrimarySelectionSource::~GtkPrimarySelectionSource() = default; - -// static -void GtkPrimarySelectionSource::OnSend(void* data, - gtk_primary_selection_source* source, - const char* mime_type, - int32_t fd) { - GtkPrimarySelectionSource* self = - static_cast<GtkPrimarySelectionSource*>(data); - std::string contents; - base::Optional<std::vector<uint8_t>> mime_data; - self->GetClipboardData(mime_type, &mime_data); - if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0) - self->GetClipboardData(kMimeTypeText, &mime_data); - contents.assign(mime_data->begin(), mime_data->end()); - bool result = - base::WriteFileDescriptor(fd, contents.data(), contents.length()); - DCHECK(result); - close(fd); -} - -// static -void GtkPrimarySelectionSource::OnCancelled( - void* data, - gtk_primary_selection_source* source) { - GtkPrimarySelectionSource* self = - static_cast<GtkPrimarySelectionSource*>(data); - self->connection_->clipboard()->DataSourceCancelled( - ClipboardBuffer::kSelection); -} - -void GtkPrimarySelectionSource::WriteToClipboard( - const PlatformClipboard::DataMap& data_map) { - for (const auto& data : data_map) { - gtk_primary_selection_source_offer(data_source_.get(), data.first.c_str()); - if (strcmp(data.first.c_str(), kMimeTypeText) == 0) - gtk_primary_selection_source_offer(data_source_.get(), kMimeTypeTextUtf8); - } - - gtk_primary_selection_device_set_selection( - connection_->primary_selection_device(), data_source_.get(), - connection_->serial()); - - connection_->ScheduleFlush(); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h deleted file mode 100644 index 994fffa25d3..00000000000 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h +++ /dev/null @@ -1,46 +0,0 @@ -// 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_GTK_PRIMARY_SELECTION_SOURCE_H_ -#define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_ - -#include "base/macros.h" -#include "ui/ozone/platform/wayland/common/wayland_object.h" -#include "ui/ozone/platform/wayland/host/wayland_data_source_base.h" -#include "ui/ozone/public/platform_clipboard.h" - -struct gtk_primary_selection_source; - -namespace ui { - -class WaylandConnection; - -class GtkPrimarySelectionSource : public WaylandDataSourceBase { - public: - // Takes ownership of data_source. - GtkPrimarySelectionSource(gtk_primary_selection_source* data_source, - WaylandConnection* connection); - ~GtkPrimarySelectionSource() override; - - void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override; - - private: - // gtk_primary_selection_source_listener callbacks - static void OnSend(void* data, - gtk_primary_selection_source* source, - const char* mime_type, - int32_t fd); - static void OnCancelled(void* data, gtk_primary_selection_source* source); - - // The gtk_primary_selection_source wrapped by this instance. - wl::Object<gtk_primary_selection_source> data_source_; - - WaylandConnection* connection_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionSource); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc index 57383be20da..dea71480eb6 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc +++ b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc @@ -4,6 +4,7 @@ #include "ui/ozone/platform/wayland/host/shell_object_factory.h" +#include "base/logging.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h" #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" @@ -44,4 +45,4 @@ std::unique_ptr<ShellPopupWrapper> ShellObjectFactory::CreateShellPopupWrapper( return nullptr; } -} // namespace ui
\ No newline at end of file +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc index 1dc1f4d4f76..f9f5aaa0825 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc +++ b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc @@ -4,6 +4,9 @@ #include "ui/ozone/platform/wayland/host/shell_popup_wrapper.h" +#include "base/check_op.h" +#include "base/notreached.h" + namespace ui { constexpr uint32_t kAnchorDefaultWidth = 1; 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 96c4099fdb8..8320574a540 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 @@ -16,6 +16,7 @@ #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_surface.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h" @@ -55,10 +56,10 @@ std::string NumberToString(uint32_t number) { class WaylandBufferManagerHost::Surface { public: - Surface(WaylandWindow* window, + Surface(WaylandSurface* wayland_surface, WaylandConnection* connection, WaylandBufferManagerHost* buffer_manager) - : window_(window), + : wayland_surface_(wayland_surface), connection_(connection), buffer_manager_(buffer_manager) {} ~Surface() = default; @@ -67,7 +68,7 @@ class WaylandBufferManagerHost::Surface { DCHECK(!pending_buffer_); // The window has already been destroyed. - if (!window_) + if (!wayland_surface_) return true; WaylandBuffer* buffer = GetBuffer(buffer_id); @@ -95,23 +96,9 @@ class WaylandBufferManagerHost::Surface { if (buffer->attached && !buffer->wl_buffer) return false; - // This request may come earlier than the Wayland compositor has imported a - // wl_buffer. Wait until the buffer is created. The wait takes place only - // once. Though, the case when a request to attach a buffer comes earlier - // than the wl_buffer is created does not happen often. 1) Depending on the - // zwp linux dmabuf protocol version, the wl_buffer can be created - // immediately without asynchronous wait 2) the wl_buffer can have been - // created by this time. - // - // Another case, which always happen is waiting until the frame callback is - // completed. Thus, wait here when the Wayland compositor fires the frame - // callback. - if (!buffer->wl_buffer || wl_frame_callback_) { - pending_buffer_ = buffer; - return true; - } - - return CommitBufferInternal(buffer); + pending_buffer_ = buffer; + MaybeProcessPendingBuffer(); + return true; } bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id) { @@ -140,7 +127,7 @@ class WaylandBufferManagerHost::Surface { // the client about successful swap. // If the window has already been destroyed, no need to complete the // submission. - if (buffer && !buffer->released && submitted_buffer_ && window_) + if (buffer && !buffer->released && submitted_buffer_ && wayland_surface_) CompleteSubmission(); if (prev_submitted_buffer_ == buffer) @@ -167,8 +154,8 @@ class WaylandBufferManagerHost::Surface { if (buffer->wl_buffer) SetupBufferReleaseListener(buffer); - if (pending_buffer_ == buffer && !wl_frame_callback_) - ProcessPendingBuffer(); + if (pending_buffer_ == buffer) + MaybeProcessPendingBuffer(); } void ClearState() { @@ -186,17 +173,23 @@ class WaylandBufferManagerHost::Surface { } void ResetSurfaceContents() { - if (!window_) + if (!wayland_surface_) return; - wl_surface_attach(window_->surface(), nullptr, 0, 0); - wl_surface_commit(window_->surface()); + 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; + // ResetSurfaceContents happens upon WaylandWindow::Hide call, which + // destroyes 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; + connection_->ScheduleFlush(); } @@ -207,8 +200,16 @@ class WaylandBufferManagerHost::Surface { bool HasBuffers() const { return !buffers_.empty(); } - void OnWindowRemoved() { window_ = nullptr; } - bool HasWindow() const { return !!window_; } + void OnWindowRemoved() { wayland_surface_ = nullptr; } + bool HasWindow() const { return !!wayland_surface_; } + + void OnWindowConfigured() { + if (configured_) + return; + + configured_ = true; + MaybeProcessPendingBuffer(); + } private: struct FeedbackInfo { @@ -226,7 +227,7 @@ class WaylandBufferManagerHost::Surface { using PresentationFeedbackQueue = std::vector<FeedbackInfo>; bool CommitBufferInternal(WaylandBuffer* buffer) { - DCHECK(buffer && window_); + DCHECK(buffer && wayland_surface_); DCHECK(!pending_buffer_); DCHECK(!submitted_buffer_); @@ -274,7 +275,7 @@ class WaylandBufferManagerHost::Surface { } void DamageBuffer(WaylandBuffer* buffer) { - DCHECK(window_); + DCHECK(wayland_surface_); gfx::Rect pending_damage_region = std::move(buffer->damage_region); // If the size of the damage region is empty, wl_surface_damage must be @@ -290,10 +291,10 @@ class WaylandBufferManagerHost::Surface { // 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(window_->surface(), pending_damage_region.x(), - pending_damage_region.y(), - pending_damage_region.width(), - pending_damage_region.height()); + 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) @@ -304,8 +305,9 @@ class WaylandBufferManagerHost::Surface { // 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 = window_->buffer_scale(); - wl_surface_damage(window_->surface(), pending_damage_region.x() / scale, + 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); @@ -313,31 +315,32 @@ class WaylandBufferManagerHost::Surface { } void AttachBuffer(WaylandBuffer* buffer) { - DCHECK(window_); + 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(window_->surface(), buffer->wl_buffer.get(), 0, 0); + wl_surface_attach(wayland_surface_->surface(), buffer->wl_buffer.get(), 0, + 0); } void CommitSurface() { - DCHECK(window_); - wl_surface_commit(window_->surface()); + DCHECK(wayland_surface_); + wl_surface_commit(wayland_surface_->surface()); } void SetupFrameCallback() { - DCHECK(window_); + DCHECK(wayland_surface_); static const wl_callback_listener frame_listener = { &Surface::FrameCallbackDone}; DCHECK(!wl_frame_callback_); - wl_frame_callback_.reset(wl_surface_frame(window_->surface())); + wl_frame_callback_.reset(wl_surface_frame(wayland_surface_->surface())); wl_callback_add_listener(wl_frame_callback_.get(), &frame_listener, this); } void SetupPresentationFeedback(uint32_t buffer_id) { - DCHECK(window_); + DCHECK(wayland_surface_); // Set up presentation feedback. if (!connection_->presentation()) return; @@ -348,7 +351,7 @@ class WaylandBufferManagerHost::Surface { feedback_queue_.push_back( {wl::Object<struct wp_presentation_feedback>(wp_presentation_feedback( - connection_->presentation(), window_->surface())), + connection_->presentation(), wayland_surface_->surface())), buffer_id, /*feedback=*/base::nullopt, /*submission_completed=*/false}); wp_presentation_feedback_add_listener( @@ -372,7 +375,7 @@ class WaylandBufferManagerHost::Surface { DCHECK(wl_frame_callback_.get() == callback); wl_frame_callback_.reset(); - ProcessPendingBuffer(); + MaybeProcessPendingBuffer(); } // wl_callback_listener @@ -438,13 +441,13 @@ class WaylandBufferManagerHost::Surface { prev_submitted_buffer_ = submitted_buffer_; submitted_buffer_ = nullptr; - if (!window_) + if (!wayland_surface_) return; // 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(window_->GetWidget(), id, + buffer_manager_->OnSubmission(wayland_surface_->GetRootWidget(), id, gfx::SwapResult::SWAP_ACK); // If presentation feedback is not supported, use a fake feedback. This @@ -452,7 +455,7 @@ class WaylandBufferManagerHost::Surface { if (!connection_->presentation()) { DCHECK(feedback_queue_.empty()); buffer_manager_->OnPresentation( - window_->GetWidget(), id, + wayland_surface_->GetWidget(), id, gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(), GetPresentationKindFlags(0))); } else { @@ -468,13 +471,20 @@ class WaylandBufferManagerHost::Surface { } void OnPresentation(struct wp_presentation_feedback* wp_presentation_feedback, - const gfx::PresentationFeedback& feedback) { + const gfx::PresentationFeedback& feedback, + bool discarded = false) { FeedbackInfo* feedback_info = nullptr; for (auto& info : feedback_queue_) { if (info.wp_presentation_feedback.get() == wp_presentation_feedback) { feedback_info = &info; break; - } else if (!info.feedback.has_value()) { // Feedback must come in order. + } else if (!info.feedback.has_value() && !discarded) { + // Feedback must come in order. However, if one of the feedbacks was + // discarded and the previous feedbacks haven't been received yet, don't + // mark previous feedbacks as failed as they will come later. For + // example, imagine you are waiting for f[0], f[1] and f[2]. f[2] gets + // discarded, previous ones mustn't be marked as failed as they will + // come later. info.feedback = gfx::PresentationFeedback::Failure(); } } @@ -499,15 +509,15 @@ class WaylandBufferManagerHost::Surface { // This function ensures that we send OnPresentation for each buffer that // already has had OnSubmission called for it (condition #2). void ProcessPresentationFeedbacks() { - if (!window_) + if (!wayland_surface_) return; while (!feedback_queue_.empty()) { const auto& info = feedback_queue_.front(); if (!info.submission_completed || !info.feedback.has_value()) break; - buffer_manager_->OnPresentation(window_->GetWidget(), info.buffer_id, - *info.feedback); + buffer_manager_->OnPresentation(wayland_surface_->GetWidget(), + info.buffer_id, *info.feedback); feedback_queue_.erase(feedback_queue_.begin()); } // This queue should be small - if not it's likely a bug. @@ -546,11 +556,31 @@ class WaylandBufferManagerHost::Surface { Surface* self = static_cast<Surface*>(data); DCHECK(self); self->OnPresentation(wp_presentation_feedback, - gfx::PresentationFeedback::Failure()); + gfx::PresentationFeedback::Failure(), + true /* discarded */); } - void ProcessPendingBuffer() { - if (!pending_buffer_ || !window_) + void MaybeProcessPendingBuffer() { + // There is nothing to process if there is no pending buffer or the window + // has been destroyed. + if (!pending_buffer_ || !wayland_surface_) + return; + + // This request may come earlier than the Wayland compositor has imported a + // wl_buffer. Wait until the buffer is created. The wait takes place only + // once. Though, the case when a request to attach a buffer comes earlier + // than the wl_buffer is created does not happen often. 1) Depending on the + // zwp linux dmabuf protocol version, the wl_buffer can be created + // immediately without asynchronous wait 2) the wl_buffer can have been + // created by this time. + // + // Another case, which always happen is waiting until the frame callback is + // completed. Thus, wait here when the Wayland compositor fires the frame + // callback. + // + // 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_) return; auto* buffer = pending_buffer_; @@ -562,7 +592,7 @@ class WaylandBufferManagerHost::Surface { // WaylandWindow. // Non-owned. The window this helper surface stores and submits buffers for. - const WaylandWindow* window_; + const WaylandSurface* wayland_surface_; // Non-owned pointer to the connection. WaylandConnection* const connection_; @@ -598,6 +628,11 @@ class WaylandBufferManagerHost::Surface { // a need to call submission callback manually. bool contents_reset_ = false; + // If WaylandWindow has never been configured, do not try to attach + // buffers to its surface. Otherwise, Wayland server will drop the connection + // and send an error - "The surface has never been configured.". + bool configured_ = false; + DISALLOW_COPY_AND_ASSIGN(Surface); }; @@ -619,7 +654,7 @@ WaylandBufferManagerHost::~WaylandBufferManagerHost() { void WaylandBufferManagerHost::OnWindowAdded(WaylandWindow* window) { DCHECK(window); surfaces_[window->GetWidget()] = - std::make_unique<Surface>(window, connection_, this); + std::make_unique<Surface>(window->wayland_surface(), connection_, this); } void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) { @@ -632,6 +667,13 @@ void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) { surfaces_.erase(it); } +void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) { + DCHECK(window); + auto it = surfaces_.find(window->GetWidget()); + DCHECK(it != surfaces_.end()); + it->second->OnWindowConfigured(); +} + void WaylandBufferManagerHost::SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_callback) { terminate_gpu_cb_ = std::move(terminate_callback); @@ -923,11 +965,10 @@ bool WaylandBufferManagerHost::ValidateBufferIdFromGpu(uint32_t buffer_id) { return true; } -bool WaylandBufferManagerHost::ValidateDataFromGpu( - const base::ScopedFD& fd, - size_t length, - const gfx::Size& size, - uint32_t buffer_id) { +bool WaylandBufferManagerHost::ValidateDataFromGpu(const base::ScopedFD& fd, + size_t length, + const gfx::Size& size, + uint32_t buffer_id) { if (!ValidateBufferIdFromGpu(buffer_id)) return false; @@ -952,21 +993,20 @@ bool WaylandBufferManagerHost::ValidateDataFromGpu( void WaylandBufferManagerHost::OnCreateBufferComplete( uint32_t buffer_id, wl::Object<struct wl_buffer> new_buffer) { - auto it = anonymous_buffers_.find(buffer_id); - // It might have already been destroyed or stored by any of the surfaces. - if (it != anonymous_buffers_.end()) { - it->second->wl_buffer = std::move(new_buffer); - } else { - for (auto& surface : surfaces_) { - if (surface.second->BufferExists(buffer_id)) { - surface.second.get()->AttachWlBuffer(buffer_id, - std::move(new_buffer)); - break; - } + auto it = anonymous_buffers_.find(buffer_id); + // It might have already been destroyed or stored by any of the surfaces. + if (it != anonymous_buffers_.end()) { + it->second->wl_buffer = std::move(new_buffer); + } else { + for (auto& surface : surfaces_) { + if (surface.second->BufferExists(buffer_id)) { + surface.second.get()->AttachWlBuffer(buffer_id, std::move(new_buffer)); + break; } } - // There is no need for the buffer anymore. Let it go out of the scope and - // be destroyed. + } + // There is no need for the buffer anymore. Let it go out of the scope and + // be destroyed. } void WaylandBufferManagerHost::OnSubmission( 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 c0c6809cad2..b467835f726 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 @@ -81,6 +81,7 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, // WaylandWindowObserver implements: void OnWindowAdded(WaylandWindow* window) override; void OnWindowRemoved(WaylandWindow* window) override; + void OnWindowConfigured(WaylandWindow* window) override; void SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_gpu_cb); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc index 11add83d1b0..a0e848a216d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc @@ -4,28 +4,137 @@ #include "ui/ozone/platform/wayland/host/wayland_clipboard.h" +#include <memory> #include <string> +#include "base/check.h" +#include "base/notreached.h" +#include "ui/base/clipboard/clipboard_buffer.h" +#include "ui/base/clipboard/clipboard_constants.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h" #include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h" -#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.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_source_base.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/public/platform_clipboard.h" + +namespace wl { + +// Internal Wayland Clipboard interface. A wl::Clipboard implementation handles +// a single ui::ClipboardBuffer. With this common interface it is possible to +// seamlessly support different clipboard buffers backed by different underlying +// Wayland protocol objects. +class Clipboard { + public: + virtual ~Clipboard() = default; + + // Synchronously retrieves the mime types list currently available to be read. + virtual std::vector<std::string> ReadMimeTypes() = 0; + + // Asynchronously reads clipboard content with |mime_type| format. The result + // data is expected to arrive through WaylandClipboard::SetData(). + // TODO(nickdiego): Decouple DataDevice impls from WaylandClipboard. + virtual bool Read(const std::string& mime_type) = 0; + + // Synchronously stores and announces |data| as available from this clipboard. + virtual void Write(const ui::PlatformClipboard::DataMap* data) = 0; + + // Tells if this clipboard instance is the current selection owner. + virtual bool IsSelectionOwner() const = 0; +}; + +// Templated wl::Clipboard implementation. Whereas DataSource is the data source +// class capable of creating data offers upon clipboard writes and communicates +// events through DataSource::Delegate, and DataDevice is its device counterpart +// providing read and write access to the underlying data selection-related +// protocol objects. See *_data_{source,device}.h for more details. +template <typename Manager, + typename DataSource = typename Manager::DataSource, + typename DataDevice = typename Manager::DataDevice> +class ClipboardImpl final : public Clipboard, public DataSource::Delegate { + public: + explicit ClipboardImpl(Manager* manager) : manager_(manager) {} + ClipboardImpl(const ClipboardImpl&) = delete; + ClipboardImpl& operator=(const ClipboardImpl&) = delete; + virtual ~ClipboardImpl() = default; + + virtual bool Read(const std::string& mime_type) override { + return GetDevice()->RequestSelectionData(mime_type); + } + + std::vector<std::string> ReadMimeTypes() override { + return GetDevice()->GetAvailableMimeTypes(); + } + + virtual void Write(const ui::PlatformClipboard::DataMap* data) override { + if (!data || data->empty()) { + data_.clear(); + source_.reset(); + } else { + data_ = *data; + if (!source_) + source_ = manager_->CreateSource(this); + source_->Offer(GetMimeTypes()); + GetDevice()->SetSelectionSource(source_.get()); + } + } + + bool IsSelectionOwner() const override { return !!source_; } + + private: + DataDevice* GetDevice() { return manager_->GetDevice(); } + + std::vector<std::string> GetMimeTypes() { + std::vector<std::string> mime_types; + for (const auto& data : data_) { + mime_types.push_back(data.first); + if (data.first == ui::kMimeTypeText) + mime_types.push_back(ui::kMimeTypeTextUtf8); + } + return mime_types; + } + + // WaylandDataSource::Delegate: + void OnDataSourceFinish(bool completed) override { + if (!completed) + Write(nullptr); + } + + void OnDataSourceSend(const std::string& mime_type, + std::string* contents) override { + DCHECK(contents); + auto it = data_.find(mime_type); + 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()); + } + + // The device manager used to access data device and create data sources. + Manager* const manager_; + + // The current data source used to offer clipboard data. + std::unique_ptr<DataSource> source_; + + // The data currently stored in a given clipboard buffer. + ui::PlatformClipboard::DataMap data_; +}; + +} // namespace wl namespace ui { -WaylandClipboard::WaylandClipboard( - WaylandDataDeviceManager* data_device_manager, - WaylandDataDevice* data_device, - GtkPrimarySelectionDeviceManager* primary_selection_device_manager, - GtkPrimarySelectionDevice* primary_selection_device) - : data_device_manager_(data_device_manager), - data_device_(data_device), - primary_selection_device_manager_(primary_selection_device_manager), - primary_selection_device_(primary_selection_device) { - DCHECK(data_device_manager_); - DCHECK(data_device_); +WaylandClipboard::WaylandClipboard(WaylandConnection* connection, + WaylandDataDeviceManager* manager) + : connection_(connection), + copypaste_clipboard_( + std::make_unique<wl::ClipboardImpl<WaylandDataDeviceManager>>( + manager)) { + DCHECK(manager); + DCHECK(connection_); + DCHECK(copypaste_clipboard_); } WaylandClipboard::~WaylandClipboard() = default; @@ -34,25 +143,8 @@ void WaylandClipboard::OfferClipboardData( ClipboardBuffer buffer, const PlatformClipboard::DataMap& data_map, PlatformClipboard::OfferDataClosure callback) { - WaylandDataSourceBase* data_source = nullptr; - if (buffer == ClipboardBuffer::kCopyPaste) { - if (!clipboard_data_source_) - clipboard_data_source_ = data_device_manager_->CreateSource(); - data_source = clipboard_data_source_.get(); - } else { - if (!IsPrimarySelectionSupported()) { - std::move(callback).Run(); - return; - } - if (!primary_data_source_) - primary_data_source_ = primary_selection_device_manager_->CreateSource(); - data_source = primary_data_source_.get(); - } - - DCHECK(data_source); - data_source->WriteToClipboard(data_map); - data_source->set_data_map(data_map); - + if (auto* clipboard = GetClipboard(buffer)) + clipboard->Write(&data_map); std::move(callback).Run(); } @@ -61,25 +153,18 @@ void WaylandClipboard::RequestClipboardData( const std::string& mime_type, PlatformClipboard::DataMap* data_map, PlatformClipboard::RequestDataClosure callback) { - read_clipboard_closure_ = std::move(callback); DCHECK(data_map); data_map_ = data_map; - if (buffer == ClipboardBuffer::kCopyPaste) { - if (!data_device_->RequestSelectionData(mime_type)) - SetData({}, mime_type); - } else { - if (!IsPrimarySelectionSupported() || - !primary_selection_device_->RequestSelectionData(mime_type)) { - SetData({}, mime_type); - } - } + read_clipboard_closure_ = std::move(callback); + auto* clipboard = GetClipboard(buffer); + if (!clipboard || !clipboard->Read(mime_type)) + SetData({}, mime_type); } bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) { - if (buffer == ClipboardBuffer::kCopyPaste) - return !!clipboard_data_source_; - else - return !!primary_data_source_; + if (auto* clipboard = GetClipboard(buffer)) + return clipboard->IsSelectionOwner(); + return false; } void WaylandClipboard::SetSequenceNumberUpdateCb( @@ -92,26 +177,10 @@ void WaylandClipboard::SetSequenceNumberUpdateCb( void WaylandClipboard::GetAvailableMimeTypes( ClipboardBuffer buffer, PlatformClipboard::GetMimeTypesClosure callback) { - if (buffer == ClipboardBuffer::kCopyPaste) { - std::move(callback).Run(data_device_->GetAvailableMimeTypes()); - } else { - std::move(callback).Run( - IsPrimarySelectionSupported() - ? primary_selection_device_->GetAvailableMimeTypes() - : std::vector<std::string>{}); - } -} - -void WaylandClipboard::DataSourceCancelled(ClipboardBuffer buffer) { - if (buffer == ClipboardBuffer::kCopyPaste) { - DCHECK(clipboard_data_source_); - SetData({}, {}); - clipboard_data_source_.reset(); - } else { - DCHECK(primary_data_source_); - SetData({}, {}); - primary_data_source_.reset(); - } + std::vector<std::string> mime_types; + if (auto* clipboard = GetClipboard(buffer)) + mime_types = clipboard->ReadMimeTypes(); + std::move(callback).Run(mime_types); } void WaylandClipboard::SetData(const std::vector<uint8_t>& contents, @@ -134,8 +203,24 @@ void WaylandClipboard::UpdateSequenceNumber(ClipboardBuffer buffer) { update_sequence_cb_.Run(buffer); } -bool WaylandClipboard::IsPrimarySelectionSupported() const { - return primary_selection_device_manager_ && primary_selection_device_; +wl::Clipboard* WaylandClipboard::GetClipboard(ClipboardBuffer buffer) { + if (buffer == ClipboardBuffer::kCopyPaste) + return copypaste_clipboard_.get(); + + if (buffer == ClipboardBuffer::kSelection) { + if (auto* manager = connection_->primary_selection_device_manager()) { + if (!primary_selection_clipboard_) { + primary_selection_clipboard_ = std::make_unique< + wl::ClipboardImpl<GtkPrimarySelectionDeviceManager>>(manager); + } + return primary_selection_clipboard_.get(); + } + // Primary selection extension not available. + return nullptr; + } + + NOTREACHED() << "Unsupported clipboard buffer: " << static_cast<int>(buffer); + return nullptr; } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h index a4482490071..d4641406e0c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h @@ -5,33 +5,36 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ +#include <memory> #include <string> #include <vector> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" -#include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "base/optional.h" +#include "ui/base/clipboard/clipboard_buffer.h" #include "ui/ozone/public/platform_clipboard.h" +namespace wl { +class Clipboard; +} // namespace wl + namespace ui { -class GtkPrimarySelectionDevice; -class GtkPrimarySelectionDeviceManager; -class GtkPrimarySelectionSource; -class WaylandDataDevice; +class WaylandConnection; class WaylandDataDeviceManager; // Handles clipboard operations. // -// Owned by WaylandConnection, which provides a data device and a data device -// manager. +// WaylandDataDeviceManager singleton is required to be up and running for +// WaylandClipboard to be minimally functional. class WaylandClipboard : public PlatformClipboard { public: - WaylandClipboard( - WaylandDataDeviceManager* data_device_manager, - WaylandDataDevice* data_device, - GtkPrimarySelectionDeviceManager* primary_selection_device_manager, - GtkPrimarySelectionDevice* primary_selection_device); + WaylandClipboard(WaylandConnection* connection, + WaylandDataDeviceManager* device_manager); + WaylandClipboard(const WaylandClipboard&) = delete; + WaylandClipboard& operator=(const WaylandClipboard&) = delete; ~WaylandClipboard() override; // PlatformClipboard. @@ -51,35 +54,34 @@ class WaylandClipboard : public PlatformClipboard { void SetSequenceNumberUpdateCb( PlatformClipboard::SequenceNumberUpdateCb cb) override; - void DataSourceCancelled(ClipboardBuffer buffer); + // 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 UpdateSequenceNumber(ClipboardBuffer buffer); private: - bool IsPrimarySelectionSupported() const; + // Get the wl::Clipboard instance owning a given |buffer|. Can return null in + // case |buffer| is unsupported. E.g: primary selection is not available. + wl::Clipboard* GetClipboard(ClipboardBuffer buffer); + + // WaylandConnection providing optional data device managers, e.g: gtk + // primary selection. + WaylandConnection* const connection_; // Holds a temporary instance of the client's clipboard content // so that we can asynchronously write to it. PlatformClipboard::DataMap* data_map_ = nullptr; - // Notifies whenever clipboard sequence number is changed. Can be empty if not - // set. + // Notifies whenever clipboard sequence number is changed. Can be empty if + // not set. PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_; // Stores the callback to be invoked upon data reading from clipboard. PlatformClipboard::RequestDataClosure read_clipboard_closure_; - std::unique_ptr<WaylandDataSource> clipboard_data_source_; - std::unique_ptr<GtkPrimarySelectionSource> primary_data_source_; - - // These four instances are owned by the connection. - WaylandDataDeviceManager* const data_device_manager_; - WaylandDataDevice* const data_device_; - GtkPrimarySelectionDeviceManager* const primary_selection_device_manager_; - GtkPrimarySelectionDevice* const primary_selection_device_; - - DISALLOW_COPY_AND_ASSIGN(WaylandClipboard); + const std::unique_ptr<wl::Clipboard> copypaste_clipboard_; + std::unique_ptr<wl::Clipboard> primary_selection_clipboard_; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc index d6c27854df9..0246bc58695 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc @@ -9,7 +9,7 @@ #include <algorithm> #include <memory> -#include <utility> +#include <vector> #include "base/bind.h" #include "base/logging.h" @@ -17,12 +17,16 @@ #include "base/message_loop/message_loop_current.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" -#include "mojo/public/cpp/system/platform_handle.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" -#include "ui/gfx/swap_result.h" +#include "ui/gfx/geometry/point.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" +#include "ui/ozone/platform/wayland/host/wayland_clipboard.h" #include "ui/ozone/platform/wayland/host/wayland_cursor.h" +#include "ui/ozone/platform/wayland/host/wayland_cursor_position.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_drm.h" #include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_input_method_context.h" @@ -32,6 +36,7 @@ #include "ui/ozone/platform/wayland/host/wayland_shm.h" #include "ui/ozone/platform/wayland/host/wayland_touch.h" #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_zwp_linux_dmabuf.h" namespace ui { @@ -39,6 +44,7 @@ namespace ui { namespace { constexpr uint32_t kMaxCompositorVersion = 4; constexpr uint32_t kMaxGtkPrimarySelectionDeviceManagerVersion = 1; +constexpr uint32_t kMaxKeyboardExtensionVersion = 1; constexpr uint32_t kMaxLinuxDmabufVersion = 3; constexpr uint32_t kMaxSeatVersion = 4; constexpr uint32_t kMaxShmVersion = 1; @@ -128,39 +134,10 @@ void WaylandConnection::SetCursorBitmap(const std::vector<SkBitmap>& bitmaps, cursor_->UpdateBitmap(bitmaps, location, serial_); } -void WaylandConnection::StartDrag(const ui::OSExchangeData& data, - int operation) { - if (!dragdrop_data_source_) - dragdrop_data_source_ = data_device_manager_->CreateSource(); - dragdrop_data_source_->Offer(data); - dragdrop_data_source_->SetAction(operation); - data_device_->StartDrag(dragdrop_data_source_->data_source(), data); -} - -void WaylandConnection::FinishDragSession(uint32_t dnd_action, - WaylandWindow* source_window) { - if (source_window) - source_window->OnDragSessionClose(dnd_action); - data_device_->ResetSourceData(); - dragdrop_data_source_.reset(); -} - -void WaylandConnection::DeliverDragData(const std::string& mime_type, - std::string* buffer) { - data_device_->DeliverDragData(mime_type, buffer); -} - -void WaylandConnection::RequestDragData( - const std::string& mime_type, - base::OnceCallback<void(const std::vector<uint8_t>&)> callback) { - data_device_->RequestDragData(mime_type, std::move(callback)); -} - -bool WaylandConnection::IsDragInProgress() { - // |data_device_| can be null when running on headless weston. - if (!data_device_) - return false; - return data_device_->IsDragEntered() || drag_data_source(); +bool WaylandConnection::IsDragInProgress() const { + // |data_drag_controller_| can be null when running on headless weston. + return data_drag_controller_ && data_drag_controller_->state() != + WaylandDataDragController::State::kIdle; } void WaylandConnection::Flush() { @@ -190,13 +167,8 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat, if (!has_keyboard) { keyboard_.reset(); - } else if (wl_keyboard* keyboard = wl_seat_get_keyboard(seat)) { - auto* layout_engine = - KeyboardLayoutEngineManager::GetKeyboardLayoutEngine(); - keyboard_ = std::make_unique<WaylandKeyboard>(keyboard, this, layout_engine, - event_source()); - } else { - LOG(ERROR) << "Failed to get wl_keyboard from seat"; + } else if (!CreateKeyboard()) { + LOG(ERROR) << "Failed to create WaylandKeyboard"; } if (!has_touch) { @@ -208,21 +180,34 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat, } } -void WaylandConnection::EnsureDataDevice() { - if (!data_device_manager_ || !seat_) - return; - DCHECK(!data_device_); - wl_data_device* data_device = data_device_manager_->GetDevice(); - data_device_ = std::make_unique<WaylandDataDevice>(this, data_device); +bool WaylandConnection::CreateKeyboard() { + wl_keyboard* keyboard = wl_seat_get_keyboard(seat_.get()); + if (!keyboard) + return false; - if (primary_selection_device_manager_) { - primary_selection_device_ = std::make_unique<GtkPrimarySelectionDevice>( - this, primary_selection_device_manager_->GetDevice()); - } + auto* layout_engine = KeyboardLayoutEngineManager::GetKeyboardLayoutEngine(); + // Make sure to destroy the old WaylandKeyboard (if it exists) before creating + // the new one. + keyboard_.reset(); + keyboard_.reset(new WaylandKeyboard(keyboard, keyboard_extension_v1_.get(), + this, layout_engine, event_source())); + return true; +} - clipboard_ = std::make_unique<WaylandClipboard>( - data_device_manager_.get(), data_device_.get(), - primary_selection_device_manager_.get(), primary_selection_device_.get()); +void WaylandConnection::CreateDataObjectsIfReady() { + if (data_device_manager_ && seat_) { + DCHECK(!data_drag_controller_); + data_drag_controller_ = std::make_unique<WaylandDataDragController>( + this, data_device_manager_.get()); + + DCHECK(!window_drag_controller_); + window_drag_controller_ = std::make_unique<WaylandWindowDragController>( + this, data_device_manager_.get(), event_source()); + + DCHECK(!clipboard_); + clipboard_ = + std::make_unique<WaylandClipboard>(this, data_device_manager_.get()); + } } // static @@ -268,7 +253,7 @@ void WaylandConnection::Global(void* data, return; } wl_seat_add_listener(connection->seat_.get(), &seat_listener, connection); - connection->EnsureDataDevice(); + connection->CreateDataObjectsIfReady(); } else if (!connection->shell_v6_ && strcmp(interface, "zxdg_shell_v6") == 0) { // Check for zxdg_shell_v6 first. @@ -323,7 +308,7 @@ void WaylandConnection::Global(void* data, connection->data_device_manager_ = std::make_unique<WaylandDataDeviceManager>( data_device_manager.release(), connection); - connection->EnsureDataDevice(); + connection->CreateDataObjectsIfReady(); } else if (!connection->primary_selection_device_manager_ && strcmp(interface, "gtk_primary_selection_device_manager") == 0) { wl::Object<gtk_primary_selection_device_manager> manager = @@ -343,6 +328,17 @@ void WaylandConnection::Global(void* data, (strcmp(interface, "wp_presentation") == 0)) { connection->presentation_ = wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion); + } else if (!connection->keyboard_extension_v1_ && + strcmp(interface, "zcr_keyboard_extension_v1") == 0) { + connection->keyboard_extension_v1_ = wl::Bind<zcr_keyboard_extension_v1>( + registry, name, kMaxKeyboardExtensionVersion); + if (!connection->keyboard_extension_v1_) { + LOG(ERROR) << "Failed to bind zcr_keyboard_extension_v1"; + return; + } + // CreateKeyboard may fail if we do not have keyboard seat capabilities yet. + // We will create the keyboard when get them in that case. + connection->CreateKeyboard(); } else if (!connection->text_input_manager_v1_ && strcmp(interface, "zwp_text_input_manager_v1") == 0) { connection->text_input_manager_v1_ = wl::Bind<zwp_text_input_manager_v1>( diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h index b3e0a8a6c3d..5c5754dd2fa 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h @@ -6,21 +6,19 @@ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CONNECTION_H_ #include <memory> -#include <string> #include <vector> -#include "ui/gfx/buffer_types.h" -#include "ui/gfx/native_widget_types.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" -#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h" -#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_clipboard.h" -#include "ui/ozone/platform/wayland/host/wayland_cursor_position.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/host/wayland_window_manager.h" +namespace gfx { +class Point; +} + namespace ui { class WaylandBufferManagerHost; @@ -32,8 +30,11 @@ class WaylandOutputManager; class WaylandPointer; class WaylandShm; class WaylandTouch; -class WaylandWindow; class WaylandZwpLinuxDmabuf; +class WaylandDataDeviceManager; +class WaylandCursorPosition; +class WaylandWindowDragController; +class GtkPrimarySelectionDeviceManager; class WaylandConnection { public: @@ -54,10 +55,6 @@ class WaylandConnection { xdg_wm_base* shell() const { return shell_.get(); } zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); } wl_seat* seat() const { return seat_.get(); } - wl_data_device* data_device() const { return data_device_->data_device(); } - gtk_primary_selection_device* primary_selection_device() const { - return primary_selection_device_->data_device(); - } wp_presentation* presentation() const { return presentation_.get(); } zwp_text_input_manager_v1* text_input_manager_v1() const { return text_input_manager_v1_.get(); @@ -82,10 +79,6 @@ class WaylandConnection { WaylandClipboard* clipboard() const { return clipboard_.get(); } - WaylandDataSource* drag_data_source() const { - return dragdrop_data_source_.get(); - } - WaylandOutputManager* wayland_output_manager() const { return wayland_output_manager_.get(); } @@ -109,35 +102,36 @@ class WaylandConnection { return &wayland_window_manager_; } - WaylandDataDevice* wayland_data_device() const { return data_device_.get(); } - - // Starts drag with |data| to be delivered, |operation| supported by the - // source side initiated the dragging. - void StartDrag(const ui::OSExchangeData& data, int operation); - // Finishes drag and drop session. It happens when WaylandDataSource gets - // 'OnDnDFinished' or 'OnCancel', which means the drop is performed or - // canceled on others. - void FinishDragSession(uint32_t dnd_action, WaylandWindow* source_window); - // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| - // is an output parameter and it should be filled with the data corresponding - // to mime_type. - void DeliverDragData(const std::string& mime_type, std::string* buffer); - // Requests the data to the platform when Chromium gets drag-and-drop started - // by others. Once reading the data from platform is done, |callback| should - // be called with the data. - void RequestDragData( - const std::string& mime_type, - base::OnceCallback<void(const std::vector<uint8_t>&)> callback); + WaylandDataDeviceManager* data_device_manager() const { + return data_device_manager_.get(); + } + + GtkPrimarySelectionDeviceManager* primary_selection_device_manager() const { + return primary_selection_device_manager_.get(); + } + + WaylandDataDragController* data_drag_controller() const { + return data_drag_controller_.get(); + } + + WaylandWindowDragController* window_drag_controller() const { + return window_drag_controller_.get(); + } // Returns true when dragging is entered or started. - bool IsDragInProgress(); + bool IsDragInProgress() const; private: void Flush(); void UpdateInputDevices(wl_seat* seat, uint32_t capabilities); - // Make sure data device is properly initialized - void EnsureDataDevice(); + // Initialize data-related objects if required protocol objects are already + // in place, i.e: wl_seat and wl_data_device_manager. + void CreateDataObjectsIfReady(); + + // Creates WaylandKeyboard with the currently acquired protocol objects, if + // possible. Returns true iff WaylandKeyboard was created. + bool CreateKeyboard(); // wl_registry_listener static void Global(void* data, @@ -166,10 +160,11 @@ class WaylandConnection { wl::Object<xdg_wm_base> shell_; wl::Object<zxdg_shell_v6> shell_v6_; 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_; - // Event source instance. Must be declared before input objects so it outlives - // them so thus being able to properly handle their destruction. + // Event source instance. Must be declared before input objects so it + // outlives them so thus being able to properly handle their destruction. std::unique_ptr<WaylandEventSource> event_source_; // Input device objects. @@ -179,9 +174,7 @@ class WaylandConnection { std::unique_ptr<WaylandCursor> cursor_; std::unique_ptr<WaylandDataDeviceManager> data_device_manager_; - std::unique_ptr<WaylandDataDevice> data_device_; std::unique_ptr<WaylandClipboard> clipboard_; - std::unique_ptr<WaylandDataSource> dragdrop_data_source_; std::unique_ptr<WaylandOutputManager> wayland_output_manager_; std::unique_ptr<WaylandCursorPosition> wayland_cursor_position_; std::unique_ptr<WaylandZwpLinuxDmabuf> zwp_dmabuf_; @@ -191,7 +184,9 @@ class WaylandConnection { std::unique_ptr<GtkPrimarySelectionDeviceManager> primary_selection_device_manager_; - std::unique_ptr<GtkPrimarySelectionDevice> primary_selection_device_; + + std::unique_ptr<WaylandDataDragController> data_drag_controller_; + std::unique_ptr<WaylandWindowDragController> window_drag_controller_; // Manages Wayland windows. WaylandWindowManager wayland_window_manager_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc index 0d495501f06..7ffd900fcae 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc @@ -7,6 +7,7 @@ #include <memory> #include <vector> +#include "base/logging.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/skia_util.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" 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 361ffa519f1..1edd356006b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc @@ -5,209 +5,62 @@ #include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include <memory> +#include <string> #include <utility> -#include <vector> #include "base/bind.h" -#include "base/strings/string16.h" -#include "base/strings/string_split.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "ui/base/clipboard/clipboard_constants.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/base/dragdrop/os_exchange_data_provider_aura.h" +#include "base/files/scoped_file.h" +#include "ui/base/clipboard/clipboard_buffer.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/ozone/platform/wayland/common/data_util.h" +#include "ui/ozone/platform/wayland/common/wayland_object.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_data_offer.h" -#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" -#include "url/gurl.h" -#include "url/url_canon.h" -#include "url/url_util.h" namespace ui { -namespace { - -constexpr FilenameToURLPolicy kFilenameToURLPolicy = CONVERT_FILENAMES; - -// Converts raw data to either narrow or wide string. -template <typename StringType> -StringType BytesTo(const PlatformClipboard::Data& bytes) { - if (bytes.size() % sizeof(typename StringType::value_type) != 0U) { - // This is suspicious. - LOG(WARNING) - << "Data is possibly truncated, or a wrong conversion is requested."; - } - - StringType result; - result.assign(reinterpret_cast<typename StringType::const_pointer>(&bytes[0]), - bytes.size() / sizeof(typename StringType::value_type)); - return result; -} - -// Returns actions possible with the given source and drag'n'drop actions. -// Also converts enums: input params are wl_data_device_manager_dnd_action but -// the result is ui::DragDropTypes. -int GetPossibleActions(uint32_t source_actions, uint32_t dnd_action) { - // If drag'n'drop action is set, use it but check for ASK action (see below). - uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE - ? dnd_action - : source_actions; - - // We accept any action except ASK (see below). - int operation = DragDropTypes::DRAG_NONE; - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) - operation |= DragDropTypes::DRAG_COPY; - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) - operation |= DragDropTypes::DRAG_MOVE; - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { - // This is very rare and non-standard. Chromium doesn't set this when - // anything is dragged from it, neither it provides any UI for asking - // the user about the desired drag'n'drop action when data is dragged - // from an external source. - // We are safe with not adding anything here. However, keep NOTIMPLEMENTED - // for an (unlikely) event of this being hit in distant future. - NOTIMPLEMENTED_LOG_ONCE(); - } - return operation; -} - -void AddString(const PlatformClipboard::Data& data, - OSExchangeData* os_exchange_data) { - DCHECK(os_exchange_data); - - if (data.empty()) - return; - - os_exchange_data->SetString(base::UTF8ToUTF16(BytesTo<std::string>(data))); -} - -void AddHtml(const PlatformClipboard::Data& data, - OSExchangeData* os_exchange_data) { - DCHECK(os_exchange_data); - - if (data.empty()) - return; - - os_exchange_data->SetHtml(base::UTF8ToUTF16(BytesTo<std::string>(data)), - GURL()); -} - -// Parses |data| as if it had text/uri-list format. Its brief spec is: -// 1. Any lines beginning with the '#' character are comment lines. -// 2. Non-comment lines shall be URIs (URNs or URLs). -// 3. Lines are terminated with a CRLF pair. -// 4. URL encoding is used. -void AddFiles(const PlatformClipboard::Data& data, - OSExchangeData* os_exchange_data) { - DCHECK(os_exchange_data); - - std::string data_as_string = BytesTo<std::string>(data); - - const auto lines = base::SplitString( - data_as_string, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - std::vector<FileInfo> filenames; - for (const auto& line : lines) { - if (line.empty() || line[0] == '#') - continue; - GURL url(line); - if (!url.is_valid() || !url.SchemeIsFile()) { - LOG(WARNING) << "Invalid URI found: " << line; - continue; - } - - std::string url_path = url.path(); - url::RawCanonOutputT<base::char16> unescaped; - url::DecodeURLEscapeSequences(url_path.data(), url_path.size(), - url::DecodeURLMode::kUTF8OrIsomorphic, - &unescaped); - - std::string path8; - base::UTF16ToUTF8(unescaped.data(), unescaped.length(), &path8); - const base::FilePath path(path8); - filenames.push_back({path, path.BaseName()}); - } - if (filenames.empty()) - return; - - os_exchange_data->SetFilenames(filenames); -} - -// Parses |data| as if it had text/x-moz-url format, which is basically -// two lines separated with newline, where the first line is the URL and -// the second one is page title. The unpleasant feature of text/x-moz-url is -// that the URL has UTF-16 encoding. -void AddUrl(const PlatformClipboard::Data& data, - OSExchangeData* os_exchange_data) { - DCHECK(os_exchange_data); - - if (data.empty()) - return; - - base::string16 data_as_string16 = BytesTo<base::string16>(data); - - const auto lines = - base::SplitString(data_as_string16, base::ASCIIToUTF16("\r\n"), - base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - if (lines.size() != 2U) { - LOG(WARNING) << "Invalid data passed as text/x-moz-url; it must contain " - << "exactly 2 lines but has " << lines.size() << " instead."; - return; - } - GURL url(lines[0]); - if (!url.is_valid()) { - LOG(WARNING) << "Invalid data passed as text/x-moz-url; the first line " - << "must contain a valid URL but it doesn't."; - return; - } - - os_exchange_data->SetURL(url, lines[1]); -} - -void AddToOSExchangeData(const PlatformClipboard::Data& data, - const std::string& mime_type, - OSExchangeData* os_exchange_data) { - DCHECK(os_exchange_data); - if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8)) { - DCHECK(!os_exchange_data->HasString()); - AddString(data, os_exchange_data); - } else if (mime_type == kMimeTypeHTML) { - DCHECK(!os_exchange_data->HasHtml()); - AddHtml(data, os_exchange_data); - } else if (mime_type == kMimeTypeMozillaURL) { - DCHECK(!os_exchange_data->HasURL(kFilenameToURLPolicy)); - AddUrl(data, os_exchange_data); - } else if (mime_type == kMimeTypeURIList) { - DCHECK(!os_exchange_data->HasFile()); - AddFiles(data, os_exchange_data); - } else { - LOG(WARNING) << "Unhandled MIME type: " << mime_type; - } -} - -} // namespace - -// static WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device) : WaylandDataDeviceBase(connection), data_device_(data_device) { static const struct wl_data_device_listener kDataDeviceListener = { - WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter, - WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion, - WaylandDataDevice::OnDrop, WaylandDataDevice::OnSelection}; + WaylandDataDevice::OnOffer, WaylandDataDevice::OnEnter, + WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion, + WaylandDataDevice::OnDrop, WaylandDataDevice::OnSelection}; wl_data_device_add_listener(data_device_.get(), &kDataDeviceListener, this); } WaylandDataDevice::~WaylandDataDevice() = default; -void WaylandDataDevice::RequestDragData( - const std::string& mime_type, - base::OnceCallback<void(const PlatformClipboard::Data&)> callback) { - base::ScopedFD fd = drag_offer_->Receive(mime_type); +void WaylandDataDevice::StartDrag(const WaylandDataSource& data_source, + const WaylandWindow& origin_window, + wl_surface* icon_surface, + DragDelegate* delegate) { + DCHECK(delegate); + DCHECK(!drag_delegate_); + drag_delegate_ = delegate; + + wl_data_device_start_drag(data_device_.get(), data_source.data_source(), + origin_window.surface(), icon_surface, + connection()->serial()); + drag_delegate_->DrawIcon(); + connection()->ScheduleFlush(); +} + +void WaylandDataDevice::ResetDragDelegate() { + DCHECK(drag_delegate_); + drag_delegate_ = nullptr; +} + +void WaylandDataDevice::RequestData(WaylandDataOffer* offer, + const std::string& mime_type, + RequestDataCallback callback) { + DCHECK(offer); + DCHECK(wl::IsMimeTypeSupported(mime_type)); + + base::ScopedFD fd = offer->Receive(mime_type); if (!fd.is_valid()) { LOG(ERROR) << "Failed to open file descriptor."; return; @@ -221,55 +74,13 @@ void WaylandDataDevice::RequestDragData( RegisterDeferredReadCallback(); } -void WaylandDataDevice::DeliverDragData(const std::string& mime_type, - std::string* buffer) { - DCHECK(buffer); - DCHECK(source_data_); - - if (mime_type == kMimeTypeMozillaURL && - source_data_->HasURL(kFilenameToURLPolicy)) { - GURL url; - base::string16 title; - source_data_->GetURLAndTitle(kFilenameToURLPolicy, &url, &title); - buffer->append(url.spec()); - } else if (mime_type == kMimeTypeHTML && source_data_->HasHtml()) { - base::string16 data; - GURL base_url; - source_data_->GetHtml(&data, &base_url); - buffer->append(base::UTF16ToUTF8(data)); - } else if (source_data_->HasString()) { - base::string16 data; - source_data_->GetString(&data); - buffer->append(base::UTF16ToUTF8(data)); - } else { - LOG(WARNING) << "Cannot deliver data of type " << mime_type - << " and no text representation is available."; - } -} - -void WaylandDataDevice::StartDrag(wl_data_source* data_source, - const ui::OSExchangeData& data) { - DCHECK(data_source); - - WaylandWindow* window = - connection()->wayland_window_manager()->GetCurrentFocusedWindow(); - if (!window) { - LOG(ERROR) << "Failed to get focused window."; - return; - } - const SkBitmap* icon = PrepareDragIcon(data); - source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone()); - wl_data_device_start_drag(data_device_.get(), data_source, window->surface(), - icon_surface_.get(), connection()->serial()); - if (icon) - DrawDragIcon(icon); +void WaylandDataDevice::SetSelectionSource(WaylandDataSource* source) { + DCHECK(source); + wl_data_device_set_selection(data_device_.get(), source->data_source(), + connection()->serial()); connection()->ScheduleFlush(); } -void WaylandDataDevice::ResetSourceData() { - source_data_.reset(); -} - void WaylandDataDevice::ReadDragDataFromFD( base::ScopedFD fd, base::OnceCallback<void(const PlatformClipboard::Data&)> callback) { @@ -278,17 +89,10 @@ void WaylandDataDevice::ReadDragDataFromFD( std::move(callback).Run(contents); } -void WaylandDataDevice::HandleDeferredLeaveIfNeeded() { - if (!is_leaving_) - return; - - OnLeave(this, data_device_.get()); -} - // static -void WaylandDataDevice::OnDataOffer(void* data, - wl_data_device* data_device, - wl_data_offer* offer) { +void WaylandDataDevice::OnOffer(void* data, + wl_data_device* data_device, + wl_data_offer* offer) { auto* self = static_cast<WaylandDataDevice*>(data); self->connection()->clipboard()->UpdateSequenceNumber( @@ -305,40 +109,24 @@ void WaylandDataDevice::OnEnter(void* data, wl_fixed_t x, wl_fixed_t y, wl_data_offer* offer) { - WaylandWindow* window = - static_cast<WaylandWindow*>(wl_surface_get_user_data(surface)); + WaylandWindow* window = WaylandWindow::FromSurface(surface); if (!window) { LOG(ERROR) << "Failed to get window."; return; } auto* self = static_cast<WaylandDataDevice*>(data); + + // Null |drag_delegate_| here means that the DND session has been initiated by + // an external application. In this case, use the default data drag delegate. + if (!self->drag_delegate_) + self->drag_delegate_ = self->connection()->data_drag_controller(); + DCHECK(self->new_offer_); - DCHECK(!self->drag_offer_); - self->drag_offer_ = std::move(self->new_offer_); - self->window_ = window; - - // TODO(crbug.com/1004715): Set mime type the client can accept. Now it sets - // all mime types offered because current implementation doesn't decide - // action based on mime type. - self->unprocessed_mime_types_.clear(); - for (auto mime : self->drag_offer_->mime_types()) { - self->unprocessed_mime_types_.push_back(mime); - self->drag_offer_->Accept(serial, mime); - } + self->drag_delegate_->OnDragOffer(std::move(self->new_offer_)); gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); - - // If |source_data_| is set, it means that dragging is started from the - // same window and it's not needed to read data through Wayland. - std::unique_ptr<OSExchangeData> dragged_data; - if (!self->IsDraggingExternalData()) - dragged_data = std::make_unique<OSExchangeData>( - self->source_data_->provider().Clone()); - self->window_->OnDragEnter( - point, std::move(dragged_data), - GetPossibleActions(self->drag_offer_->source_actions(), - self->drag_offer_->dnd_action())); + self->drag_delegate_->OnDragEnter(window, point, serial); } void WaylandDataDevice::OnMotion(void* data, @@ -347,65 +135,31 @@ void WaylandDataDevice::OnMotion(void* data, wl_fixed_t x, wl_fixed_t y) { auto* self = static_cast<WaylandDataDevice*>(data); - if (!self->window_) { - LOG(ERROR) << "Failed to get window."; - return; + if (self->drag_delegate_) { + gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); + self->drag_delegate_->OnDragMotion(point); } - - gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); - int client_operation = self->window_->OnDragMotion( - point, time, - GetPossibleActions(self->drag_offer_->source_actions(), - self->drag_offer_->dnd_action())); - self->SetOperation(client_operation); } void WaylandDataDevice::OnDrop(void* data, wl_data_device* data_device) { auto* self = static_cast<WaylandDataDevice*>(data); - if (!self->window_) { - LOG(ERROR) << "Failed to get window."; - return; - } - if (self->IsDraggingExternalData()) { - // We are about to accept data dragged from another application. - // Reading all the data may take some time so we set - // |is_handling_dropped_data_| that will postpone handling of OnLeave - // until reading is completed. - self->is_handling_dropped_data_ = true; - self->received_data_ = std::make_unique<OSExchangeData>( - std::make_unique<OSExchangeDataProviderAura>()); - self->HandleUnprocessedMimeTypes(); - } else { - // If the drag session had been started internally by chromium, - // |source_data_| already holds the data, and it is already forwarded to the - // delegate through OnDragEnter, so here we short-cut the data transfer by - // sending nullptr. - self->HandleReceivedData(nullptr); - } + if (self->drag_delegate_) + self->drag_delegate_->OnDragDrop(); } void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) { - // While reading data, it could get OnLeave event. We don't handle OnLeave - // event directly if |is_handling_dropped_data_| is set. auto* self = static_cast<WaylandDataDevice*>(data); - if (!self->window_) { - LOG(ERROR) << "Failed to get window."; - return; - } + if (self->drag_delegate_) { + self->drag_delegate_->OnDragLeave(); - if (self->is_handling_dropped_data_) { - self->is_leaving_ = true; - return; + // 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; } - - self->window_->OnDragLeave(); - self->window_ = nullptr; - self->drag_offer_.reset(); - self->is_handling_dropped_data_ = false; - self->is_leaving_ = false; } -// static void WaylandDataDevice::OnSelection(void* data, wl_data_device* data_device, wl_data_offer* offer) { @@ -428,106 +182,4 @@ void WaylandDataDevice::OnSelection(void* data, self->data_offer()->EnsureTextMimeTypeIfNeeded(); } -const SkBitmap* WaylandDataDevice::PrepareDragIcon(const OSExchangeData& data) { - const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap(); - if (!icon_bitmap || icon_bitmap->empty()) - return nullptr; - icon_surface_.reset(wl_compositor_create_surface(connection()->compositor())); - DCHECK(icon_surface_); - return icon_bitmap; -} - -void WaylandDataDevice::DrawDragIcon(const SkBitmap* icon_bitmap) { - DCHECK(icon_bitmap); - DCHECK(!icon_bitmap->empty()); - gfx::Size size(icon_bitmap->width(), icon_bitmap->height()); - - if (!shm_buffer_ || shm_buffer_->size() != size) { - shm_buffer_ = std::make_unique<WaylandShmBuffer>(connection()->shm(), size); - if (!shm_buffer_->IsValid()) { - LOG(ERROR) << "Failed to create drag icon buffer."; - return; - } - } - wl::DrawBitmap(*icon_bitmap, shm_buffer_.get()); - - wl_surface* surface = icon_surface_.get(); - wl_surface_attach(surface, shm_buffer_->get(), 0, 0); - wl_surface_damage(surface, 0, 0, size.width(), size.height()); - wl_surface_commit(surface); -} - -void WaylandDataDevice::HandleUnprocessedMimeTypes() { - std::string mime_type = SelectNextMimeType(); - if (mime_type.empty()) { - HandleReceivedData(std::move(received_data_)); - } else { - RequestDragData(mime_type, - base::BindOnce(&WaylandDataDevice::OnDragDataReceived, - base::Unretained(this))); - } -} - -void WaylandDataDevice::OnDragDataReceived( - const PlatformClipboard::Data& contents) { - if (!contents.empty()) { - AddToOSExchangeData(contents, unprocessed_mime_types_.front(), - received_data_.get()); - } - - unprocessed_mime_types_.pop_front(); - - // Continue reading data for other negotiated mime types. - HandleUnprocessedMimeTypes(); -} - -void WaylandDataDevice::HandleReceivedData( - std::unique_ptr<ui::OSExchangeData> received_data) { - unprocessed_mime_types_.clear(); - - window_->OnDragDrop(std::move(received_data)); - drag_offer_->FinishOffer(); - is_handling_dropped_data_ = false; - HandleDeferredLeaveIfNeeded(); -} - -std::string WaylandDataDevice::SelectNextMimeType() { - while (!unprocessed_mime_types_.empty()) { - const std::string& mime_type = unprocessed_mime_types_.front(); - if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8) && - !received_data_->HasString()) { - return mime_type; - } - if (mime_type == kMimeTypeURIList && !received_data_->HasFile()) { - return mime_type; - } - if (mime_type == kMimeTypeMozillaURL && - !received_data_->HasURL(kFilenameToURLPolicy)) { - return mime_type; - } - if (mime_type == kMimeTypeHTML && !received_data_->HasHtml()) { - return mime_type; - } - unprocessed_mime_types_.pop_front(); - } - return {}; -} - -void WaylandDataDevice::SetOperation(const int operation) { - uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - - if (operation & DragDropTypes::DRAG_COPY) { - dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - } - - if (operation & DragDropTypes::DRAG_MOVE) { - dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) - preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - } - drag_offer_->SetAction(dnd_actions, preferred_action); -} - } // namespace ui 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 cf2e8cc7a65..5512f4c4480 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h @@ -7,73 +7,84 @@ #include <wayland-client.h> -#include <list> +#include <cstdint> #include <memory> #include <string> #include "base/callback.h" #include "base/files/scoped_file.h" -#include "base/macros.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" #include "ui/ozone/public/platform_clipboard.h" -class SkBitmap; +namespace gfx { +class PointF; +} // namespace gfx namespace ui { -class OSExchangeData; class WaylandDataOffer; class WaylandConnection; -class WaylandShmBuffer; class WaylandWindow; // This class provides access to inter-client data transfer mechanisms // such as copy-and-paste and drag-and-drop mechanisms. class WaylandDataDevice : public WaylandDataDeviceBase { public: + using RequestDataCallback = + base::OnceCallback<void(const PlatformClipboard::Data&)>; + + // DragDelegate is responsible for handling drag and drop sessions. + class DragDelegate { + public: + virtual bool IsDragSource() const = 0; + virtual void DrawIcon() = 0; + virtual void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) = 0; + virtual void OnDragEnter(WaylandWindow* window, + const gfx::PointF& location, + uint32_t serial) = 0; + virtual void OnDragMotion(const gfx::PointF& location) = 0; + virtual void OnDragLeave() = 0; + virtual void OnDragDrop() = 0; + + protected: + virtual ~DragDelegate() = default; + }; + WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device); + WaylandDataDevice(const WaylandDataDevice&) = delete; + WaylandDataDevice& operator=(const WaylandDataDevice&) = delete; ~WaylandDataDevice() override; - // Requests the data to the platform when Chromium gets drag-and-drop started - // by others. Once reading the data from platform is done, |callback| should - // be called with the data. - void RequestDragData( - const std::string& mime_type, - base::OnceCallback<void(const PlatformClipboard::Data&)> callback); - // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| - // is an output parameter and it should be filled with the data corresponding - // to mime_type. - void DeliverDragData(const std::string& mime_type, std::string* buffer); - // Starts drag with |data| to be delivered, |operation| supported by the - // source side initiated the dragging. - void StartDrag(wl_data_source* data_source, const ui::OSExchangeData& data); - // Resets |source_data_| when the dragging is finished. - void ResetSourceData(); - - wl_data_device* data_device() const { return data_device_.get(); } + // Starts a wayland drag and drop session, controlled by |delegate|. + void StartDrag(const WaylandDataSource& data_source, + const WaylandWindow& origin_window, + wl_surface* icon_surface, + DragDelegate* delegate); - bool IsDragEntered() { return drag_offer_ != nullptr; } + // Reset the drag delegate, assuming there is one set. Any wl_data_device + // event received after this will be ignored until a new delegate is set. + void ResetDragDelegate(); - WaylandWindow* entered_window() const { return window_; } + // Requests data for an |offer| in a format specified by |mime_type|. The + // transfer happens asynchronously and |callback| is called when it is done. + void RequestData(WaylandDataOffer* offer, + const std::string& mime_type, + RequestDataCallback callback); - private: - void ReadDragDataFromFD( - base::ScopedFD fd, - base::OnceCallback<void(const PlatformClipboard::Data&)> callback); + // Returns the underlying wl_data_device singleton object. + wl_data_device* data_device() const { return data_device_.get(); } - // If source_data_ is not set, data is being dragged from an external - // application (non-chromium). - bool IsDraggingExternalData() const { return !source_data_; } + void SetSelectionSource(WaylandDataSource* source); - // If OnLeave event occurs while it's reading drag data, it defers handling - // it. Once reading data is completed, it's handled. - void HandleDeferredLeaveIfNeeded(); + private: + void ReadDragDataFromFD(base::ScopedFD fd, RequestDataCallback callback); // wl_data_device_listener callbacks - static void OnDataOffer(void* data, - wl_data_device* data_device, - wl_data_offer* id); + static void OnOffer(void* data, + wl_data_device* data_device, + wl_data_offer* id); static void OnEnter(void* data, wl_data_device* data_device, @@ -101,60 +112,18 @@ class WaylandDataDevice : public WaylandDataDeviceBase { wl_data_device* data_device, wl_data_offer* id); - // Returns the drag icon bitmap and creates and wayland surface to draw it - // on, if a valid drag image is present in |data|; otherwise returns null. - const SkBitmap* PrepareDragIcon(const OSExchangeData& data); - void DrawDragIcon(const SkBitmap* bitmap); - - void OnDragDataReceived(const PlatformClipboard::Data& contents); - - // HandleUnprocessedMimeTypes asynchronously request and read data for every - // negotiated mime type, one after another (OnDragDataReceived calls back - // HandleUnprocessedMimeTypes so it finish only when there's no more items in - // unprocessed_mime_types_ vector). HandleReceivedData is called once the - // process is finished. - void HandleUnprocessedMimeTypes(); - void HandleReceivedData(std::unique_ptr<ui::OSExchangeData> received_data); - // Returns the next MIME type to be received from the source process, or an - // empty string if there are no more interesting MIME types left to process. - std::string SelectNextMimeType(); - - // Set drag operation decided by client. - void SetOperation(const int operation); - // The wl_data_device wrapped by this WaylandDataDevice. wl::Object<wl_data_device> data_device_; + DragDelegate* drag_delegate_ = nullptr; + // There are two separate data offers at a time, the drag offer and the // selection offer, each with independent lifetimes. When we receive a new - // offer, it is not immediately possible to know whether the new offer is the - // drag offer or the selection offer. This variable is used to store ownership - // of new data offers temporarily until its identity becomes known. + // offer, it is not immediately possible to know whether the new offer is + // the drag offer or the selection offer. This variable is used to store + // new data offers temporarily until it is possible to determine which kind + // session it belongs to. std::unique_ptr<WaylandDataOffer> new_offer_; - - // Offer to receive data from another process via drag-and-drop, or null if no - // drag-and-drop from another process is in progress. - std::unique_ptr<WaylandDataOffer> drag_offer_; - - WaylandWindow* window_ = nullptr; - - bool is_handling_dropped_data_ = false; - bool is_leaving_ = false; - - std::unique_ptr<WaylandShmBuffer> shm_buffer_; - wl::Object<wl_surface> icon_surface_; - - // Mime types to be handled. - std::list<std::string> unprocessed_mime_types_; - - // The data delivered from Wayland - std::unique_ptr<ui::OSExchangeData> received_data_; - - // When dragging is started from Chromium, |source_data_| is forwarded to - // Wayland when they are ready to get the data. - std::unique_ptr<ui::OSExchangeData> source_data_; - - DISALLOW_COPY_AND_ASSIGN(WaylandDataDevice); }; } // namespace ui 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 e61c9b53816..3e020a31134 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 @@ -7,6 +7,7 @@ #include <utility> #include "base/bind.h" +#include "base/logging.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" @@ -64,7 +65,7 @@ void WaylandDataDeviceBase::RegisterDeferredReadCallback() { deferred_read_callback_.reset(wl_display_sync(connection_->display())); static const wl_callback_listener kListener = { - GtkPrimarySelectionDevice::DeferredReadCallback}; + WaylandDataDeviceBase::DeferredReadCallback}; wl_callback_add_listener(deferred_read_callback_.get(), &kListener, this); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h index 78abf99aec9..e331243fc5e 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h @@ -25,6 +25,7 @@ class WaylandDataDeviceBase { // Returns MIME types given by the current data offer. const std::vector<std::string>& GetAvailableMimeTypes() const; + // Extracts data of the specified MIME type from the data offer. bool RequestSelectionData(const std::string& mime_type); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc index 5b4a79d7979..6c283794473 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc @@ -4,7 +4,12 @@ #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" +#include <wayland-client-protocol.h> + +#include <memory> + #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" namespace ui { @@ -19,16 +24,23 @@ WaylandDataDeviceManager::WaylandDataDeviceManager( WaylandDataDeviceManager::~WaylandDataDeviceManager() = default; -wl_data_device* WaylandDataDeviceManager::GetDevice() { +WaylandDataDevice* WaylandDataDeviceManager::GetDevice() { DCHECK(connection_->seat()); - return wl_data_device_manager_get_data_device(device_manager_.get(), - connection_->seat()); + if (!device_) { + device_ = std::make_unique<WaylandDataDevice>( + connection_, wl_data_device_manager_get_data_device( + device_manager_.get(), connection_->seat())); + } + DCHECK(device_); + return device_.get(); } -std::unique_ptr<WaylandDataSource> WaylandDataDeviceManager::CreateSource() { +std::unique_ptr<WaylandDataSource> WaylandDataDeviceManager::CreateSource( + WaylandDataSource::Delegate* delegate) { wl_data_source* data_source = wl_data_device_manager_create_data_source(device_manager_.get()); - return std::make_unique<WaylandDataSource>(data_source, connection_); + return std::make_unique<WaylandDataSource>(data_source, connection_, + delegate); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h index ea37eee0ba3..7ed3a330ea3 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h @@ -5,33 +5,37 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DEVICE_MANAGER_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DEVICE_MANAGER_H_ -#include <wayland-client.h> - #include <memory> -#include "base/macros.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" namespace ui { class WaylandConnection; -class WaylandDataSource; +class WaylandDataDevice; class WaylandDataDeviceManager { public: + using DataSource = WaylandDataSource; + using DataDevice = WaylandDataDevice; + WaylandDataDeviceManager(wl_data_device_manager* device_manager, WaylandConnection* connection); + WaylandDataDeviceManager(const WaylandDataDeviceManager&) = delete; + WaylandDataDeviceManager& operator=(const WaylandDataDeviceManager&) = delete; ~WaylandDataDeviceManager(); - wl_data_device* GetDevice(); - std::unique_ptr<WaylandDataSource> CreateSource(); + WaylandDataDevice* GetDevice(); + std::unique_ptr<WaylandDataSource> CreateSource( + WaylandDataSource::Delegate* delegate); private: wl::Object<wl_data_device_manager> device_manager_; - WaylandConnection* connection_; + WaylandConnection* const connection_; - DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManager); + std::unique_ptr<WaylandDataDevice> device_; }; } // namespace ui 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 9195d876030..b4bedb59ac9 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 @@ -14,9 +14,6 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard_constants.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/ozone/platform/wayland/test/constants.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" @@ -27,8 +24,6 @@ #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 "url/gurl.h" using testing::_; using testing::Mock; @@ -37,8 +32,6 @@ namespace ui { namespace { -constexpr FilenameToURLPolicy kFilenameToURLPolicy = CONVERT_FILENAMES; - template <typename StringType> ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) { ui::PlatformClipboard::Data result; @@ -52,39 +45,6 @@ ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) { } // namespace -class MockDropHandler : public WmDropHandler { - public: - MockDropHandler() = default; - ~MockDropHandler() override {} - - MOCK_METHOD3(OnDragEnter, - void(const gfx::PointF& point, - std::unique_ptr<OSExchangeData> data, - int operation)); - MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation)); - MOCK_METHOD0(MockOnDragDrop, void()); - MOCK_METHOD0(OnDragLeave, void()); - - void SetOnDropClosure(base::RepeatingClosure closure) { - on_drop_closure_ = closure; - } - - ui::OSExchangeData* dropped_data() { return dropped_data_.get(); } - - protected: - void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) override { - dropped_data_ = std::move(data); - MockOnDragDrop(); - on_drop_closure_.Run(); - on_drop_closure_.Reset(); - } - - private: - base::RepeatingClosure on_drop_closure_; - - std::unique_ptr<ui::OSExchangeData> dropped_data_; -}; - // This class mocks how a real clipboard/ozone client would // hook to PlatformClipboard, with one difference: real clients // have no access to the WaylandConnection instance like this @@ -143,15 +103,11 @@ class WaylandDataDeviceManagerTest : public WaylandTest { clipboard_client_ = std::make_unique<MockClipboardClient>(connection_.get()); - - drop_handler_ = std::make_unique<MockDropHandler>(); - SetWmDropHandler(window_.get(), drop_handler_.get()); } protected: wl::TestDataDeviceManager* data_device_manager_; std::unique_ptr<MockClipboardClient> clipboard_client_; - std::unique_ptr<MockDropHandler> drop_handler_; private: DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManagerTest); @@ -231,255 +187,6 @@ TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) { ASSERT_FALSE(clipboard_client_->IsSelectionOwner()); } -TEST_P(WaylandDataDeviceManagerTest, StartDrag) { - bool restored_focus = window_->has_pointer_focus(); - window_->SetPointerFocus(true); - - // The client starts dragging. - OSExchangeData os_exchange_data; - int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; - connection_->StartDrag(os_exchange_data, operation); - - WaylandDataSource::DragDataMap data; - data[wl::kTextMimeTypeUtf8] = wl::kSampleTextForDragAndDrop; - connection_->drag_data_source()->SetDragData(data); - 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(); - window_->SetPointerFocus(restored_focus); -} - -TEST_P(WaylandDataDeviceManagerTest, StartDragWithWrongMimeType) { - bool restored_focus = window_->has_pointer_focus(); - window_->SetPointerFocus(true); - - // The client starts dragging offering data with wl::kTextMimeTypeUtf8 - // mime type. - OSExchangeData os_exchange_data; - int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; - connection_->StartDrag(os_exchange_data, operation); - - WaylandDataSource::DragDataMap data; - data[wl::kTextMimeTypeUtf8] = wl::kSampleTextForDragAndDrop; - connection_->drag_data_source()->SetDragData(data); - Sync(); - - // The server should get an empty data buffer in ReadData callback - // when trying to read it. - base::RunLoop run_loop; - auto callback = base::BindOnce( - [](base::RunLoop* loop, PlatformClipboard::Data&& data) { - std::string result(data.begin(), data.end()); - EXPECT_EQ("", result); - loop->Quit(); - }, - &run_loop); - data_device_manager_->data_source()->ReadData(ui::kMimeTypeText, - std::move(callback)); - run_loop.Run(); - window_->SetPointerFocus(restored_focus); -} - -TEST_P(WaylandDataDeviceManagerTest, ReceiveDrag) { - auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer( - ui::kMimeTypeText, - ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); - - gfx::Point entered_point(10, 10); - // The server sends an enter event. - data_device_manager_->data_device()->OnEnter( - 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), - wl_fixed_from_int(entered_point.y()), data_offer); - - int64_t time = - (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX; - gfx::Point motion_point(11, 11); - - // The server sends an motion event. - data_device_manager_->data_device()->OnMotion( - time, wl_fixed_from_int(motion_point.x()), - wl_fixed_from_int(motion_point.y())); - - Sync(); - - auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) { - std::string result; - result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]), - contents.size()); - EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); - }); - - // The client requests the data and gets callback with it. - connection_->RequestDragData(ui::kMimeTypeText, std::move(callback)); - Sync(); - - data_device_manager_->data_device()->OnLeave(); -} - -TEST_P(WaylandDataDeviceManagerTest, DropSeveralMimeTypes) { - auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer( - ui::kMimeTypeText, - ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); - data_offer->OnOffer( - ui::kMimeTypeMozillaURL, - ToClipboardData(base::UTF8ToUTF16("https://sample.com/\r\n" - "Sample"))); - data_offer->OnOffer( - ui::kMimeTypeURIList, - ToClipboardData(std::string("file:///home/user/file\r\n"))); - - 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()), - wl_fixed_from_int(entered_point.y()), data_offer); - Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - - EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); - base::RunLoop loop; - drop_handler_->SetOnDropClosure(loop.QuitClosure()); - data_device_manager_->data_device()->OnDrop(); - - // Here we are expecting three data items, so there will be three roundtrips - // to the Wayland and back. Hence Sync() three times. - Sync(); - Sync(); - Sync(); - loop.Run(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - - EXPECT_TRUE(drop_handler_->dropped_data()->HasString()); - EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); - EXPECT_TRUE(drop_handler_->dropped_data()->HasURL(kFilenameToURLPolicy)); - - data_device_manager_->data_device()->OnLeave(); -} - -// Tests URI validation for text/uri-list MIME type. Log warnings rendered in -// the console when this test is running are the expected and valid side effect. -TEST_P(WaylandDataDeviceManagerTest, ValidateDroppedUriList) { - const struct { - std::string content; - base::flat_set<std::string> expected_uris; - } kCases[] = {{{}, {}}, - {"file:///home/user/file\r\n", {"/home/user/file"}}, - {"# Comment\r\n" - "file:///home/user/file\r\n" - "file:///home/guest/file\r\n" - "not a filename at all\r\n" - "https://valid.url/but/scheme/is/not/file/so/invalid\r\n", - {"/home/user/file", "/home/guest/file"}}}; - - for (const auto& kCase : kCases) { - auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer(ui::kMimeTypeURIList, ToClipboardData(kCase.content)); - - 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()), - wl_fixed_from_int(entered_point.y()), data_offer); - Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - - EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); - base::RunLoop loop; - drop_handler_->SetOnDropClosure(loop.QuitClosure()); - data_device_manager_->data_device()->OnDrop(); - - Sync(); - loop.Run(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - - if (kCase.expected_uris.empty()) { - EXPECT_FALSE(drop_handler_->dropped_data()->HasFile()); - } else { - EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); - std::vector<FileInfo> filenames; - EXPECT_TRUE(drop_handler_->dropped_data()->GetFilenames(&filenames)); - EXPECT_EQ(filenames.size(), kCase.expected_uris.size()); - for (const auto& filename : filenames) - EXPECT_EQ(kCase.expected_uris.count(filename.path.AsUTF8Unsafe()), 1U); - } - - EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); - data_device_manager_->data_device()->OnLeave(); - Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - } -} - -// Tests URI validation for text/x-moz-url MIME type. Log warnings rendered in -// the console when this test is running are the expected and valid side effect. -TEST_P(WaylandDataDeviceManagerTest, ValidateDroppedXMozUrl) { - const struct { - std::string content; - std::string expected_url; - std::string expected_title; - } kCases[] = { - {{}, {}, {}}, - {"http://sample.com/\r\nSample", "http://sample.com/", "Sample"}, - {"http://title.must.be.set/", {}, {}}, - {"url.must.be.valid/and/have.scheme\r\nInvalid URL", {}, {}}, - {"file:///files/are/ok\r\nThe policy allows that", "file:///files/are/ok", - "The policy allows that"}}; - - for (const auto& kCase : kCases) { - auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer(ui::kMimeTypeMozillaURL, - ToClipboardData(base::UTF8ToUTF16(kCase.content))); - - 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()), - wl_fixed_from_int(entered_point.y()), data_offer); - Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - - EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); - base::RunLoop loop; - drop_handler_->SetOnDropClosure(loop.QuitClosure()); - data_device_manager_->data_device()->OnDrop(); - - Sync(); - loop.Run(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - - const auto* const dropped_data = drop_handler_->dropped_data(); - if (kCase.expected_url.empty()) { - EXPECT_FALSE(dropped_data->HasURL(kFilenameToURLPolicy)); - } else { - EXPECT_TRUE(dropped_data->HasURL(kFilenameToURLPolicy)); - GURL url; - base::string16 title; - EXPECT_TRUE( - dropped_data->GetURLAndTitle(kFilenameToURLPolicy, &url, &title)); - EXPECT_EQ(url.spec(), kCase.expected_url); - EXPECT_EQ(title, base::UTF8ToUTF16(kCase.expected_title)); - } - - EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); - data_device_manager_->data_device()->OnLeave(); - Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); - } -} - INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandDataDeviceManagerTest, ::testing::Values(kXdgShellStable)); 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 new file mode 100644 index 00000000000..ec14c3e3b7f --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc @@ -0,0 +1,355 @@ +// 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_data_drag_controller.h" + +#include <wayland-client-protocol.h> + +#include <cstdint> + +#include "base/check.h" +#include "base/notreached.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/ozone/platform/wayland/common/data_util.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_data_device_manager.h" +#include "ui/ozone/platform/wayland/host/wayland_data_offer.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/ozone/platform/wayland/host/wayland_window_manager.h" + +namespace ui { + +namespace { + +// Returns actions possible with the given source and drag'n'drop actions. +// Also converts enums: input params are wl_data_device_manager_dnd_action but +// the result is ui::DragDropTypes. +int GetPossibleActions(uint32_t source_actions, uint32_t dnd_action) { + // If drag'n'drop action is set, use it but check for ASK action (see below). + uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE + ? dnd_action + : source_actions; + + // We accept any action except ASK (see below). + int operation = DragDropTypes::DRAG_NONE; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + operation |= DragDropTypes::DRAG_COPY; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + operation |= DragDropTypes::DRAG_MOVE; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { + // This is very rare and non-standard. Chromium doesn't set this when + // anything is dragged from it, neither it provides any UI for asking + // the user about the desired drag'n'drop action when data is dragged + // from an external source. + // We are safe with not adding anything here. However, keep NOTIMPLEMENTED + // for an (unlikely) event of this being hit in distant future. + NOTIMPLEMENTED_LOG_ONCE(); + } + return operation; +} + +const SkBitmap* GetDragImage(const OSExchangeData& data) { + const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap(); + return icon_bitmap && !icon_bitmap->empty() ? icon_bitmap : nullptr; +} + +} // namespace + +WaylandDataDragController::WaylandDataDragController( + WaylandConnection* connection, + WaylandDataDeviceManager* data_device_manager) + : connection_(connection), + data_device_manager_(data_device_manager), + data_device_(data_device_manager->GetDevice()), + window_manager_(connection->wayland_window_manager()) { + DCHECK(connection_); + DCHECK(window_manager_); + DCHECK(data_device_manager_); + DCHECK(data_device_); +} + +WaylandDataDragController::~WaylandDataDragController() = default; + +void WaylandDataDragController::StartSession(const OSExchangeData& data, + int operation) { + DCHECK_EQ(state_, State::kIdle); + DCHECK(!origin_window_); + + origin_window_ = window_manager_->GetCurrentFocusedWindow(); + if (!origin_window_) { + LOG(ERROR) << "Failed to get focused window."; + return; + } + + // Create new new data source and offers |data|. + if (!data_source_) + data_source_ = data_device_manager_->CreateSource(this); + Offer(data, operation); + + // Create drag icon surface (if any) and store the data to be exchanged. + icon_surface_.reset(CreateIconSurfaceIfNeeded(data)); + data_ = std::make_unique<OSExchangeData>(data.provider().Clone()); + + // Starts the wayland drag session setting |this| object as delegate. + state_ = State::kStarted; + data_device_->StartDrag(*data_source_, *origin_window_, icon_surface_.get(), + this); +} + +// Sessions initiated from Chromium, will have |origin_window_| pointing to the +// window where the drag started in. In such cases, |data_| is expected to be +// non-null, which can be used to save some IO cycles. +bool WaylandDataDragController::IsDragSource() const { + DCHECK(!origin_window_ || data_); + return !!origin_window_; +} + +void WaylandDataDragController::DrawIcon() { + if (!icon_bitmap_) + return; + + DCHECK(!icon_bitmap_->empty()); + gfx::Size size(icon_bitmap_->width(), icon_bitmap_->height()); + + if (!shm_buffer_ || shm_buffer_->size() != size) { + shm_buffer_ = std::make_unique<WaylandShmBuffer>(connection_->shm(), size); + if (!shm_buffer_->IsValid()) { + LOG(ERROR) << "Failed to create drag icon buffer."; + return; + } + } + // TODO(crbug.com/1085418): Fix drag icon scaling + wl::DrawBitmap(*icon_bitmap_, shm_buffer_.get()); + wl_surface_attach(icon_surface_.get(), shm_buffer_->get(), 0, 0); + wl_surface_damage(icon_surface_.get(), 0, 0, size.width(), size.height()); + wl_surface_commit(icon_surface_.get()); +} + +void WaylandDataDragController::OnDragOffer( + std::unique_ptr<WaylandDataOffer> offer) { + DCHECK(!data_offer_); + data_offer_ = std::move(offer); +} + +void WaylandDataDragController::OnDragEnter(WaylandWindow* window, + const gfx::PointF& location, + uint32_t serial) { + DCHECK(window); + window_ = window; + + // TODO(crbug.com/1004715): Set mime type the client can accept. Now it sets + // all mime types offered because current implementation doesn't decide + // action based on mime type. + unprocessed_mime_types_.clear(); + for (auto mime : data_offer_->mime_types()) { + unprocessed_mime_types_.push_back(mime); + data_offer_->Accept(serial, mime); + } + + std::unique_ptr<OSExchangeData> dragged_data; + // If the DND session was initiated from a Chromium window, |data_| already + // holds the data to be exchanged, so no needed to read it through Wayland, + // thus just copy it here. + if (IsDragSource()) + dragged_data = std::make_unique<OSExchangeData>(data_->provider().Clone()); + window_->OnDragEnter(location, std::move(dragged_data), + GetPossibleActions(data_offer_->source_actions(), + data_offer_->dnd_action())); +} + +void WaylandDataDragController::OnDragMotion(const gfx::PointF& location) { + if (!window_) + return; + + int client_operation = window_->OnDragMotion( + location, GetPossibleActions(data_offer_->source_actions(), + data_offer_->dnd_action())); + SetOperation(client_operation); +} + +void WaylandDataDragController::OnDragLeave() { + if (!window_) + return; + + // Leave event can arrive while data is being transferred. As it cannot be + // handled right away, just mark it to be processed when the data is ready. + if (state_ == State::kTransferring) { + is_leave_pending_ = true; + return; + } + + window_->OnDragLeave(); + window_ = nullptr; + data_offer_.reset(); + is_leave_pending_ = false; +} + +void WaylandDataDragController::OnDragDrop() { + if (!window_) + return; + + if (IsDragSource()) { + // This means the data is being exchanged between Chromium windows. In this + // case, data is supposed to have already been sent to the drop handler + // before (see OnDragEnter()), expecting to receive null at this stage. + OnDataTransferFinished(nullptr); + return; + } + + // Otherwise, we are about to accept data dragged from another application. + // Reading the data may take some time so set |state_| to |kTrasfering|, which + // will make final "leave" event handling to be postponed until data is ready. + state_ = State::kTransferring; + received_data_ = std::make_unique<OSExchangeData>( + std::make_unique<OSExchangeDataProviderNonBacked>()); + HandleUnprocessedMimeTypes(); +} + +void WaylandDataDragController::OnDataSourceFinish(bool completed) { + DCHECK(data_source_); + if (origin_window_) + origin_window_->OnDragSessionClose(data_source_->dnd_action()); + + origin_window_ = nullptr; + data_source_.reset(); + data_offer_.reset(); + data_.reset(); + state_ = State::kIdle; +} + +void WaylandDataDragController::OnDataSourceSend(const std::string& mime_type, + std::string* buffer) { + DCHECK(data_source_); + DCHECK(buffer); + DCHECK(data_); + if (!wl::ExtractOSExchangeData(*data_, mime_type, buffer)) { + LOG(WARNING) << "Cannot deliver data of type " << mime_type + << " and no text representation is available."; + } +} + +void WaylandDataDragController::Offer(const OSExchangeData& data, + int operation) { + DCHECK(data_source_); + + // Drag'n'drop manuals usually suggest putting data in order so the more + // specific a MIME type is, the earlier it occurs in the list. Wayland + // specs don't say anything like that, but here we follow that common + // practice: begin with URIs and end with plain text. Just in case. + std::vector<std::string> mime_types; + if (data.HasFile()) { + mime_types.push_back(kMimeTypeURIList); + } + if (data.HasURL(FilenameToURLPolicy::CONVERT_FILENAMES)) { + mime_types.push_back(kMimeTypeMozillaURL); + } + if (data.HasHtml()) { + mime_types.push_back(kMimeTypeHTML); + } + if (data.HasString()) { + mime_types.push_back(kMimeTypeTextUtf8); + mime_types.push_back(kMimeTypeText); + } + + DCHECK(!mime_types.empty()); + data_source_->Offer(mime_types); + data_source_->SetAction(operation); +} + +wl_surface* WaylandDataDragController::CreateIconSurfaceIfNeeded( + const OSExchangeData& data) { + icon_bitmap_ = GetDragImage(data); + return icon_bitmap_ ? wl_compositor_create_surface(connection_->compositor()) + : nullptr; +} + +// Asynchronously requests and reads data for every negotiated/supported mime +// type, one after another, OnMimeTypeDataTransferred calls back into this +// function once it finishes reading data for each mime type, until there is no +// more unprocessed mime types on the |unprocessed_mime_types_| queue. Once this +// process is finished, OnDataTransferFinished is called to deliver the +// |received_data_| to the drop handler. +void WaylandDataDragController::HandleUnprocessedMimeTypes() { + DCHECK_EQ(state_, State::kTransferring); + std::string mime_type = GetNextUnprocessedMimeType(); + if (mime_type.empty()) { + OnDataTransferFinished(std::move(received_data_)); + } else { + DCHECK(data_offer_); + data_device_->RequestData( + data_offer_.get(), mime_type, + base::BindOnce(&WaylandDataDragController::OnMimeTypeDataTransferred, + weak_factory_.GetWeakPtr())); + } +} + +void WaylandDataDragController::OnMimeTypeDataTransferred( + const PlatformClipboard::Data& contents) { + DCHECK_EQ(state_, State::kTransferring); + if (!contents.empty()) { + std::string mime_type = unprocessed_mime_types_.front(); + wl::AddToOSExchangeData(contents, mime_type, received_data_.get()); + } + unprocessed_mime_types_.pop_front(); + + // Continue reading data for other negotiated mime types. + HandleUnprocessedMimeTypes(); +} + +void WaylandDataDragController::OnDataTransferFinished( + std::unique_ptr<OSExchangeData> received_data) { + data_offer_->FinishOffer(); + window_->OnDragDrop(std::move(received_data)); + + unprocessed_mime_types_.clear(); + state_ = State::kIdle; + + // If |is_leave_pending_| is set, it means a 'leave' event was fired while + // data was on transit, so process it here (See OnDragLeave for more context). + if (is_leave_pending_) + OnDragLeave(); +} + +// Returns the next MIME type to be received from the source process, or an +// empty string if there are no more interesting MIME types left to process. +std::string WaylandDataDragController::GetNextUnprocessedMimeType() { + while (!unprocessed_mime_types_.empty()) { + const std::string& mime_type = unprocessed_mime_types_.front(); + // Skip unsupported or already processed mime types. + if (!wl::IsMimeTypeSupported(mime_type) || + wl::ContainsMimeType(*received_data_, mime_type)) { + unprocessed_mime_types_.pop_front(); + continue; + } + return mime_type; + } + return {}; +} + +void WaylandDataDragController::SetOperation(const int operation) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + + if (operation & DragDropTypes::DRAG_COPY) { + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + if (operation & DragDropTypes::DRAG_MOVE) { + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } + data_offer_->SetAction(dnd_actions, preferred_action); +} + +} // namespace ui 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 new file mode 100644 index 00000000000..2d1e1b09d6d --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h @@ -0,0 +1,136 @@ +// 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_DATA_DRAG_CONTROLLER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DRAG_CONTROLLER_H_ + +#include <list> +#include <memory> +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" + +struct wl_surface; +class SkBitmap; + +namespace ui { + +class OSExchangeData; +class WaylandConnection; +class WaylandDataDeviceManager; +class WaylandDataOffer; +class WaylandWindow; +class WaylandWindowManager; +class WaylandShmBuffer; + +// WaylandDataDragController implements regular data exchanging between Chromium +// and other client applications on top of the Wayland Drag and Drop protocol. +// By implementing both DataDevice::DragDelegate and DataSource::Delegate, +// it is responsible for handling both DND sessions initiated from Chromium +// windows as well as those triggered by other clients. +class WaylandDataDragController : public WaylandDataDevice::DragDelegate, + public WaylandDataSource::Delegate { + public: + enum class State { kIdle, kStarted, kTransferring }; + + WaylandDataDragController(WaylandConnection* connection, + WaylandDataDeviceManager* data_device_manager); + WaylandDataDragController(const WaylandDataDragController&) = delete; + WaylandDataDragController& operator=(const WaylandDataDragController&) = + delete; + ~WaylandDataDragController() override; + + // Starts a data drag and drop session for |data|. Only one DND session can + // run at a given time. + void StartSession(const ui::OSExchangeData& data, int operation); + + State state() const { return state_; } + + // TODO(crbug.com/896640): Remove once focus is fixed during DND sessions. + WaylandWindow* entered_window() const { return window_; } + + private: + FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, ReceiveDrag); + FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDrag); + FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDragWithText); + FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, + StartDragWithWrongMimeType); + + // WaylandDataDevice::DragDelegate: + bool IsDragSource() const override; + void DrawIcon() override; + void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) override; + void OnDragEnter(WaylandWindow* window, + const gfx::PointF& location, + uint32_t serial) override; + void OnDragMotion(const gfx::PointF& location) override; + void OnDragLeave() override; + void OnDragDrop() override; + + // WaylandDataSource::Delegate: + void OnDataSourceFinish(bool completed) override; + void OnDataSourceSend(const std::string& mime_type, + std::string* contents) override; + + void Offer(const OSExchangeData& data, int operation); + wl_surface* CreateIconSurfaceIfNeeded(const OSExchangeData& data); + void HandleUnprocessedMimeTypes(); + void OnMimeTypeDataTransferred(const PlatformClipboard::Data& contents); + void OnDataTransferFinished( + std::unique_ptr<ui::OSExchangeData> received_data); + std::string GetNextUnprocessedMimeType(); + void SetOperation(const int operation); + + WaylandConnection* const connection_; + WaylandDataDeviceManager* const data_device_manager_; + WaylandDataDevice* const data_device_; + WaylandWindowManager* const window_manager_; + + State state_ = State::kIdle; + + std::unique_ptr<WaylandDataSource> data_source_; + + // When dragging is started from Chromium, |data_| holds the data to be sent + // through wl_data_device instance. + std::unique_ptr<ui::OSExchangeData> data_; + + // Offer to receive data from another process via drag-and-drop, or null if + // no drag-and-drop from another process is in progress. + // + // The data offer from another Wayland client through wl_data_device, that + // triggered the current drag and drop session. If null, either there is no + // dnd session running or Chromium is the data source. + std::unique_ptr<WaylandDataOffer> data_offer_; + + // Mime types to be handled. + std::list<std::string> unprocessed_mime_types_; + + // The window that initiated the drag session. Can be null when the session + // has been started by an external Wayland client. + WaylandWindow* origin_window_ = nullptr; + + // Current window under pointer. + WaylandWindow* window_ = nullptr; + + // The data delivered from Wayland + std::unique_ptr<ui::OSExchangeData> received_data_; + + // Set when 'leave' event is fired while data is being transferred. + bool is_leave_pending_ = false; + + // Drag icon related variables. + wl::Object<wl_surface> icon_surface_; + std::unique_ptr<WaylandShmBuffer> shm_buffer_; + const SkBitmap* icon_bitmap_ = nullptr; + + base::WeakPtrFactory<WaylandDataDragController> weak_factory_{this}; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DRAG_CONTROLLER_H_ 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 new file mode 100644 index 00000000000..a320c70dd1d --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc @@ -0,0 +1,406 @@ +// Copyright 2018 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 <wayland-server.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/containers/flat_set.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/clipboard/clipboard_constants.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/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/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" +#include "ui/ozone/platform/wayland/test/test_data_offer.h" +#include "ui/ozone/platform/wayland/test/test_data_source.h" +#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 "url/gurl.h" + +using testing::_; +using testing::Mock; + +namespace ui { + +namespace { + +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; +} + +} // namespace + +class MockDropHandler : public WmDropHandler { + public: + MockDropHandler() = default; + ~MockDropHandler() override = default; + + MOCK_METHOD3(OnDragEnter, + void(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation)); + MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation)); + MOCK_METHOD0(MockOnDragDrop, void()); + MOCK_METHOD0(OnDragLeave, void()); + + void SetOnDropClosure(base::RepeatingClosure closure) { + on_drop_closure_ = closure; + } + + OSExchangeData* dropped_data() { return dropped_data_.get(); } + + protected: + void OnDragDrop(std::unique_ptr<OSExchangeData> data) override { + dropped_data_ = std::move(data); + MockOnDragDrop(); + on_drop_closure_.Run(); + on_drop_closure_.Reset(); + } + + private: + base::RepeatingClosure on_drop_closure_; + + std::unique_ptr<OSExchangeData> dropped_data_; +}; + +class WaylandDataDragControllerTest : public WaylandTest { + public: + WaylandDataDragControllerTest() = default; + + void SetUp() override { + WaylandTest::SetUp(); + + Sync(); + + data_device_manager_ = server_.data_device_manager(); + DCHECK(data_device_manager_); + + drop_handler_ = std::make_unique<MockDropHandler>(); + SetWmDropHandler(window_.get(), drop_handler_.get()); + } + + WaylandDataDragController* drag_controller() const { + return connection_->data_drag_controller(); + } + + WaylandDataDevice* data_device() const { + return connection_->data_device_manager()->GetDevice(); + } + + base::string16 sample_text_for_dnd() const { + static auto text = base::ASCIIToUTF16(wl::kSampleTextForDragAndDrop); + return text; + } + + protected: + wl::TestDataDeviceManager* data_device_manager_; + std::unique_ptr<MockDropHandler> drop_handler_; +}; + +TEST_P(WaylandDataDragControllerTest, StartDrag) { + bool restored_focus = window_->has_pointer_focus(); + window_->SetPointerFocus(true); + + // The client starts dragging. + 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); + 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(); + window_->SetPointerFocus(restored_focus); +} + +TEST_P(WaylandDataDragControllerTest, StartDragWithWrongMimeType) { + bool restored_focus = window_->has_pointer_focus(); + window_->SetPointerFocus(true); + + // The client starts dragging offering data with |kMimeTypeHTML| + OSExchangeData os_exchange_data; + os_exchange_data.SetHtml(sample_text_for_dnd(), {}); + int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; + drag_controller()->StartSession(os_exchange_data, operation); + Sync(); + + // The server should get an empty data buffer in ReadData callback when trying + // to read it with a different mime type. + base::RunLoop run_loop; + auto callback = base::BindOnce( + [](base::RunLoop* loop, PlatformClipboard::Data&& data) { + std::string result(data.begin(), data.end()); + EXPECT_TRUE(result.empty()); + loop->Quit(); + }, + &run_loop); + data_device_manager_->data_source()->ReadData(kMimeTypeText, + std::move(callback)); + run_loop.Run(); + window_->SetPointerFocus(restored_focus); +} + +TEST_P(WaylandDataDragControllerTest, StartDragWithText) { + bool restored_focus = window_->has_pointer_focus(); + window_->SetPointerFocus(true); + + // The client starts dragging offering text mime 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); + Sync(); + + // The server should get a "text" representation in ReadData callback when + // trying to read it as mime type other than |kMimeTypeText| and + // |kTextMimeTypeUtf8|. + 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(kMimeTypeMozillaURL, + std::move(callback)); + run_loop.Run(); + window_->SetPointerFocus(restored_focus); +} + +TEST_P(WaylandDataDragControllerTest, ReceiveDrag) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer( + kMimeTypeText, + ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); + + gfx::Point entered_point(10, 10); + // The server sends an enter event. + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), data_offer); + + int64_t time = + (EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX; + gfx::Point motion_point(11, 11); + + // The server sends an motion event. + data_device_manager_->data_device()->OnMotion( + time, wl_fixed_from_int(motion_point.x()), + wl_fixed_from_int(motion_point.y())); + + Sync(); + + auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) { + std::string result; + result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]), + contents.size()); + EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); + }); + + // The client requests the data and gets callback with it. + data_device()->RequestData(drag_controller()->data_offer_.get(), + kMimeTypeText, std::move(callback)); + Sync(); + + data_device_manager_->data_device()->OnLeave(); +} + +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(kMimeTypeMozillaURL, ToClipboardData(base::UTF8ToUTF16( + "https://sample.com/\r\n" + "Sample"))); + data_offer->OnOffer( + kMimeTypeURIList, + ToClipboardData(std::string("file:///home/user/file\r\n"))); + + 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()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); + base::RunLoop loop; + drop_handler_->SetOnDropClosure(loop.QuitClosure()); + data_device_manager_->data_device()->OnDrop(); + + // Here we are expecting three data items, so there will be three roundtrips + // to the Wayland and back. Hence Sync() three times. + Sync(); + Sync(); + Sync(); + loop.Run(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_TRUE(drop_handler_->dropped_data()->HasString()); + EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); + EXPECT_TRUE(drop_handler_->dropped_data()->HasURL(kFilenameToURLPolicy)); + + data_device_manager_->data_device()->OnLeave(); +} + +// Tests URI validation for text/uri-list MIME type. Log warnings rendered in +// the console when this test is running are the expected and valid side effect. +TEST_P(WaylandDataDragControllerTest, ValidateDroppedUriList) { + const struct { + std::string content; + base::flat_set<std::string> expected_uris; + } kCases[] = {{{}, {}}, + {"file:///home/user/file\r\n", {"/home/user/file"}}, + {"# Comment\r\n" + "file:///home/user/file\r\n" + "file:///home/guest/file\r\n" + "not a filename at all\r\n" + "https://valid.url/but/scheme/is/not/file/so/invalid\r\n", + {"/home/user/file", "/home/guest/file"}}}; + + for (const auto& kCase : kCases) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(kMimeTypeURIList, ToClipboardData(kCase.content)); + + 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()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); + base::RunLoop loop; + drop_handler_->SetOnDropClosure(loop.QuitClosure()); + data_device_manager_->data_device()->OnDrop(); + + Sync(); + loop.Run(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + if (kCase.expected_uris.empty()) { + EXPECT_FALSE(drop_handler_->dropped_data()->HasFile()); + } else { + EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); + std::vector<FileInfo> filenames; + EXPECT_TRUE(drop_handler_->dropped_data()->GetFilenames(&filenames)); + EXPECT_EQ(filenames.size(), kCase.expected_uris.size()); + for (const auto& filename : filenames) + EXPECT_EQ(kCase.expected_uris.count(filename.path.AsUTF8Unsafe()), 1U); + } + + EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); + data_device_manager_->data_device()->OnLeave(); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + } +} + +// Tests URI validation for text/x-moz-url MIME type. Log warnings rendered in +// the console when this test is running are the expected and valid side effect. +TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) { + const struct { + std::string content; + std::string expected_url; + std::string expected_title; + } kCases[] = { + {{}, {}, {}}, + {"http://sample.com/\r\nSample", "http://sample.com/", "Sample"}, + {"http://title.must.be.set/", {}, {}}, + {"url.must.be.valid/and/have.scheme\r\nInvalid URL", {}, {}}, + {"file:///files/are/ok\r\nThe policy allows that", "file:///files/are/ok", + "The policy allows that"}}; + + for (const auto& kCase : kCases) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(kMimeTypeMozillaURL, + ToClipboardData(base::UTF8ToUTF16(kCase.content))); + + 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()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); + base::RunLoop loop; + drop_handler_->SetOnDropClosure(loop.QuitClosure()); + data_device_manager_->data_device()->OnDrop(); + + Sync(); + loop.Run(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + const auto* const dropped_data = drop_handler_->dropped_data(); + if (kCase.expected_url.empty()) { + EXPECT_FALSE(dropped_data->HasURL(kFilenameToURLPolicy)); + } else { + EXPECT_TRUE(dropped_data->HasURL(kFilenameToURLPolicy)); + GURL url; + base::string16 title; + EXPECT_TRUE( + dropped_data->GetURLAndTitle(kFilenameToURLPolicy, &url, &title)); + EXPECT_EQ(url.spec(), kCase.expected_url); + EXPECT_EQ(title, base::UTF8ToUTF16(kCase.expected_title)); + } + + EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); + data_device_manager_->data_device()->OnLeave(); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + } +} + +INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, + WaylandDataDragControllerTest, + ::testing::Values(kXdgShellStable)); + +INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, + WaylandDataDragControllerTest, + ::testing::Values(kXdgShellV6)); + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc index 0b92ceaeba7..1d66a9f74cb 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc @@ -4,156 +4,148 @@ #include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include <gtk-primary-selection-client-protocol.h> +#include <wayland-client-protocol.h> + +#include <cstdint> #include <vector> #include "base/files/file_util.h" -#include "base/optional.h" -#include "ui/base/clipboard/clipboard_constants.h" #include "ui/base/dragdrop/drag_drop_types.h" -#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" -#include "ui/ozone/platform/wayland/host/wayland_window.h" -namespace ui { +namespace wl { -WaylandDataSource::WaylandDataSource(wl_data_source* data_source, - WaylandConnection* connection) - : data_source_(data_source), connection_(connection) { - static const struct wl_data_source_listener kDataSourceListener = { - WaylandDataSource::OnTarget, WaylandDataSource::OnSend, - WaylandDataSource::OnCancel, WaylandDataSource::OnDnDDropPerformed, - WaylandDataSource::OnDnDFinished, WaylandDataSource::OnAction}; - wl_data_source_add_listener(data_source, &kDataSourceListener, this); +template <typename T> +DataSource<T>::DataSource(T* data_source, + ui::WaylandConnection* connection, + Delegate* delegate) + : data_source_(data_source), connection_(connection), delegate_(delegate) { + DCHECK(data_source_); + DCHECK(connection_); + DCHECK(delegate_); + + Initialize(); } -WaylandDataSource::~WaylandDataSource() = default; +template <typename T> +void DataSource<T>::HandleFinishEvent(bool completed) { + delegate_->OnDataSourceFinish(/*completed=*/false); +} -void WaylandDataSource::WriteToClipboard( - const PlatformClipboard::DataMap& data_map) { - for (const auto& data : data_map) { - wl_data_source_offer(data_source_.get(), data.first.c_str()); - if (strcmp(data.first.c_str(), kMimeTypeText) == 0) - wl_data_source_offer(data_source_.get(), kMimeTypeTextUtf8); - } - wl_data_device_set_selection(connection_->data_device(), data_source_.get(), - connection_->serial()); +template <typename T> +void DataSource<T>::HandleSendEvent(const std::string& mime_type, int32_t fd) { + std::string contents; + delegate_->OnDataSourceSend(mime_type, &contents); + bool done = base::WriteFileDescriptor(fd, contents.data(), contents.length()); + DCHECK(done); + close(fd); +} - connection_->ScheduleFlush(); +// static +template <typename T> +void DataSource<T>::OnSend(void* data, + T* source, + const char* mime_type, + int32_t fd) { + auto* self = static_cast<DataSource<T>*>(data); + self->HandleSendEvent(mime_type, fd); } -void WaylandDataSource::Offer(const ui::OSExchangeData& data) { - // Drag'n'drop manuals usually suggest putting data in order so the more - // specific a MIME type is, the earlier it occurs in the list. Wayland specs - // don't say anything like that, but here we follow that common practice: - // begin with URIs and end with plain text. Just in case. - std::vector<std::string> mime_types; - if (data.HasFile()) { - mime_types.push_back(kMimeTypeURIList); - } - if (data.HasURL(ui::CONVERT_FILENAMES)) { - mime_types.push_back(kMimeTypeMozillaURL); - } - if (data.HasHtml()) { - mime_types.push_back(kMimeTypeHTML); - } - if (data.HasString()) { - mime_types.push_back(kMimeTypeTextUtf8); - mime_types.push_back(kMimeTypeText); - } +template <typename T> +void DataSource<T>::OnCancel(void* data, T* source) { + auto* self = static_cast<DataSource<T>*>(data); + self->HandleFinishEvent(/*completed=*/false); +} - source_window_ = - connection_->wayland_window_manager()->GetCurrentFocusedWindow(); - for (auto& mime_type : mime_types) - wl_data_source_offer(data_source_.get(), mime_type.data()); +template <typename T> +void DataSource<T>::OnDnDFinished(void* data, T* source) { + auto* self = static_cast<DataSource<T>*>(data); + self->HandleFinishEvent(/*completed=*/true); } -void WaylandDataSource::SetDragData(const DragDataMap& data_map) { - DCHECK(drag_data_map_.empty()); - drag_data_map_ = data_map; +template <typename T> +void DataSource<T>::OnAction(void* data, T* source, uint32_t dnd_action) { + auto* self = static_cast<DataSource<T>*>(data); + self->dnd_action_ = dnd_action; } -void WaylandDataSource::SetAction(int operation) { - if (wl_data_source_get_version(data_source_.get()) >= - WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) { - uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - if (operation & DragDropTypes::DRAG_COPY) - dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - if (operation & DragDropTypes::DRAG_MOVE) - dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - wl_data_source_set_actions(data_source_.get(), dnd_actions); - } +template <typename T> +void DataSource<T>::OnTarget(void* data, T* source, const char* mime_type) { + NOTIMPLEMENTED_LOG_ONCE(); } -// static -void WaylandDataSource::OnTarget(void* data, - wl_data_source* source, - const char* mime_type) { +template <typename T> +void DataSource<T>::OnDnDDropPerformed(void* data, T* source) { NOTIMPLEMENTED_LOG_ONCE(); } -// static -void WaylandDataSource::OnSend(void* data, - wl_data_source* source, - const char* mime_type, - int32_t fd) { - WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - std::string contents; - if (self->source_window_) { - // If |source_window_| is valid when OnSend() is called, it means that DnD - // is working. - self->GetDragData(mime_type, &contents); - } else { - base::Optional<std::vector<uint8_t>> mime_data; - self->GetClipboardData(mime_type, &mime_data); - if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0) - self->GetClipboardData(kMimeTypeText, &mime_data); - contents.assign(mime_data->begin(), mime_data->end()); - } - bool result = - base::WriteFileDescriptor(fd, contents.data(), contents.length()); - DCHECK(result); - close(fd); +////////////////////////////////////////////////////////////////////////////// +// wl_data_source specializations and instantiation +////////////////////////////////////////////////////////////////////////////// + +template <> +void DataSource<wl_data_source>::Initialize() { + static const struct wl_data_source_listener kDataSourceListener = { + DataSource<wl_data_source>::OnTarget, + DataSource<wl_data_source>::OnSend, + DataSource<wl_data_source>::OnCancel, + DataSource<wl_data_source>::OnDnDDropPerformed, + DataSource<wl_data_source>::OnDnDFinished, + DataSource<wl_data_source>::OnAction}; + wl_data_source_add_listener(data_source_.get(), &kDataSourceListener, this); } -// static -void WaylandDataSource::OnCancel(void* data, wl_data_source* source) { - WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - if (self->source_window_) { - // If it has |source_window_|, it is in the middle of 'drag and drop'. it - // cancels 'drag and drop'. - self->connection_->FinishDragSession(self->dnd_action_, - self->source_window_); - } else { - self->connection_->clipboard()->DataSourceCancelled( - ClipboardBuffer::kCopyPaste); - } +template <> +void DataSource<wl_data_source>::Offer( + const std::vector<std::string>& mime_types) { + for (auto& mime_type : mime_types) + wl_data_source_offer(data_source_.get(), mime_type.c_str()); + connection_->ScheduleFlush(); } -void WaylandDataSource::OnDnDDropPerformed(void* data, wl_data_source* source) { +template <typename T> +void DataSource<T>::SetAction(int operation) { NOTIMPLEMENTED_LOG_ONCE(); } -void WaylandDataSource::OnDnDFinished(void* data, wl_data_source* source) { - WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - self->connection_->FinishDragSession(self->dnd_action_, self->source_window_); +template <> +void DataSource<wl_data_source>::SetAction(int operation) { + if (wl_data_source_get_version(data_source_.get()) >= + WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (operation & ui::DragDropTypes::DRAG_COPY) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (operation & ui::DragDropTypes::DRAG_MOVE) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + wl_data_source_set_actions(data_source_.get(), dnd_actions); + } } -void WaylandDataSource::OnAction(void* data, - wl_data_source* source, - uint32_t dnd_action) { - WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - self->dnd_action_ = dnd_action; -} +template class DataSource<wl_data_source>; -void WaylandDataSource::GetDragData(const std::string& mime_type, - std::string* contents) { - auto it = drag_data_map_.find(mime_type); - if (it != drag_data_map_.end()) { - *contents = it->second; - return; - } +////////////////////////////////////////////////////////////////////////////// +// gtk_primary_selection_source specializations and instantiation +////////////////////////////////////////////////////////////////////////////// + +template <> +void DataSource<gtk_primary_selection_source>::Initialize() { + static const struct gtk_primary_selection_source_listener + kDataSourceListener = { + DataSource<gtk_primary_selection_source>::OnSend, + DataSource<gtk_primary_selection_source>::OnCancel}; + gtk_primary_selection_source_add_listener(data_source_.get(), + &kDataSourceListener, this); +} - connection_->DeliverDragData(mime_type, contents); +template <> +void DataSource<gtk_primary_selection_source>::Offer( + const std::vector<std::string>& mime_types) { + for (const auto& mime_type : mime_types) + gtk_primary_selection_source_offer(data_source_.get(), mime_type.c_str()); + connection_->ScheduleFlush(); } -} // namespace ui +template class DataSource<gtk_primary_selection_source>; + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h index 4f66465a699..2bcee213cc5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h @@ -5,76 +5,98 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_H_ -#include <wayland-client.h> - -#include <map> +#include <cstdint> #include <string> +#include <vector> -#include "base/logging.h" #include "base/macros.h" -#include "base/optional.h" +#include "base/notreached.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" -#include "ui/ozone/platform/wayland/host/wayland_data_source_base.h" -#include "ui/ozone/public/platform_clipboard.h" + +struct wl_data_source; +struct gtk_primary_selection_source; + +namespace wl { +template <typename T> +class DataSource; +} // namespace wl namespace ui { -class OSExchangeData; class WaylandConnection; -class WaylandWindow; - -// The WaylandDataSource object represents the source side of a -// WaylandDataOffer. It is created by the source client in a data -// transfer and provides a way to describe the offered data -// (wl_data_source_offer) // and a way to respond to requests to -// transfer the data (OnSend listener). -class WaylandDataSource : public WaylandDataSourceBase { - public: - using DragDataMap = std::map<std::string, std::string>; - // Takes ownership of data_source. - explicit WaylandDataSource(wl_data_source* data_source, - WaylandConnection* connection); - ~WaylandDataSource() override; +// DataSource represents the source side of a DataOffer. It is created by the +// source client in a data transfer and provides a way to describe the offered +// data and a way to respond to requests to transfer the data. There are a few +// variants of Wayland protocol objects and extensions supporting different +// features. E.g: regular copy/paste and drag operations are implemented by +// wl_data_source (along with its _device and _offer counterparts), etc. +// Implementation wise, these variants are share a single class template, with +// specializations defined for each underlying supported extensions. Below are +// the type aliases for the variants currently supported. +// +// TODO(crbug.com/1088132): Support standard primary selection extension. + +using WaylandDataSource = wl::DataSource<wl_data_source>; + +using GtkPrimarySelectionSource = wl::DataSource<gtk_primary_selection_source>; + +} // namespace ui - void set_connection(WaylandConnection* connection) { - DCHECK(connection); - connection_ = connection; - } +namespace wl { - void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override; - void Offer(const ui::OSExchangeData& data); +// Template class implementing DataSource, whereas T is the underlying source +// type, e.g: wl_data_source, gtk_primary_selection_source, etc. This class +// is not supposed to be used directly, instead use the aliases defined above. +template <typename T> +class DataSource { + public: + class Delegate { + public: + virtual void OnDataSourceFinish(bool completed) = 0; + virtual void OnDataSourceSend(const std::string& mime_type, + std::string* contents) = 0; + + protected: + virtual ~Delegate() = default; + }; + + // Takes ownership of |data_source|. + DataSource(T* data_source, + ui::WaylandConnection* connection, + Delegate* delegate); + DataSource(const DataSource<T>&) = delete; + DataSource& operator=(const DataSource<T>&) = delete; + ~DataSource() = default; + + void Initialize(); + void Offer(const std::vector<std::string>& mime_types); void SetAction(int operation); - void SetDragData(const DragDataMap& data_map); - wl_data_source* data_source() const { return data_source_.get(); } + uint32_t dnd_action() const { return dnd_action_; } + T* data_source() const { return data_source_.get(); } private: - static void OnTarget(void* data, - wl_data_source* source, - const char* mime_type); - static void OnSend(void* data, - wl_data_source* source, - const char* mime_type, - int32_t fd); - static void OnCancel(void* data, wl_data_source* source); - static void OnDnDDropPerformed(void* data, wl_data_source* source); - static void OnDnDFinished(void* data, wl_data_source* source); - static void OnAction(void* data, wl_data_source* source, uint32_t dnd_action); - - void GetDragData(const std::string& mime_type, std::string* contents); - - wl::Object<wl_data_source> data_source_; - WaylandConnection* connection_ = nullptr; - WaylandWindow* source_window_ = nullptr; - - DragDataMap drag_data_map_; + void HandleFinishEvent(bool completed); + void HandleSendEvent(const std::string& mime_type, int32_t fd); + + static void OnSend(void* data, T* source, const char* mime_type, int32_t fd); + static void OnCancel(void* data, T* source); + static void OnDnDFinished(void* data, T* source); + static void OnAction(void* data, T* source, uint32_t dnd_action); + static void OnTarget(void* data, T* source, const char* mime_type); + static void OnDnDDropPerformed(void* data, T* source); + + wl::Object<T> data_source_; + + ui::WaylandConnection* const connection_; + + Delegate* const delegate_; + // Action selected by the compositor uint32_t dnd_action_; - - DISALLOW_COPY_AND_ASSIGN(WaylandDataSource); }; -} // namespace ui +} // namespace wl #endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc deleted file mode 100644 index c7bba1d2538..00000000000 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc +++ /dev/null @@ -1,21 +0,0 @@ -// 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_data_source_base.h" - -namespace ui { - -WaylandDataSourceBase::WaylandDataSourceBase() = default; -WaylandDataSourceBase::~WaylandDataSourceBase() = default; - -void WaylandDataSourceBase::GetClipboardData( - const std::string& mime_type, - base::Optional<std::vector<uint8_t>>* data) const { - auto it = data_map_.find(mime_type); - if (it == data_map_.end()) - return; - data->emplace(it->second); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h deleted file mode 100644 index 4e4d66b1607..00000000000 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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_DATA_SOURCE_BASE_H_ -#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_BASE_H_ - -#include "base/macros.h" -#include "ui/ozone/public/platform_clipboard.h" - -namespace ui { - -// Implements high level (protocol-agnostic) interface to a Wayland data source. -class WaylandDataSourceBase { - public: - WaylandDataSourceBase(); - virtual ~WaylandDataSourceBase(); - - void set_data_map(const PlatformClipboard::DataMap& data_map) { - data_map_ = data_map; - } - - // Writes data to the system clipboard using the protocol-defined data source. - virtual void WriteToClipboard(const PlatformClipboard::DataMap& data_map) = 0; - - protected: - void GetClipboardData(const std::string& mime_type, - base::Optional<std::vector<uint8_t>>* data) const; - - private: - PlatformClipboard::DataMap data_map_; - - DISALLOW_COPY_AND_ASSIGN(WaylandDataSourceBase); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_BASE_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc b/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc index 12a91eed83a..e387e48c686 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc @@ -8,6 +8,7 @@ #include <xf86drm.h> #include "base/files/scoped_file.h" +#include "base/logging.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gfx/linux/drm_util_linux.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" 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 a83c0bfe87c..48d500ae5fb 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc @@ -96,21 +96,21 @@ void WaylandEventSource::OnKeyboardModifiersChanged(int modifiers) { keyboard_modifiers_ = modifiers; } -void WaylandEventSource::OnKeyboardKeyEvent(EventType type, - DomCode dom_code, - DomKey dom_key, - KeyboardCode key_code, - bool repeat, - base::TimeTicks timestamp) { +uint32_t WaylandEventSource::OnKeyboardKeyEvent(EventType type, + DomCode dom_code, + DomKey dom_key, + KeyboardCode key_code, + bool repeat, + base::TimeTicks timestamp) { DCHECK(type == ET_KEY_PRESSED || type == ET_KEY_RELEASED); if (!keyboard_) - return; + return POST_DISPATCH_NONE; // try to decode key, if not yet. if (dom_key == DomKey::NONE && !keyboard_->Decode(dom_code, keyboard_modifiers_, &dom_key, &key_code)) { LOG(ERROR) << "Failed to decode key event."; - return; + return POST_DISPATCH_NONE; } if (!repeat) { @@ -121,7 +121,7 @@ void WaylandEventSource::OnKeyboardKeyEvent(EventType type, KeyEvent event(type, key_code, dom_code, keyboard_modifiers_, dom_key, timestamp); event.set_source_device_id(keyboard_->device_id()); - DispatchEvent(&event); + return DispatchEvent(&event); } void WaylandEventSource::OnPointerCreated(WaylandPointer* pointer) { @@ -133,15 +133,13 @@ void WaylandEventSource::OnPointerDestroyed(WaylandPointer* pointer) { DCHECK_EQ(pointer_, pointer); // Clear focused window, if any. - if (auto* focused_window = window_manager_->GetCurrentFocusedWindow()) - HandlePointerFocusChange(focused_window, false); + HandlePointerFocusChange(nullptr); ResetPointerFlags(); pointer_ = nullptr; } void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window, - bool focused, const gfx::PointF& location) { if (!pointer_) return; @@ -149,8 +147,9 @@ void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window, // Save new pointer location. pointer_location_ = location; + bool focused = !!window; if (focused) - HandlePointerFocusChange(window, focused); + HandlePointerFocusChange(window); EventType type = focused ? ET_MOUSE_ENTERED : ET_MOUSE_EXITED; MouseEvent event(type, location, location, EventTimeForNow(), pointer_flags_, @@ -158,7 +157,7 @@ void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window, DispatchEvent(&event); if (!focused) - HandlePointerFocusChange(window, focused); + HandlePointerFocusChange(nullptr); } void WaylandEventSource::OnPointerButtonEvent(EventType type, @@ -172,6 +171,7 @@ void WaylandEventSource::OnPointerButtonEvent(EventType type, pointer_flags_ = type == ET_MOUSE_PRESSED ? (pointer_flags_ | changed_button) : (pointer_flags_ & ~changed_button); + last_pointer_button_pressed_ = changed_button; // MouseEvent's flags should contain the button that was released too. int flags = pointer_flags_ | keyboard_modifiers_ | changed_button; MouseEvent event(type, pointer_location_, pointer_location_, @@ -322,22 +322,16 @@ void WaylandEventSource::HandleKeyboardFocusChange(WaylandWindow* window, window->set_keyboard_focus(focused); } -void WaylandEventSource::HandlePointerFocusChange(WaylandWindow* window, - bool focused) { - // window can be null on wl_pointer::leave events, for example. - if (window) - window->SetPointerFocus(focused); - - if (focused) { - DCHECK(window); - window_with_pointer_focus_ = window; - } else { - // Focused window might have been destroyed at this point (eg: context - // menus), in this case, |window| is null, otherwise it must be equal to - // |window_with_pointer_focus_|. In both cases, they must be equal. - DCHECK_EQ(window_with_pointer_focus_, window); - window_with_pointer_focus_ = nullptr; - } +void WaylandEventSource::HandlePointerFocusChange(WaylandWindow* window) { + // Focused window might have been destroyed at this point (eg: context menus), + // in this case, |window| is null. + if (window_with_pointer_focus_) + window_with_pointer_focus_->SetPointerFocus(false); + + window_with_pointer_focus_ = window; + + if (window_with_pointer_focus_) + window_with_pointer_focus_->SetPointerFocus(true); } void WaylandEventSource::HandleTouchFocusChange(WaylandWindow* window, 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 7f1a565c613..7c9c409e702 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h @@ -55,6 +55,10 @@ class WaylandEventSource : public PlatformEventSource, WaylandEventSource& operator=(const WaylandEventSource&) = delete; ~WaylandEventSource() override; + int last_pointer_button_pressed() const { + return last_pointer_button_pressed_; + } + // 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. @@ -76,18 +80,17 @@ class WaylandEventSource : public PlatformEventSource, void OnKeyboardDestroyed(WaylandKeyboard* keyboard) override; void OnKeyboardFocusChanged(WaylandWindow* window, bool focused) override; void OnKeyboardModifiersChanged(int modifiers) override; - void OnKeyboardKeyEvent(EventType type, - DomCode dom_code, - DomKey dom_key, - KeyboardCode key_code, - bool repeat, - base::TimeTicks timestamp) override; + uint32_t OnKeyboardKeyEvent(EventType type, + DomCode dom_code, + DomKey dom_key, + KeyboardCode key_code, + bool repeat, + base::TimeTicks timestamp) override; // WaylandPointer::Delegate void OnPointerCreated(WaylandPointer* pointer) override; void OnPointerDestroyed(WaylandPointer* pointer) override; void OnPointerFocusChanged(WaylandWindow* window, - bool focused, const gfx::PointF& location) override; void OnPointerButtonEvent(EventType evtype, int changed_button) override; void OnPointerMotionEvent(const gfx::PointF& location) override; @@ -117,7 +120,7 @@ class WaylandEventSource : public PlatformEventSource, void UpdateKeyboardModifiers(int modifier, bool down); void HandleKeyboardFocusChange(WaylandWindow* window, bool focused); - void HandlePointerFocusChange(WaylandWindow* window, bool focused); + void HandlePointerFocusChange(WaylandWindow* window); void HandleTouchFocusChange(WaylandWindow* window, bool focused, base::Optional<PointerId> id = base::nullopt); @@ -133,6 +136,9 @@ class WaylandEventSource : public PlatformEventSource, // Bitmask of EventFlags used to keep track of the the pointer state. int pointer_flags_ = 0; + // Bitmask of EventFlags used to keep track of the last changed button. + int last_pointer_button_pressed_ = 0; + // Bitmask of EventFlags used to keep track of the the keyboard state. int keyboard_modifiers_ = 0; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc index 0258d157776..7de0fe694d7 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc @@ -34,10 +34,12 @@ const wl_callback_listener WaylandKeyboard::callback_listener_ = { WaylandKeyboard::SyncCallback, }; -WaylandKeyboard::WaylandKeyboard(wl_keyboard* keyboard, - WaylandConnection* connection, - KeyboardLayoutEngine* layout_engine, - Delegate* delegate) +WaylandKeyboard::WaylandKeyboard( + wl_keyboard* keyboard, + zcr_keyboard_extension_v1* keyboard_extension_v1, + WaylandConnection* connection, + KeyboardLayoutEngine* layout_engine, + Delegate* delegate) : obj_(keyboard), connection_(connection), delegate_(delegate), @@ -58,6 +60,10 @@ WaylandKeyboard::WaylandKeyboard(wl_keyboard* keyboard, wl_keyboard_add_listener(obj_.get(), &listener, this); // TODO(tonikitoo): Default auto-repeat to ON here? + + if (keyboard_extension_v1) + extended_keyboard_v1_.reset(zcr_keyboard_extension_v1_get_extended_keyboard( + keyboard_extension_v1, obj_.get())); } WaylandKeyboard::~WaylandKeyboard() { @@ -193,9 +199,15 @@ void WaylandKeyboard::DispatchKey(uint32_t key, // Pass empty DomKey and KeyboardCode here so the delegate can pre-process // and decode it when needed. - delegate_->OnKeyboardKeyEvent(down ? ET_KEY_PRESSED : ET_KEY_RELEASED, - dom_code, DomKey::NONE, - KeyboardCode::VKEY_UNKNOWN, repeat, timestamp); + uint32_t result = delegate_->OnKeyboardKeyEvent( + down ? ET_KEY_PRESSED : ET_KEY_RELEASED, dom_code, DomKey::NONE, + KeyboardCode::VKEY_UNKNOWN, repeat, timestamp); + + if (extended_keyboard_v1_) { + bool handled = result & POST_DISPATCH_STOP_PROPAGATION; + zcr_extended_keyboard_v1_ack_key(extended_keyboard_v1_.get(), + connection_->serial(), handled); + } } bool WaylandKeyboard::Decode(DomCode dom_code, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h index 1630d8aa681..e449da0be85 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h @@ -5,6 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_KEYBOARD_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_KEYBOARD_H_ +#include <keyboard-extension-unstable-v1-client-protocol.h> #include <wayland-client.h> #include "base/time/time.h" @@ -30,6 +31,7 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate { class Delegate; WaylandKeyboard(wl_keyboard* keyboard, + zcr_keyboard_extension_v1* keyboard_extension_v1, WaylandConnection* connection, KeyboardLayoutEngine* keyboard_layout_engine, Delegate* delegate); @@ -88,6 +90,7 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate { int flags) override; wl::Object<wl_keyboard> obj_; + wl::Object<zcr_extended_keyboard_v1> extended_keyboard_v1_; WaylandConnection* const connection_; Delegate* const delegate_; @@ -110,12 +113,18 @@ class WaylandKeyboard::Delegate { virtual void OnKeyboardDestroyed(WaylandKeyboard* keyboard) = 0; virtual void OnKeyboardFocusChanged(WaylandWindow* window, bool focused) = 0; virtual void OnKeyboardModifiersChanged(int modifiers) = 0; - virtual void OnKeyboardKeyEvent(EventType type, - DomCode dom_code, - DomKey dom_key, - KeyboardCode key_code, - bool repeat, - base::TimeTicks timestamp) = 0; + // Returns a mask of ui::PostDispatchAction indicating how the event was + // dispatched. + virtual uint32_t OnKeyboardKeyEvent(EventType type, + DomCode dom_code, + DomKey dom_key, + KeyboardCode key_code, + bool repeat, + base::TimeTicks timestamp) = 0; + + protected: + // Prevent deletion through a WaylandKeyboard::Delegate pointer. + virtual ~Delegate() = default; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc index 8a2da9f409a..1658a68cc93 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc @@ -45,10 +45,10 @@ void WaylandPointer::Enter(void* data, wl_fixed_t surface_y) { DCHECK(data); WaylandPointer* pointer = static_cast<WaylandPointer*>(data); - gfx::PointF location(wl_fixed_to_double(surface_x), - wl_fixed_to_double(surface_y)); - pointer->delegate_->OnPointerFocusChanged(WaylandWindow::FromSurface(surface), - /*focused=*/true, location); + WaylandWindow* window = WaylandWindow::FromSurface(surface); + gfx::PointF location{wl_fixed_to_double(surface_x), + wl_fixed_to_double(surface_y)}; + pointer->delegate_->OnPointerFocusChanged(window, location); } // static @@ -58,8 +58,7 @@ void WaylandPointer::Leave(void* data, wl_surface* surface) { DCHECK(data); WaylandPointer* pointer = static_cast<WaylandPointer*>(data); - pointer->delegate_->OnPointerFocusChanged(WaylandWindow::FromSurface(surface), - /*focused=*/false, {}); + pointer->delegate_->OnPointerFocusChanged(nullptr, {}); } // static diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h index c4c9ecfbc35..b3f3a6ccbfa 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h @@ -73,7 +73,6 @@ class WaylandPointer::Delegate { virtual void OnPointerCreated(WaylandPointer* pointer) = 0; virtual void OnPointerDestroyed(WaylandPointer* pointer) = 0; virtual void OnPointerFocusChanged(WaylandWindow* window, - bool focused, const gfx::PointF& location) = 0; virtual void OnPointerButtonEvent(EventType evtype, int changed_button) = 0; virtual void OnPointerMotionEvent(const gfx::PointF& location) = 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 f20ee54c250..cf12f91f1b7 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc @@ -237,7 +237,7 @@ TEST_P(WaylandPointerTest, SetBitmapOnPointerFocus) { BitmapCursorFactoryOzone cursor_factory; PlatformCursor cursor = - cursor_factory.CreateImageCursor(dummy_cursor, gfx::Point(5, 8), 1.0f); + cursor_factory.CreateImageCursor(dummy_cursor, gfx::Point(5, 8)); scoped_refptr<BitmapCursorOzone> bitmap = BitmapCursorFactoryOzone::GetBitmapCursor(cursor); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc index 116c7be47d1..ba52979990a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc @@ -144,9 +144,7 @@ bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) { } gfx::Rect WaylandPopup::AdjustPopupWindowPosition() { - auto* top_level_parent = wl::IsMenuType(parent_window()->type()) - ? parent_window()->parent_window() - : parent_window(); + 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()); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc index 298ea68b29f..e4fa21fa09a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc @@ -4,6 +4,7 @@ #include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h" +#include "base/logging.h" #include "base/memory/platform_shared_memory_region.h" #include "base/memory/unsafe_shared_memory_region.h" #include "ui/gfx/skia_util.h" diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc index 28f435f39de..e76aa6376dd 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc @@ -7,6 +7,7 @@ #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 { @@ -15,7 +16,7 @@ namespace { gfx::Rect AdjustSubsurfaceBounds(const gfx::Rect& bounds_px, const gfx::Rect& parent_bounds_px, - int32_t ui_scale, + float ui_scale, int32_t buffer_scale) { const auto parent_bounds_dip = gfx::ScaleToRoundedRect(parent_bounds_px, 1.0 / ui_scale); @@ -78,7 +79,7 @@ void WaylandSubsurface::CreateSubsurface() { // windows. If we are in a drag process, use the entered window. Otherwise, // it must be a tooltip. if (connection()->IsDragInProgress()) { - parent = connection()->wayland_data_device()->entered_window(); + parent = connection()->data_drag_controller()->entered_window(); set_parent_window(parent); } else { // If Aura does not not provide a reference parent window, needed by @@ -114,6 +115,11 @@ void WaylandSubsurface::CreateSubsurface() { 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); } bool WaylandSubsurface::OnInitialize(PlatformWindowInitProperties properties) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc index 6f72776d0d3..c2cf21baf62 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc @@ -4,350 +4,21 @@ #include "ui/ozone/platform/wayland/host/wayland_surface.h" -#include "ui/base/dragdrop/drag_drop_types.h" -#include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/base/hit_test.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/ozone/platform/wayland/host/shell_object_factory.h" -#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.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_event_source.h" -#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" namespace ui { -WaylandSurface::WaylandSurface(PlatformWindowDelegate* delegate, - WaylandConnection* connection) - : WaylandWindow(delegate, connection), - state_(PlatformWindowState::kNormal) { - // Set a class property key, which allows |this| to be used for interactive - // events, e.g. move or resize. - SetWmMoveResizeHandler(this, AsWmMoveResizeHandler()); +WaylandSurface::WaylandSurface() = default; +WaylandSurface::~WaylandSurface() = default; - // Set a class property key, which allows |this| to be used for drag action. - SetWmDragHandler(this, this); +gfx::AcceleratedWidget WaylandSurface::GetWidget() const { + if (!surface_) + return gfx::kNullAcceleratedWidget; + return surface_.id(); } -WaylandSurface::~WaylandSurface() { - if (drag_closed_callback_) { - std::move(drag_closed_callback_) - .Run(DragDropTypes::DragOperation::DRAG_NONE); - } -} - -bool WaylandSurface::CreateShellSurface() { - ShellObjectFactory factory; - shell_surface_ = factory.CreateShellSurfaceWrapper(connection(), this); - if (!shell_surface_) { - LOG(ERROR) << "Failed to create a ShellSurface."; - return false; - } - - shell_surface_->SetAppId(app_id_); - shell_surface_->SetTitle(window_title_); - SetSizeConstraints(); - TriggerStateChanges(); - return true; -} - -void WaylandSurface::ApplyPendingBounds() { - if (pending_bounds_dip_.IsEmpty()) - return; - DCHECK(shell_surface_); - - SetBoundsDip(pending_bounds_dip_); - shell_surface_->SetWindowGeometry(pending_bounds_dip_); - pending_bounds_dip_ = gfx::Rect(); - connection()->ScheduleFlush(); -} - -void WaylandSurface::DispatchHostWindowDragMovement( - int hittest, - const gfx::Point& pointer_location_in_px) { - DCHECK(shell_surface_); - - connection()->event_source()->ResetPointerFlags(); - if (hittest == HTCAPTION) - shell_surface_->SurfaceMove(connection()); - else - shell_surface_->SurfaceResize(connection(), hittest); - - connection()->ScheduleFlush(); -} - -void WaylandSurface::StartDrag(const ui::OSExchangeData& data, - int operation, - gfx::NativeCursor cursor, - base::OnceCallback<void(int)> callback) { - DCHECK(!drag_closed_callback_); - drag_closed_callback_ = std::move(callback); - connection()->StartDrag(data, operation); -} - -void WaylandSurface::Show(bool inactive) { - if (shell_surface_) - return; - - if (!CreateShellSurface()) { - Close(); - return; - } - - UpdateBufferScale(false); -} - -void WaylandSurface::Hide() { - if (!shell_surface_) - return; - - if (child_window()) { - child_window()->Hide(); - set_child_window(nullptr); - } - - shell_surface_.reset(); - connection()->ScheduleFlush(); - - // Detach buffer from surface in order to completely shutdown menus and - // tooltips, and release resources. - connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget()); -} - -bool WaylandSurface::IsVisible() const { - // X and Windows return true if the window is minimized. For consistency, do - // the same. - return !!shell_surface_ || state_ == PlatformWindowState::kMinimized; -} - -void WaylandSurface::SetTitle(const base::string16& title) { - if (window_title_ == title) - return; - - window_title_ = title; - - if (shell_surface_) { - shell_surface_->SetTitle(title); - connection()->ScheduleFlush(); - } -} - -void WaylandSurface::ToggleFullscreen() { - // TODO(msisov, tonikitoo): add multiscreen support. As the documentation says - // if xdg_toplevel_set_fullscreen() is not provided with wl_output, it's up - // to the compositor to choose which display will be used to map this surface. - - // We must track the previous state to correctly say our state as long as it - // can be the maximized instead of normal one. - PlatformWindowState new_state = PlatformWindowState::kUnknown; - if (state_ == PlatformWindowState::kFullScreen) { - if (previous_state_ == PlatformWindowState::kMaximized) - new_state = previous_state_; - else - new_state = PlatformWindowState::kNormal; - } else { - new_state = PlatformWindowState::kFullScreen; - } - - SetWindowState(new_state); -} - -void WaylandSurface::Maximize() { - SetWindowState(PlatformWindowState::kMaximized); -} - -void WaylandSurface::Minimize() { - SetWindowState(PlatformWindowState::kMinimized); -} - -void WaylandSurface::Restore() { - DCHECK(shell_surface_); - SetWindowState(PlatformWindowState::kNormal); -} - -PlatformWindowState WaylandSurface::GetPlatformWindowState() const { - return state_; -} - -void WaylandSurface::SizeConstraintsChanged() { - // Size constraints only make sense for normal windows. - if (!shell_surface_) - return; - - DCHECK(delegate()); - min_size_ = delegate()->GetMinimumSizeForWindow(); - max_size_ = delegate()->GetMaximumSizeForWindow(); - SetSizeConstraints(); -} - -void WaylandSurface::HandleSurfaceConfigure(int32_t width, - int32_t height, - bool is_maximized, - bool is_fullscreen, - bool is_activated) { - // Store the old state to propagte state changes if Wayland decides to change - // the state to something else. - PlatformWindowState old_state = state_; - if (state_ == PlatformWindowState::kMinimized && !is_activated) { - state_ = PlatformWindowState::kMinimized; - } else if (is_fullscreen) { - state_ = PlatformWindowState::kFullScreen; - } else if (is_maximized) { - state_ = PlatformWindowState::kMaximized; - } else { - state_ = PlatformWindowState::kNormal; - } - - const bool state_changed = old_state != state_; - const bool is_normal = state_ == PlatformWindowState::kNormal; - - // Update state before notifying delegate. - const bool did_active_change = is_active_ != is_activated; - is_active_ = is_activated; - - // Rather than call SetBounds here for every configure event, just save the - // most recent bounds, and have WaylandConnection call ApplyPendingBounds - // when it has finished processing events. We may get many configure events - // in a row during an interactive resize, and only the last one matters. - // - // Width or height set to 0 means that we should decide on width and height by - // ourselves, but we don't want to set them to anything else. Use restored - // bounds size or the current bounds iff the current state is normal (neither - // maximized nor fullscreen). - // - // Note: if the browser was started with --start-fullscreen and a user exits - // the fullscreen mode, wayland may set the width and height to be 1. Instead, - // explicitly set the bounds to the current desired ones or the previous - // bounds. - if (width > 1 && height > 1) { - pending_bounds_dip_ = gfx::Rect(0, 0, width, height); - } else if (is_normal) { - pending_bounds_dip_.set_size( - gfx::ScaleToRoundedSize(GetRestoredBoundsInPixels().IsEmpty() - ? GetBounds().size() - : GetRestoredBoundsInPixels().size(), - - 1.0 / buffer_scale())); - } - - // Store the restored bounds of current state differs from the normal state. - // It can be client or compositor side change from normal to something else. - // Thus, we must store previous bounds to restore later. - SetOrResetRestoredBounds(); - ApplyPendingBounds(); - - if (state_changed) - delegate()->OnWindowStateChanged(state_); - - if (did_active_change) - delegate()->OnActivationChanged(is_active_); -} - -void WaylandSurface::OnDragEnter(const gfx::PointF& point, - std::unique_ptr<OSExchangeData> data, - int operation) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return; - - // Wayland sends locations in DIP so they need to be translated to - // physical pixels. - drop_handler->OnDragEnter( - gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data), - operation); -} - -int WaylandSurface::OnDragMotion(const gfx::PointF& point, - uint32_t time, - int operation) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return 0; - - // Wayland sends locations in DIP so they need to be translated to - // physical pixels. - return drop_handler->OnDragMotion( - gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation); -} - -void WaylandSurface::OnDragDrop(std::unique_ptr<OSExchangeData> data) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return; - drop_handler->OnDragDrop(std::move(data)); -} - -void WaylandSurface::OnDragLeave() { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return; - drop_handler->OnDragLeave(); -} - -void WaylandSurface::OnDragSessionClose(uint32_t dnd_action) { - std::move(drag_closed_callback_).Run(dnd_action); - connection()->event_source()->ResetPointerFlags(); -} - -bool WaylandSurface::OnInitialize(PlatformWindowInitProperties properties) { - app_id_ = properties.wm_class_class; - return true; -} - -void WaylandSurface::TriggerStateChanges() { - if (!shell_surface_) - return; - - if (state_ == PlatformWindowState::kFullScreen) - shell_surface_->SetFullscreen(); - else - shell_surface_->UnSetFullscreen(); - - // Call UnSetMaximized only if current state is normal. Otherwise, if the - // current state is fullscreen and the previous is maximized, calling - // UnSetMaximized may result in wrong restored window position that clients - // are not allowed to know about. - if (state_ == PlatformWindowState::kMaximized) - shell_surface_->SetMaximized(); - else if (state_ == PlatformWindowState::kNormal) - shell_surface_->UnSetMaximized(); - - if (state_ == PlatformWindowState::kMinimized) - shell_surface_->SetMinimized(); - - connection()->ScheduleFlush(); -} - -void WaylandSurface::SetWindowState(PlatformWindowState state) { - previous_state_ = state_; - state_ = state; - TriggerStateChanges(); -} - -WmMoveResizeHandler* WaylandSurface::AsWmMoveResizeHandler() { - return static_cast<WmMoveResizeHandler*>(this); -} - -void WaylandSurface::SetSizeConstraints() { - if (min_size_.has_value()) - shell_surface_->SetMinSize(min_size_->width(), min_size_->height()); - if (max_size_.has_value()) - shell_surface_->SetMaxSize(max_size_->width(), max_size_->height()); - - connection()->ScheduleFlush(); -} - -void WaylandSurface::SetOrResetRestoredBounds() { - // The |restored_bounds_| are used when the window gets back to normal - // state after it went maximized or fullscreen. So we reset these if the - // window has just become normal and store the current bounds if it is - // either going out of normal state or simply changes the state and we don't - // have any meaningful value stored. - if (GetPlatformWindowState() == PlatformWindowState::kNormal) { - SetRestoredBoundsInPixels({}); - } else if (GetRestoredBoundsInPixels().IsEmpty()) { - SetRestoredBoundsInPixels(GetBounds()); - } +gfx::AcceleratedWidget WaylandSurface::GetRootWidget() const { + return root_window_->GetWidget(); } } // 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 ceda32d24a7..e432ceb7e7d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h @@ -5,121 +5,38 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_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_resize_handler.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" namespace ui { -class ShellSurfaceWrapper; +class WaylandWindow; -class WaylandSurface : public WaylandWindow, - public WmMoveResizeHandler, - public WmDragHandler { +// Wrapper of a wl_surface, owned by a WaylandWindow or a WlSubsurface. +class WaylandSurface { public: - WaylandSurface(PlatformWindowDelegate* delegate, - WaylandConnection* connection); - ~WaylandSurface() override; - - ShellSurfaceWrapper* shell_surface() const { return shell_surface_.get(); } - - // Apply the bounds specified in the most recent configure event. This should - // be called after processing all pending events in the wayland connection. - void ApplyPendingBounds(); + WaylandSurface(); + WaylandSurface(const WaylandSurface&) = delete; + WaylandSurface& operator=(const WaylandSurface&) = delete; + ~WaylandSurface(); - // WmMoveResizeHandler - void DispatchHostWindowDragMovement( - int hittest, - const gfx::Point& pointer_location_in_px) override; + WaylandWindow* root_window() const { return root_window_; } + wl_surface* surface() const { return surface_.get(); } + int32_t buffer_scale() const { return buffer_scale_; } - // WmDragHandler - void StartDrag(const ui::OSExchangeData& data, - int operation, - gfx::NativeCursor cursor, - base::OnceCallback<void(int)> callback) override; - - // PlatformWindow - void Show(bool inactive) override; - void Hide() override; - bool IsVisible() const override; - void SetTitle(const base::string16& title) override; - void ToggleFullscreen() override; - void Maximize() override; - void Minimize() override; - void Restore() override; - PlatformWindowState GetPlatformWindowState() const override; - void SizeConstraintsChanged() override; + // gfx::AcceleratedWidget identifies a wl_surface or a ui::WaylandWindow. Note + // that GetWidget() and GetRootWidget() do not necessarily return the same + // result. + gfx::AcceleratedWidget GetWidget() const; + gfx::AcceleratedWidget GetRootWidget() const; private: - // WaylandWindow overrides: - void HandleSurfaceConfigure(int32_t widht, - int32_t height, - bool is_maximized, - bool is_fullscreen, - bool is_activated) override; - void OnDragEnter(const gfx::PointF& point, - std::unique_ptr<OSExchangeData> data, - int operation) override; - int OnDragMotion(const gfx::PointF& point, - uint32_t time, - int operation) override; - void OnDragDrop(std::unique_ptr<OSExchangeData> data) override; - void OnDragLeave() override; - void OnDragSessionClose(uint32_t dnd_action) override; - bool OnInitialize(PlatformWindowInitProperties properties) override; - - void TriggerStateChanges(); - void SetWindowState(PlatformWindowState state); - - // Creates a surface window, which is visible as a main window. - bool CreateShellSurface(); - - WmMoveResizeHandler* AsWmMoveResizeHandler(); - - // Propagates the |min_size_| and |max_size_| to the ShellSurface. - void SetSizeConstraints(); - - void SetOrResetRestoredBounds(); - - // Wrappers around shell surface. - std::unique_ptr<ShellSurfaceWrapper> shell_surface_; - - base::OnceCallback<void(int)> drag_closed_callback_; - - // These bounds attributes below have suffices that indicate units used. - // Wayland operates in DIP but the platform operates in physical pixels so - // our WaylandSurface is the link that has to translate the units. See also - // comments in the implementation. - // - // Bounds that will be applied when the window state is finalized. The window - // may get several configuration events that update the pending bounds, and - // only upon finalizing the state is the latest value stored as the current - // bounds via |ApplyPendingBounds|. Measured in DIP because updated in the - // handler that receives DIP from Wayland. - gfx::Rect pending_bounds_dip_; - - // Contains the current state of the window. - PlatformWindowState state_; - // Contains the previous state of the window. - PlatformWindowState previous_state_; - - bool is_active_ = false; - - // 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_; - - // Title of the ShellSurface. - base::string16 window_title_; - - // Max and min sizes of the WaylandSurface window. - base::Optional<gfx::Size> min_size_; - base::Optional<gfx::Size> max_size_; - - DISALLOW_COPY_AND_ASSIGN(WaylandSurface); + WaylandWindow* root_window_ = nullptr; + 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; }; } // 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 new file mode 100644 index 00000000000..10ecbcf7b47 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc @@ -0,0 +1,368 @@ +// 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_toplevel_window.h" + +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/hit_test.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/host/shell_object_factory.h" +#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.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_event_source.h" +#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" + +namespace ui { + +WaylandToplevelWindow::WaylandToplevelWindow(PlatformWindowDelegate* delegate, + WaylandConnection* connection) + : WaylandWindow(delegate, connection), + state_(PlatformWindowState::kNormal) { + // Set a class property key, which allows |this| to be used for interactive + // events, e.g. move or resize. + SetWmMoveResizeHandler(this, AsWmMoveResizeHandler()); + + // Set a class property key, which allows |this| to be used for drag action. + SetWmDragHandler(this, this); +} + +WaylandToplevelWindow::~WaylandToplevelWindow() { + if (drag_handler_delegate_) { + drag_handler_delegate_->OnDragFinished( + DragDropTypes::DragOperation::DRAG_NONE); + } +} + +bool WaylandToplevelWindow::CreateShellSurface() { + ShellObjectFactory factory; + shell_surface_ = factory.CreateShellSurfaceWrapper(connection(), this); + if (!shell_surface_) { + LOG(ERROR) << "Failed to create a ShellSurface."; + return false; + } + + shell_surface_->SetAppId(app_id_); + shell_surface_->SetTitle(window_title_); + SetSizeConstraints(); + TriggerStateChanges(); + return true; +} + +void WaylandToplevelWindow::ApplyPendingBounds() { + if (pending_bounds_dip_.IsEmpty()) + return; + DCHECK(shell_surface_); + + SetBoundsDip(pending_bounds_dip_); + shell_surface_->SetWindowGeometry(pending_bounds_dip_); + pending_bounds_dip_ = gfx::Rect(); + connection()->ScheduleFlush(); +} + +void WaylandToplevelWindow::DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location_in_px) { + DCHECK(shell_surface_); + + connection()->event_source()->ResetPointerFlags(); + if (hittest == HTCAPTION) + shell_surface_->SurfaceMove(connection()); + else + shell_surface_->SurfaceResize(connection(), hittest); + + connection()->ScheduleFlush(); +} + +void WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data, + int operation, + gfx::NativeCursor cursor, + WmDragHandler::Delegate* delegate) { + DCHECK(!drag_handler_delegate_); + drag_handler_delegate_ = delegate; + connection()->data_drag_controller()->StartSession(data, operation); +} + +void WaylandToplevelWindow::Show(bool inactive) { + if (shell_surface_) + return; + + if (!CreateShellSurface()) { + Close(); + return; + } + + UpdateBufferScale(false); +} + +void WaylandToplevelWindow::Hide() { + if (!shell_surface_) + return; + + if (child_window()) { + child_window()->Hide(); + set_child_window(nullptr); + } + + shell_surface_.reset(); + connection()->ScheduleFlush(); + + // Detach buffer from surface in order to completely shutdown menus and + // tooltips, and release resources. + connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget()); +} + +bool WaylandToplevelWindow::IsVisible() const { + // X and Windows return true if the window is minimized. For consistency, do + // the same. + return !!shell_surface_ || state_ == PlatformWindowState::kMinimized; +} + +void WaylandToplevelWindow::SetTitle(const base::string16& title) { + if (window_title_ == title) + return; + + window_title_ = title; + + if (shell_surface_) { + shell_surface_->SetTitle(title); + connection()->ScheduleFlush(); + } +} + +void WaylandToplevelWindow::ToggleFullscreen() { + // TODO(msisov, tonikitoo): add multiscreen support. As the documentation says + // if xdg_toplevel_set_fullscreen() is not provided with wl_output, it's up + // to the compositor to choose which display will be used to map this surface. + + // We must track the previous state to correctly say our state as long as it + // can be the maximized instead of normal one. + PlatformWindowState new_state = PlatformWindowState::kUnknown; + if (state_ == PlatformWindowState::kFullScreen) { + if (previous_state_ == PlatformWindowState::kMaximized) + new_state = previous_state_; + else + new_state = PlatformWindowState::kNormal; + } else { + new_state = PlatformWindowState::kFullScreen; + } + + SetWindowState(new_state); +} + +void WaylandToplevelWindow::Maximize() { + SetWindowState(PlatformWindowState::kMaximized); +} + +void WaylandToplevelWindow::Minimize() { + SetWindowState(PlatformWindowState::kMinimized); +} + +void WaylandToplevelWindow::Restore() { + DCHECK(shell_surface_); + SetWindowState(PlatformWindowState::kNormal); +} + +PlatformWindowState WaylandToplevelWindow::GetPlatformWindowState() const { + return state_; +} + +void WaylandToplevelWindow::SizeConstraintsChanged() { + // Size constraints only make sense for normal windows. + if (!shell_surface_) + return; + + DCHECK(delegate()); + min_size_ = delegate()->GetMinimumSizeForWindow(); + max_size_ = delegate()->GetMaximumSizeForWindow(); + SetSizeConstraints(); +} + +void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width, + int32_t height, + bool is_maximized, + bool is_fullscreen, + bool is_activated) { + // Store the old state to propagte state changes if Wayland decides to change + // the state to something else. + PlatformWindowState old_state = state_; + if (state_ == PlatformWindowState::kMinimized && !is_activated) { + state_ = PlatformWindowState::kMinimized; + } else if (is_fullscreen) { + state_ = PlatformWindowState::kFullScreen; + } else if (is_maximized) { + state_ = PlatformWindowState::kMaximized; + } else { + state_ = PlatformWindowState::kNormal; + } + + const bool state_changed = old_state != state_; + const bool is_normal = state_ == PlatformWindowState::kNormal; + + // Update state before notifying delegate. + const bool did_active_change = is_active_ != is_activated; + is_active_ = is_activated; + + // Rather than call SetBounds here for every configure event, just save the + // most recent bounds, and have WaylandConnection call ApplyPendingBounds + // when it has finished processing events. We may get many configure events + // in a row during an interactive resize, and only the last one matters. + // + // Width or height set to 0 means that we should decide on width and height by + // ourselves, but we don't want to set them to anything else. Use restored + // bounds size or the current bounds iff the current state is normal (neither + // maximized nor fullscreen). + // + // Note: if the browser was started with --start-fullscreen and a user exits + // the fullscreen mode, wayland may set the width and height to be 1. Instead, + // explicitly set the bounds to the current desired ones or the previous + // bounds. + if (width > 1 && height > 1) { + pending_bounds_dip_ = gfx::Rect(0, 0, width, height); + } else if (is_normal) { + pending_bounds_dip_.set_size( + gfx::ScaleToRoundedSize(GetRestoredBoundsInPixels().IsEmpty() + ? GetBounds().size() + : GetRestoredBoundsInPixels().size(), + + 1.0 / buffer_scale())); + } + + // Store the restored bounds of current state differs from the normal state. + // It can be client or compositor side change from normal to something else. + // Thus, we must store previous bounds to restore later. + SetOrResetRestoredBounds(); + ApplyPendingBounds(); + + if (state_changed) + delegate()->OnWindowStateChanged(state_); + + if (did_active_change) + delegate()->OnActivationChanged(is_active_); +} + +void WaylandToplevelWindow::OnDragEnter(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation) { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return; + + // Wayland sends locations in DIP so they need to be translated to + // physical pixels. + drop_handler->OnDragEnter( + gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data), + operation); +} + +int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point, + int operation) { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return 0; + + // Wayland sends locations in DIP so they need to be translated to + // physical pixels. + return drop_handler->OnDragMotion( + gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation); +} + +void WaylandToplevelWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return; + drop_handler->OnDragDrop(std::move(data)); +} + +void WaylandToplevelWindow::OnDragLeave() { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return; + drop_handler->OnDragLeave(); +} + +void WaylandToplevelWindow::OnDragSessionClose(uint32_t dnd_action) { + DCHECK(drag_handler_delegate_); + drag_handler_delegate_->OnDragFinished(dnd_action); + drag_handler_delegate_ = nullptr; + connection()->event_source()->ResetPointerFlags(); +} + +bool WaylandToplevelWindow::OnInitialize( + PlatformWindowInitProperties properties) { + app_id_ = properties.wm_class_class; + SetWmMoveLoopHandler(this, static_cast<WmMoveLoopHandler*>(this)); + return true; +} + +bool WaylandToplevelWindow::RunMoveLoop(const gfx::Vector2d& drag_offset) { + DCHECK(connection()->window_drag_controller()); + return connection()->window_drag_controller()->Drag(this, drag_offset); +} + +void WaylandToplevelWindow::EndMoveLoop() { + DCHECK(connection()->window_drag_controller()); + connection()->window_drag_controller()->StopDragging(); +} + +void WaylandToplevelWindow::TriggerStateChanges() { + if (!shell_surface_) + return; + + if (state_ == PlatformWindowState::kFullScreen) + shell_surface_->SetFullscreen(); + else + shell_surface_->UnSetFullscreen(); + + // Call UnSetMaximized only if current state is normal. Otherwise, if the + // current state is fullscreen and the previous is maximized, calling + // UnSetMaximized may result in wrong restored window position that clients + // are not allowed to know about. + if (state_ == PlatformWindowState::kMaximized) + shell_surface_->SetMaximized(); + else if (state_ == PlatformWindowState::kNormal) + shell_surface_->UnSetMaximized(); + + if (state_ == PlatformWindowState::kMinimized) + shell_surface_->SetMinimized(); + + connection()->ScheduleFlush(); +} + +void WaylandToplevelWindow::SetWindowState(PlatformWindowState state) { + previous_state_ = state_; + state_ = state; + TriggerStateChanges(); +} + +WmMoveResizeHandler* WaylandToplevelWindow::AsWmMoveResizeHandler() { + return static_cast<WmMoveResizeHandler*>(this); +} + +void WaylandToplevelWindow::SetSizeConstraints() { + if (min_size_.has_value()) + shell_surface_->SetMinSize(min_size_->width(), min_size_->height()); + if (max_size_.has_value()) + shell_surface_->SetMaxSize(max_size_->width(), max_size_->height()); + + connection()->ScheduleFlush(); +} + +void WaylandToplevelWindow::SetOrResetRestoredBounds() { + // The |restored_bounds_| are used when the window gets back to normal + // state after it went maximized or fullscreen. So we reset these if the + // window has just become normal and store the current bounds if it is + // either going out of normal state or simply changes the state and we don't + // have any meaningful value stored. + if (GetPlatformWindowState() == PlatformWindowState::kNormal) { + SetRestoredBoundsInPixels({}); + } else if (GetRestoredBoundsInPixels().IsEmpty()) { + SetRestoredBoundsInPixels(GetBounds()); + } +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h new file mode 100644 index 00000000000..0455078ee38 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h @@ -0,0 +1,131 @@ +// 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_TOPLEVEL_WINDOW_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_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" + +namespace ui { + +class ShellSurfaceWrapper; + +class WaylandToplevelWindow : public WaylandWindow, + public WmMoveResizeHandler, + public WmDragHandler, + public WmMoveLoopHandler { + public: + WaylandToplevelWindow(PlatformWindowDelegate* delegate, + WaylandConnection* connection); + WaylandToplevelWindow(const WaylandToplevelWindow&) = delete; + WaylandToplevelWindow& operator=(const WaylandToplevelWindow&) = delete; + ~WaylandToplevelWindow() override; + + ShellSurfaceWrapper* shell_surface() const { return shell_surface_.get(); } + + // Apply the bounds specified in the most recent configure event. This should + // be called after processing all pending events in the wayland connection. + void ApplyPendingBounds(); + + // WmMoveResizeHandler + void DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location_in_px) override; + + // WmDragHandler + void StartDrag(const ui::OSExchangeData& data, + int operation, + gfx::NativeCursor cursor, + WmDragHandler::Delegate* delegate) override; + + // PlatformWindow + void Show(bool inactive) override; + void Hide() override; + bool IsVisible() const override; + void SetTitle(const base::string16& title) override; + void ToggleFullscreen() override; + void Maximize() override; + void Minimize() override; + void Restore() override; + PlatformWindowState GetPlatformWindowState() const override; + void SizeConstraintsChanged() override; + + private: + // WaylandWindow overrides: + void HandleSurfaceConfigure(int32_t widht, + int32_t height, + bool is_maximized, + bool is_fullscreen, + bool is_activated) override; + void OnDragEnter(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation) override; + int OnDragMotion(const gfx::PointF& point, int operation) override; + void OnDragDrop(std::unique_ptr<OSExchangeData> data) override; + void OnDragLeave() override; + void OnDragSessionClose(uint32_t dnd_action) override; + bool OnInitialize(PlatformWindowInitProperties properties) override; + + // WmMoveLoopHandler: + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; + void EndMoveLoop() override; + + void TriggerStateChanges(); + void SetWindowState(PlatformWindowState state); + + // Creates a surface window, which is visible as a main window. + bool CreateShellSurface(); + + WmMoveResizeHandler* AsWmMoveResizeHandler(); + + // Propagates the |min_size_| and |max_size_| to the ShellSurface. + void SetSizeConstraints(); + + void SetOrResetRestoredBounds(); + + // Wrappers around shell surface. + std::unique_ptr<ShellSurfaceWrapper> shell_surface_; + + WmDragHandler::Delegate* drag_handler_delegate_ = nullptr; + + // These bounds attributes below have suffices that indicate units used. + // Wayland operates in DIP but the platform operates in physical pixels so + // our WaylandToplevelWindow is the link that has to translate the units. See + // also comments in the implementation. + // + // Bounds that will be applied when the window state is finalized. The window + // may get several configuration events that update the pending bounds, and + // only upon finalizing the state is the latest value stored as the current + // bounds via |ApplyPendingBounds|. Measured in DIP because updated in the + // handler that receives DIP from Wayland. + gfx::Rect pending_bounds_dip_; + + // Contains the current state of the window. + PlatformWindowState state_; + // Contains the previous state of the window. + PlatformWindowState previous_state_; + + bool is_active_ = false; + + // 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_; + + // Title of the ShellSurface. + base::string16 window_title_; + + // Max and min sizes of the WaylandToplevelWindow window. + base::Optional<gfx::Size> min_size_; + base::Optional<gfx::Size> max_size_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc index 9298cb3acbc..21ebb192e5a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc @@ -30,7 +30,7 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, WaylandWindow::~WaylandWindow() { PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - if (surface_) + if (surface()) connection_->wayland_window_manager()->RemoveWindow(GetWidget()); if (parent_window_) @@ -61,7 +61,7 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) { int32_t new_scale = 0; if (parent_window_) { - new_scale = parent_window_->buffer_scale_; + new_scale = parent_window_->buffer_scale(); ui_scale_ = parent_window_->ui_scale_; } else { const auto display = (widget == gfx::kNullAcceleratedWidget) @@ -80,9 +80,7 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) { } gfx::AcceleratedWidget WaylandWindow::GetWidget() const { - if (!surface_) - return gfx::kNullAcceleratedWidget; - return surface_.id(); + return wayland_surface_.GetWidget(); } void WaylandWindow::SetPointerFocus(bool focus) { has_pointer_focus_ = focus; @@ -163,7 +161,7 @@ void WaylandWindow::Restore() {} PlatformWindowState WaylandWindow::GetPlatformWindowState() const { // Remove normal state for all the other types of windows as it's only the - // WaylandSurface that supports state changes. + // WaylandToplevelWindow that supports state changes. return PlatformWindowState::kNormal; } @@ -250,6 +248,12 @@ uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) { auto* event_grabber = connection_->wayland_window_manager()->located_events_grabber(); auto* root_parent_window = GetRootParentWindow(); + + // Wayland sends locations in DIP so they need to be translated to + // physical pixels. + event->AsLocatedEvent()->set_location_f(gfx::ScalePoint( + event->AsLocatedEvent()->location_f(), buffer_scale(), buffer_scale())); + // We must reroute the events to the event grabber iff these windows belong // to the same root parent window. For example, there are 2 top level // Wayland windows. One of them (window_1) has a child menu window that is @@ -297,9 +301,7 @@ void WaylandWindow::OnDragEnter(const gfx::PointF& point, std::unique_ptr<OSExchangeData> data, int operation) {} -int WaylandWindow::OnDragMotion(const gfx::PointF& point, - uint32_t time, - int operation) { +int WaylandWindow::OnDragMotion(const gfx::PointF& point, int operation) { return -1; } @@ -310,24 +312,26 @@ void WaylandWindow::OnDragLeave() {} void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) {} void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) { - SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale_)); + SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale())); } bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { // 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); + DCHECK_EQ(buffer_scale(), 1); bounds_px_ = properties.bounds; opacity_ = properties.opacity; type_ = properties.type; - surface_.reset(wl_compositor_create_surface(connection_->compositor())); - if (!surface_) { + 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_.get(), this); + wl_surface_set_user_data(surface(), this); AddSurfaceListener(); connection_->wayland_window_manager()->AddWindow(GetWidget(), this); @@ -350,16 +354,16 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { void WaylandWindow::SetBufferScale(int32_t new_scale, bool update_bounds) { DCHECK_GT(new_scale, 0); - if (new_scale == buffer_scale_) + if (new_scale == buffer_scale()) return; - auto old_scale = buffer_scale_; - buffer_scale_ = new_scale; + 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_); + wl_surface_set_buffer_scale(surface(), buffer_scale()); connection_->ScheduleFlush(); } @@ -377,11 +381,12 @@ WaylandWindow* WaylandWindow::GetParentWindow( // Another case is a notifcation window or a drop down window, which do not // have a parent in aura. In this case, take the current focused window as a // parent. - if (parent_window && parent_window->child_window_) - return parent_window->child_window_; + if (!parent_window) - return connection_->wayland_window_manager()->GetCurrentFocusedWindow(); - return parent_window; + parent_window = + connection_->wayland_window_manager()->GetCurrentFocusedWindow(); + + return parent_window ? parent_window->GetTopMostChildWindow() : nullptr; } WaylandWindow* WaylandWindow::GetRootParentWindow() { @@ -393,7 +398,7 @@ void WaylandWindow::AddSurfaceListener() { &WaylandWindow::Enter, &WaylandWindow::Leave, }; - wl_surface_add_listener(surface_.get(), &surface_listener, this); + wl_surface_add_listener(surface(), &surface_listener, this); } void WaylandWindow::AddEnteredOutputId(struct wl_output* output) { @@ -468,6 +473,10 @@ WaylandWindow* WaylandWindow::GetTopLevelWindow() { return parent_window_ ? parent_window_->GetTopLevelWindow() : this; } +WaylandWindow* WaylandWindow::GetTopMostChildWindow() { + return child_window_ ? child_window_->GetTopMostChildWindow() : this; +} + void WaylandWindow::MaybeUpdateOpaqueRegion() { if (!IsOpaqueWindow()) return; @@ -490,10 +499,10 @@ uint32_t WaylandWindow::DispatchEventToDelegate( if (event->IsLocatedEvent()) UpdateCursorPositionFromEvent(Event::Clone(*event)); - DispatchEventFromNativeUiEvent( + bool handled = DispatchEventFromNativeUiEvent( native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, base::Unretained(delegate_))); - return POST_DISPATCH_STOP_PROPAGATION; + return handled ? POST_DISPATCH_STOP_PROPAGATION : POST_DISPATCH_NONE; } // static @@ -502,7 +511,7 @@ void WaylandWindow::Enter(void* data, struct wl_output* output) { auto* window = static_cast<WaylandWindow*>(data); if (window) { - DCHECK(window->surface_.get() == wl_surface); + DCHECK(window->surface() == wl_surface); window->AddEnteredOutputId(output); } } @@ -513,7 +522,7 @@ void WaylandWindow::Leave(void* data, struct wl_output* output) { auto* window = static_cast<WaylandWindow*>(data); if (window) { - DCHECK(window->surface_.get() == wl_surface); + DCHECK(window->surface() == wl_surface); window->RemoveEnteredOutputId(output); } } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_window.h index 43cfa54d4cc..9c42eb59c80 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.h @@ -17,6 +17,7 @@ #include "ui/gfx/geometry/rect.h" #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/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -36,7 +37,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { ~WaylandWindow() override; // A factory method that can create any of the derived types of WaylandWindow - // (WaylandSurface, WaylandPopup and WaylandSubsurface). + // (WaylandToplevelWindow, WaylandPopup and WaylandSubsurface). static std::unique_ptr<WaylandWindow> Create( PlatformWindowDelegate* delegate, WaylandConnection* connection, @@ -53,7 +54,8 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // to do so (this is not needed upon window initialization). void UpdateBufferScale(bool update_bounds); - wl_surface* surface() const { return surface_.get(); } + WaylandSurface* wayland_surface() { return &wayland_surface_; } + wl_surface* surface() const { return wayland_surface_.surface(); } void set_parent_window(WaylandWindow* parent_window) { parent_window_ = parent_window; @@ -80,7 +82,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 buffer_scale_; } + int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); } int32_t ui_scale() const { return ui_scale_; } const base::flat_set<uint32_t>& entered_outputs_ids() const { @@ -144,12 +146,17 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { std::unique_ptr<OSExchangeData> data, int operation); virtual int OnDragMotion(const gfx::PointF& point, - uint32_t time, int operation); virtual void OnDragDrop(std::unique_ptr<OSExchangeData> data); virtual void OnDragLeave(); virtual void OnDragSessionClose(uint32_t dnd_action); + // Returns a root parent window within the same hierarchy. + WaylandWindow* GetRootParentWindow(); + + // Returns a top most child window within the same hierarchy. + WaylandWindow* GetTopMostChildWindow(); + protected: WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection); @@ -175,9 +182,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Initializes the WaylandWindow with supplied properties. bool Initialize(PlatformWindowInitProperties properties); - // Returns a root parent window. - WaylandWindow* GetRootParentWindow(); - // Install a surface listener and start getting wl_output enter/leave events. void AddSurfaceListener(); @@ -212,7 +216,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { WaylandWindow* parent_window_ = nullptr; WaylandWindow* child_window_ = nullptr; - wl::Object<wl_surface> surface_; + WaylandSurface wayland_surface_; // The current cursor bitmap (immutable). scoped_refptr<BitmapCursorOzone> bitmap_; @@ -225,9 +229,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { bool has_pointer_focus_ = false; bool has_keyboard_focus_ = false; bool has_touch_focus_ = false; - // Wayland's scale factor for the output that this window currently belongs - // to. - int32_t buffer_scale_ = 1; // The UI scale may be forced through the command line, which means that it // replaces the default value that is equal to the natural device scale. // We need it to place and size the menus properly. 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 new file mode 100644 index 00000000000..08d5cb633ca --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc @@ -0,0 +1,325 @@ +// 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_window_drag_controller.h" + +#include <cstdint> +#include <memory> +#include <ostream> +#include <utility> + +#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 "ui/base/dragdrop/drag_drop_types.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/scoped_event_dispatcher.h" +#include "ui/events/platform_event.h" +#include "ui/events/types/event_type.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" +#include "ui/ozone/platform/wayland/host/wayland_data_offer.h" +#include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/platform/wayland/host/wayland_event_source.h" +#include "ui/ozone/platform/wayland/host/wayland_pointer.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_window_manager.h" + +namespace ui { + +namespace { + +// Custom mime type used for window dragging DND sessions. +constexpr char kMimeTypeChromiumWindow[] = "chromium/x-window"; + +// DND action used in window dragging DND sessions. +constexpr uint32_t kDndActionWindowDrag = + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + +} // namespace + +WaylandWindowDragController::WaylandWindowDragController( + WaylandConnection* connection, + WaylandDataDeviceManager* device_manager, + WaylandPointer::Delegate* pointer_delegate) + : connection_(connection), + data_device_manager_(device_manager), + data_device_(device_manager->GetDevice()), + window_manager_(connection_->wayland_window_manager()), + pointer_delegate_(pointer_delegate) { + DCHECK(data_device_); + DCHECK(pointer_delegate_); +} + +WaylandWindowDragController::~WaylandWindowDragController() = default; + +bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window, + const gfx::Vector2d& offset) { + DCHECK_LE(state_, State::kAttached); + DCHECK(window); + + if (!OfferWindow()) + return false; + + DCHECK_EQ(state_, State::kAttached); + dragged_window_ = window; + drag_offset_ = offset; + RunLoop(); + + DCHECK(state_ == State::kAttached || state_ == State::kDropped); + bool dropped = state_ == State::kDropped; + if (dropped) + HandleDropAndResetState(); + return dropped; +} + +void WaylandWindowDragController::StopDragging() { + if (state_ != State::kDetached) + return; + + 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. + state_ = State::kAttached; + QuitLoop(); +} + +bool WaylandWindowDragController::IsDragSource() const { + DCHECK(data_source_); + return true; +} + +// Icon drawing and update for window/tab dragging is handled by buffer manager. +void WaylandWindowDragController::DrawIcon() {} + +void WaylandWindowDragController::OnDragOffer( + std::unique_ptr<WaylandDataOffer> offer) { + DCHECK_GE(state_, State::kAttached); + DCHECK(offer); + DCHECK(!data_offer_); + data_offer_ = std::move(offer); +} + +void WaylandWindowDragController::OnDragEnter(WaylandWindow* window, + const gfx::PointF& location, + uint32_t serial) { + DCHECK_GE(state_, State::kAttached); + DCHECK(window); + + // Forward focus change event to the input delegate, so other components, such + // as WaylandScreen, are able to properly retrieve focus related info during + // window dragging sesstions. + pointer_delegate_->OnPointerFocusChanged(window, location); + + VLOG(1) << "OnEnter. widget=" << window->GetWidget(); + + // 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); + + // Accept the offer and set the dnd action. + data_offer_->SetAction(kDndActionWindowDrag, kDndActionWindowDrag); + data_offer_->Accept(serial, kMimeTypeChromiumWindow); +} + +void WaylandWindowDragController::OnDragMotion(const gfx::PointF& location) { + DCHECK_GE(state_, State::kAttached); + VLOG(2) << "OnMotion. location=" << location.ToString(); + + // Forward cursor location update info to the input handling delegate. + pointer_delegate_->OnPointerMotionEvent(location); +} + +void WaylandWindowDragController::OnDragLeave() { + DCHECK_GE(state_, State::kAttached); + DCHECK_LE(state_, State::kDetached); + + // In order to guarantee ET_MOUSE_RELEASED event is delivered once the DND + // session finishes, the focused window is not reset here. This is similar to + // the "implicit grab" behavior implemented by Wayland compositors for + // wl_pointer events. Additionally, this makes it possible for the drag + // controller to overcome deviations in the order that wl_data_source and + // wl_pointer events arrive when the drop happens. For example, unlike Weston + // and Sway, Gnome Shell <= 2.26 sends them in the following order: + // + // wl_data_device.leave > wl_pointer.enter > wl_data_source.cancel/finish + // + // which would require hacky workarounds in HandleDropAndResetState function + // to properly detect and handle such cases. + + VLOG(1) << "OnLeave"; + + data_offer_.reset(); +} + +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}. +} + +void WaylandWindowDragController::OnDataSourceFinish(bool completed) { + DCHECK_GE(state_, State::kAttached); + DCHECK(data_source_); + + VLOG(1) << "Drop received. state=" << state_; + + // Release DND objects. + data_offer_.reset(); + data_source_.reset(); + icon_surface_.reset(); + dragged_window_ = nullptr; + + // Transition to |kDropped| state and determine the next action to take. If + // drop happened while the move loop was running (i.e: kDetached), ask to quit + // the loop, otherwise notify session end and reset state right away. + State state_when_dropped = std::exchange(state_, State::kDropped); + if (state_when_dropped == State::kDetached) + QuitLoop(); + else + HandleDropAndResetState(); + + data_device_->ResetDragDelegate(); +} + +void WaylandWindowDragController::OnDataSourceSend(const std::string& mime_type, + std::string* contents) { + // There is no actual data exchange in DnD window dragging sessions. Window + // snapping, for example, is supposed to be handled at higher level UI layers. +} + +bool WaylandWindowDragController::CanDispatchEvent(const PlatformEvent& event) { + return state_ == State::kDetached; +} + +uint32_t WaylandWindowDragController::DispatchEvent( + const PlatformEvent& event) { + DCHECK_EQ(state_, State::kDetached); + DCHECK(base::MessageLoopCurrentForUI::IsSet()); + + VLOG(2) << "Dispatch. event=" << event->GetName(); + + if (event->type() == ET_MOUSE_MOVED || event->type() == ET_MOUSE_DRAGGED) { + HandleMotionEvent(event->AsMouseEvent()); + return POST_DISPATCH_STOP_PROPAGATION; + } + 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::HandleMotionEvent(MouseEvent* event) { + DCHECK_EQ(state_, State::kDetached); + DCHECK(dragged_window_); + DCHECK(event); + + // Update current cursor position, so it can be retrieved later on through + // |Screen::GetCursorScreenPoint| API. + int32_t scale = dragged_window_->buffer_scale(); + gfx::PointF scaled_location = + gfx::ScalePoint(event->location_f(), scale, scale); + connection_->wayland_cursor_position()->OnCursorPositionChanged( + gfx::ToFlooredPoint(scaled_location)); + + // Notify listeners about window bounds change (i.e: re-positioning) event. + // To do so, set the new bounds as per the motion event location and the drag + // offset. Note that setting a new location (i.e: bounds.origin()) for a + // surface has no visual effect in ozone/wayland backend. Actual window + // re-positioning during dragging session is done through the drag icon. + gfx::Point new_location = event->location() - drag_offset_; + gfx::Size size = dragged_window_->GetBounds().size(); + dragged_window_->SetBounds({new_location, size}); +} + +// Dispatch mouse release event (to tell clients that the drop just happened) +// clear focus and reset internal state. Must be called when the session is +// about to finish. +void WaylandWindowDragController::HandleDropAndResetState() { + DCHECK_EQ(state_, State::kDropped); + DCHECK(window_manager_->GetCurrentFocusedWindow()); + VLOG(1) << "Notifying drop. window=" + << window_manager_->GetCurrentFocusedWindow(); + + EventFlags pointer_button = EF_LEFT_MOUSE_BUTTON; + DCHECK(connection_->event_source()->IsPointerButtonPressed(pointer_button)); + pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button); + + state_ = State::kIdle; +} + +void WaylandWindowDragController::RunLoop() { + DCHECK_EQ(state_, State::kAttached); + DCHECK(dragged_window_); + + VLOG(1) << "Starting drag loop. widget=" << dragged_window_->GetWidget(); + + // TODO(crbug.com/896640): Handle cursor + auto old_dispatcher = std::move(nested_dispatcher_); + nested_dispatcher_ = + PlatformEventSource::GetInstance()->OverrideDispatcher(this); + + base::WeakPtr<WaylandWindowDragController> alive(weak_factory_.GetWeakPtr()); + + state_ = State::kDetached; + base::RunLoop loop(base::RunLoop::Type::kNestableTasksAllowed); + quit_loop_closure_ = loop.QuitClosure(); + loop.Run(); + + if (!alive) + return; + + nested_dispatcher_ = std::move(old_dispatcher); + + VLOG(1) << "Quitting drag loop " << state_; +} + +void WaylandWindowDragController::QuitLoop() { + DCHECK(!quit_loop_closure_.is_null()); + + nested_dispatcher_.reset(); + std::move(quit_loop_closure_).Run(); +} + +std::ostream& operator<<(std::ostream& out, + WaylandWindowDragController::State state) { + return out << static_cast<int>(state); +} + +} // namespace ui 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 new file mode 100644 index 00000000000..071cc9672bc --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h @@ -0,0 +1,126 @@ +// 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_WINDOW_DRAG_CONTROLLER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_ + +#include <cstdint> +#include <iosfwd> +#include <memory> +#include <string> + +#include "base/callback_forward.h" +#include "base/memory/weak_ptr.h" +#include "ui/events/event.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/scoped_event_dispatcher.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device.h" +#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" + +namespace ui { + +class WaylandConnection; +class WaylandDataDeviceManager; +class WaylandDataOffer; +class WaylandWindow; +class WaylandWindowManager; + +// Drag controller implementation that drives window moving sessions (aka: tab +// dragging). Wayland Drag and Drop protocol is used, under the hood, to keep +// track of cursor location and surface focus. +// +// TODO(crbug.com/896640): Use drag icon to emulate window moving. +class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, + public WaylandDataSource::Delegate, + public PlatformEventDispatcher { + public: + // Constants used to keep track of the drag controller state. + enum class State { + kIdle, // No DnD session nor drag loop running. + kAttached, // DnD session ongoing but no drag loop running. + kDetached, // Drag loop running. ie: blocked in a Drag() call. + kDropped // Drop event was just received. + }; + + WaylandWindowDragController(WaylandConnection* connection, + WaylandDataDeviceManager* device_manager, + WaylandPointer::Delegate* pointer_delegate); + WaylandWindowDragController(const WaylandWindowDragController&) = delete; + WaylandWindowDragController& operator=(const WaylandWindowDragController&) = + delete; + ~WaylandWindowDragController() override; + + bool Drag(WaylandToplevelWindow* surface, const gfx::Vector2d& offset); + void StopDragging(); + + State state() const { return state_; } + + private: + // WaylandDataDevice::DragDelegate: + bool IsDragSource() const override; + void DrawIcon() override; + void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) override; + void OnDragEnter(WaylandWindow* window, + const gfx::PointF& location, + uint32_t serial) override; + void OnDragMotion(const gfx::PointF& location) override; + void OnDragLeave() override; + void OnDragDrop() override; + + // WaylandDataSource::Delegate + void OnDataSourceFinish(bool completed) override; + void OnDataSourceSend(const std::string& mime_type, + std::string* contents) override; + + // PlatformEventDispatcher + 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(); + // 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); + // Handles the mouse button release (i.e: drop). Dispatches the required + // events and resets the internal state. + void HandleDropAndResetState(); + // Registers as the top level PlatformEvent dispatcher and runs a nested + // RunLoop, which blocks until the DnD session finishes. + void RunLoop(); + // Unregisters the internal event dispatcher and asks to quit the nested loop. + void QuitLoop(); + + WaylandConnection* const connection_; + WaylandDataDeviceManager* const data_device_manager_; + WaylandDataDevice* const data_device_; + WaylandWindowManager* const window_manager_; + 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_; + + std::unique_ptr<ScopedEventDispatcher> nested_dispatcher_; + base::OnceClosure quit_loop_closure_; + + base::WeakPtrFactory<WaylandWindowDragController> weak_factory_{this}; +}; + +// Stream operator so WaylandWindowDragController::State can be used in +// log/assertion statements. +std::ostream& operator<<(std::ostream& out, + WaylandWindowDragController::State state); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_ 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 new file mode 100644 index 00000000000..56a13b46e48 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc @@ -0,0 +1,519 @@ +// 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 <linux/input-event-codes.h> +#include <wayland-server-protocol.h> +#include <wayland-server.h> +#include <wayland-util.h> + +#include <cstdint> + +#include "base/notreached.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/event.h" +#include "ui/events/types/event_type.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device.h" +#include "ui/ozone/platform/wayland/host/wayland_screen.h" +#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" +#include "ui/ozone/platform/wayland/test/test_data_device_manager.h" +#include "ui/ozone/platform/wayland/test/test_data_offer.h" +#include "ui/ozone/platform/wayland/test/test_data_source.h" +#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_delegate.h" +#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h" + +using testing::_; +using testing::Mock; + +namespace ui { + +class WaylandWindowDragControllerTest : public WaylandTest, + public wl::TestDataDevice::Delegate { + public: + WaylandWindowDragControllerTest() = default; + ~WaylandWindowDragControllerTest() override = default; + + void SetUp() override { + WaylandTest::SetUp(); + screen_ = std::make_unique<WaylandScreen>(connection_.get()); + + wl_seat_send_capabilities(server_.seat()->resource(), + WL_SEAT_CAPABILITY_POINTER); + Sync(); + pointer_ = server_.seat()->pointer(); + ASSERT_TRUE(pointer_); + + EXPECT_FALSE(window_->has_pointer_focus()); + EXPECT_EQ(State::kIdle, drag_controller()->state()); + + data_device_manager_ = server_.data_device_manager(); + DCHECK(data_device_manager_); + + source_ = nullptr; + data_device_manager_->data_device()->set_delegate(this); + } + + void TearDown() override { + data_device_manager_->data_device()->set_delegate(nullptr); + } + + WaylandWindowDragController* drag_controller() const { + return connection_->window_drag_controller(); + } + + WaylandWindowManager* window_manager() const { + return connection_->wayland_window_manager(); + } + + uint32_t NextSerial() const { + static uint32_t serial = 0; + return ++serial; + } + + uint32_t NextTime() const { + static uint32_t timestamp = 0; + return ++timestamp; + } + + protected: + using State = WaylandWindowDragController::State; + + // wl::TestDataDevice::Delegate: + void StartDrag(wl::TestDataSource* source, + wl::MockSurface* origin, + uint32_t serial) override { + EXPECT_FALSE(source_); + source_ = source; + OfferAndEnter(origin); + } + + // Helper functions + void SendDndMotion(const gfx::Point& location) { + EXPECT_TRUE(source_); + wl_fixed_t x = wl_fixed_from_int(location.x()); + wl_fixed_t y = wl_fixed_from_int(location.y()); + data_device_manager_->data_device()->OnMotion(NextTime(), x, y); + } + + void SendDndEnter(WaylandWindow* window) { + EXPECT_TRUE(window); + OfferAndEnter(server_.GetObject<wl::MockSurface>(window->GetWidget())); + } + + void SendDndLeave() { + EXPECT_TRUE(source_); + data_device_manager_->data_device()->OnLeave(); + } + + void SendDndDrop() { + EXPECT_TRUE(source_); + source_->OnCancelled(); + } + + void SendPointerEnter(WaylandWindow* window, + MockPlatformWindowDelegate* delegate) { + auto* surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); + wl_pointer_send_enter(pointer_->resource(), NextSerial(), + surface->resource(), 0, 0); + EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1); + Sync(); + + EXPECT_EQ(window, window_manager()->GetCurrentFocusedWindow()); + } + + void SendPointerPress(WaylandWindow* window, + MockPlatformWindowDelegate* delegate, + int button) { + wl_pointer_send_button(pointer_->resource(), NextSerial(), NextTime(), + button, WL_POINTER_BUTTON_STATE_PRESSED); + EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1); + Sync(); + + EXPECT_EQ(window, window_manager()->GetCurrentFocusedWindow()); + } + + void SendPointerMotion(WaylandWindow* window, + MockPlatformWindowDelegate* delegate, + gfx::Point location) { + wl_fixed_t x = wl_fixed_from_int(location.x()); + wl_fixed_t y = wl_fixed_from_int(location.y()); + wl_pointer_send_motion(pointer_->resource(), NextTime(), x, y); + EXPECT_CALL(*delegate, DispatchEvent(_)).WillOnce([](Event* event) { + EXPECT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ET_MOUSE_DRAGGED, event->type()); + }); + Sync(); + + EXPECT_EQ(window->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint(location, {})); + } + + void OfferAndEnter(wl::MockSurface* surface) { + EXPECT_TRUE(source_); + auto* data_device = data_device_manager_->data_device(); + auto* offer = data_device->OnDataOffer(); + EXPECT_EQ(1u, source_->mime_types().size()); + for (const auto& mime_type : source_->mime_types()) + offer->OnOffer(mime_type, {}); + + wl_data_device_send_enter(data_device->resource(), NextSerial(), + surface->resource(), 0, 0, offer->resource()); + } + + // client objects + std::unique_ptr<WaylandScreen> screen_; + + // server objects + wl::TestDataDeviceManager* data_device_manager_; + wl::TestDataSource* source_; + wl::MockPointer* pointer_; +}; + +// Check the following flow works as expected: +// 1. With a single 1 window open, +// 2. Move pointer into it, press left button, move cursor a bit (drag), +// 3. Run move loop, drag it within the window bounds and drop. +TEST_P(WaylandWindowDragControllerTest, DragInsideWindowAndDrop) { + // 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}); + + // Set up an "interaction flow" and RunMoveLoop: + // - 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* move_loop_handler = GetWmMoveLoopHandler(*window_); + DCHECK(move_loop_handler); + + enum { kStarted, kDragging, kDropping, kDone } test_step = kStarted; + + EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) { + EXPECT_TRUE(event->IsMouseEvent()); + switch (test_step) { + case kStarted: + EXPECT_EQ(ET_MOUSE_ENTERED, event->type()); + EXPECT_EQ(State::kDetached, drag_controller()->state()); + // Ensure PlatformScreen keeps consistent. + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + // Drag it a bit more. + SendDndMotion({20, 20}); + test_step = kDragging; + break; + case kDropping: + EXPECT_EQ(ET_MOUSE_RELEASED, event->type()); + EXPECT_EQ(State::kDropped, drag_controller()->state()); + // Ensure PlatformScreen keeps consistent. + EXPECT_EQ(gfx::Point(20, 20), screen_->GetCursorScreenPoint()); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); + test_step = kDone; + break; + case kDone: + EXPECT_EQ(ET_MOUSE_EXITED, event->type()); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); + break; + case kDragging: + default: + FAIL() << " event=" << event->GetName() + << " state=" << drag_controller()->state() + << " step=" << static_cast<int>(test_step); + return; + } + }); + + EXPECT_CALL(delegate_, OnBoundsChanged(_)) + .WillOnce([&](const gfx::Rect& bounds) { + EXPECT_EQ(State::kDetached, drag_controller()->state()); + EXPECT_EQ(kDragging, test_step); + EXPECT_EQ(gfx::Point(20, 20), bounds.origin()); + + SendDndDrop(); + test_step = kDropping; + }); + + // RunMoveLoop() blocks until the dragging session ends, so resume test + // server's run loop until it returns. + server_.Resume(); + move_loop_handler->RunMoveLoop({}); + server_.Pause(); + + SendPointerEnter(window_.get(), &delegate_); + Sync(); + + EXPECT_EQ(State::kIdle, drag_controller()->state()); + EXPECT_EQ(window_.get(), window_manager()->GetCurrentFocusedWindow()); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); +} + +// Check the following flow works as expected: +// 1. With only 1 window open; +// 2. Move pointer into it, press left button, move cursor a bit (drag); +// 3. Run move loop, +// 4. Drag pointer to outside the window and release the mouse button, and make +// sure RELEASE and EXIT mouse events are delivered even when the drop +// happens outside the bounds of any surface. +TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) { + // 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}); + + // Sets up an "interaction flow" and RunMoveLoop: + // - 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* move_loop_handler = GetWmMoveLoopHandler(*window_); + DCHECK(move_loop_handler); + + enum { + kStarted, + kDragging, + kExitedWindow, + kDropping, + kDone + } test_step = kStarted; + + EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) { + EXPECT_TRUE(event->IsMouseEvent()); + switch (test_step) { + case kStarted: + EXPECT_EQ(ET_MOUSE_ENTERED, event->type()); + EXPECT_EQ(State::kDetached, drag_controller()->state()); + // Ensure PlatformScreen keeps consistent. + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + // Drag window a bit more. + 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: + EXPECT_EQ(ET_MOUSE_RELEASED, event->type()); + EXPECT_EQ(State::kDropped, drag_controller()->state()); + // Ensure PlatformScreen keeps consistent. + EXPECT_EQ(gfx::Point(20, 20), screen_->GetCursorScreenPoint()); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); + test_step = kDone; + break; + case kDone: + EXPECT_EQ(ET_MOUSE_EXITED, event->type()); + break; + case kDragging: + default: + FAIL() << " event=" << event->GetName() + << " state=" << drag_controller()->state() + << " step=" << static_cast<int>(test_step); + return; + } + }); + + EXPECT_CALL(delegate_, OnBoundsChanged(_)) + .WillOnce([&](const gfx::Rect& bounds) { + EXPECT_EQ(State::kDetached, drag_controller()->state()); + EXPECT_EQ(kDragging, test_step); + EXPECT_EQ(gfx::Point(20, 20), bounds.origin()); + + SendDndDrop(); + test_step = kDropping; + }); + + // RunMoveLoop() blocks until the dragging sessions ends, so resume test + // server's run loop until it returns. + server_.Resume(); + move_loop_handler->RunMoveLoop({}); + server_.Pause(); + + SendPointerEnter(window_.get(), &delegate_); + Sync(); + + EXPECT_EQ(State::kIdle, drag_controller()->state()); + EXPECT_EQ(window_.get(), window_manager()->GetCurrentFocusedWindow()); + EXPECT_EQ(window_->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); +} + +// Check the following flow works as expected: +// 1. With 2 windows open, +// 2. Focus window 1, starts dragging, +// 3. Run move loop, +// 4. Drag the pointer out of window 1 and then into window 2, +// 5. Drag it a bit more (within window 2) and then calls EndMoveLoop(), +// emulating a window snap), and then +// 6. With the window in "snapped" state, drag it further and then drop. +TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) { + // Init and open |target_window|. + PlatformWindowInitProperties properties{gfx::Rect{80, 80}}; + properties.type = PlatformWindowType::kWindow; + EXPECT_CALL(delegate_, OnAcceleratedWidgetAvailable(_)).Times(1); + auto window_2 = WaylandWindow::Create(&delegate_, connection_.get(), + std::move(properties)); + ASSERT_NE(gfx::kNullAcceleratedWidget, window_2->GetWidget()); + Sync(); + + // Ensure there is no window currently focused + EXPECT_FALSE(window_manager()->GetCurrentFocusedWindow()); + EXPECT_EQ(gfx::kNullAcceleratedWidget, + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + + auto* source_window = window_.get(); + auto* target_window = window_2.get(); + EXPECT_TRUE(source_window); + EXPECT_TRUE(target_window); + + SendPointerEnter(source_window, &delegate_); + SendPointerPress(source_window, &delegate_, BTN_LEFT); + SendPointerMotion(source_window, &delegate_, {10, 10}); + + // Sets up an "interaction flow" and RunMoveLoop: + // - 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* move_loop_handler = GetWmMoveLoopHandler(*window_); + DCHECK(move_loop_handler); + + enum { + kStarted, + kDragging, + kEnteredTarget, + kSnapped, + kDone + } test_step = kStarted; + + EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) { + EXPECT_TRUE(event->IsMouseEvent()); + switch (test_step) { + case kStarted: + EXPECT_EQ(ET_MOUSE_ENTERED, event->type()); + EXPECT_EQ(State::kDetached, drag_controller()->state()); + // Ensure PlatformScreen keeps consistent. + EXPECT_EQ(source_window->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + // Drag window a bit more. + SendDndMotion({50, 50}); + test_step = kDragging; + break; + case kEnteredTarget: + EXPECT_EQ(ET_MOUSE_ENTERED, event->type()); + EXPECT_EQ(State::kDetached, drag_controller()->state()); + // Ensure PlatformScreen keeps consistent. + EXPECT_EQ(target_window->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({10, 10}, {})); + + move_loop_handler->EndMoveLoop(); + test_step = kSnapped; + break; + default: + FAIL() << " event=" << event->GetName() + << " state=" << drag_controller()->state() + << " step=" << static_cast<int>(test_step); + return; + } + }); + + EXPECT_CALL(delegate_, OnBoundsChanged(_)) + .WillOnce([&](const gfx::Rect& bounds) { + EXPECT_EQ(State::kDetached, drag_controller()->state()); + EXPECT_EQ(kDragging, test_step); + EXPECT_EQ(gfx::Point(50, 50), bounds.origin()); + + // Exit |source_window| and enter the |target_window|. + SendDndLeave(); + SendDndEnter(target_window); + test_step = kEnteredTarget; + }); + + // RunMoveLoop() blocks until the dragging sessions ends, so resume test + // server's run loop until it returns. + server_.Resume(); + move_loop_handler->RunMoveLoop({}); + server_.Pause(); + + // Continue the dragging session after "snapping" the window. At this point, + // the DND session is expected to be still alive and responding normally to + // data object events. + EXPECT_EQ(State::kAttached, drag_controller()->state()); + EXPECT_EQ(kSnapped, test_step); + + // Drag the pointer a bit more within |target_window| and then releases the + // mouse button and ensures drag controller delivers the events properly and + // exit gracefully. + SendDndMotion({30, 30}); + SendDndMotion({30, 33}); + SendDndMotion({30, 36}); + SendDndMotion({30, 39}); + SendDndMotion({30, 42}); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(5); + Sync(); + + EXPECT_EQ(gfx::Point(30, 42), screen_->GetCursorScreenPoint()); + EXPECT_EQ(target_window->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({50, 50}, {})); + + SendDndDrop(); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](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()); + 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() + << " step=" << static_cast<int>(test_step); + return; + } + }); + Sync(); + + SendPointerEnter(target_window, &delegate_); + EXPECT_EQ(target_window, window_manager()->GetCurrentFocusedWindow()); + EXPECT_EQ(target_window->GetWidget(), + screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); +} + +INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, + WaylandWindowDragControllerTest, + ::testing::Values(kXdgShellStable)); + +INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, + WaylandWindowDragControllerTest, + ::testing::Values(kXdgShellV6)); + +} // namespace ui 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 f74df68dbff..29d70dbdc39 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc @@ -8,7 +8,7 @@ #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_surface.h" +#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" namespace ui { @@ -26,7 +26,7 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create( // parent window to be set. Thus, create a normal window instead then. if (properties.parent_widget == gfx::kNullAcceleratedWidget && !connection->wayland_window_manager()->GetCurrentFocusedWindow()) { - window.reset(new WaylandSurface(delegate, connection)); + window.reset(new WaylandToplevelWindow(delegate, connection)); } else if (connection->IsDragInProgress()) { // We are in the process of drag and requested a popup. Most probably, // it is an arrow window. @@ -43,7 +43,7 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create( case PlatformWindowType::kDrag: // TODO(msisov): Figure out what kind of surface we need to create for // bubble and drag windows. - window.reset(new WaylandSurface(delegate, connection)); + window.reset(new WaylandToplevelWindow(delegate, connection)); break; default: NOTREACHED(); 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 bd7050813b2..51f07bbc27a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc @@ -20,6 +20,11 @@ void WaylandWindowManager::RemoveObserver(WaylandWindowObserver* observer) { observers_.RemoveObserver(observer); } +void WaylandWindowManager::NotifyWindowConfigured(WaylandWindow* window) { + for (WaylandWindowObserver& observer : observers_) + observer.OnWindowConfigured(window); +} + void WaylandWindowManager::GrabLocatedEvents(WaylandWindow* window) { DCHECK_NE(located_events_grabber_, window); 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 1873c907a68..1f4cad045ca 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h @@ -27,6 +27,10 @@ class WaylandWindowManager { void AddObserver(WaylandWindowObserver* observer); void RemoveObserver(WaylandWindowObserver* observer); + // Notifies observers that the Window has been ack configured and + // WaylandBufferManagerHost can start attaching buffers to the |surface_|. + void NotifyWindowConfigured(WaylandWindow* window); + // Stores the window that should grab the located events. void GrabLocatedEvents(WaylandWindow* event_grabber); 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 d81f991e080..e289ea88e3d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc @@ -12,4 +12,6 @@ void WaylandWindowObserver::OnWindowAdded(WaylandWindow* window) {} void WaylandWindowObserver::OnWindowRemoved(WaylandWindow* window) {} +void WaylandWindowObserver::OnWindowConfigured(WaylandWindow* window) {} + } // 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 0f81b8b5731..c461b03114e 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h @@ -20,6 +20,9 @@ class WaylandWindowObserver : public base::CheckedObserver { // Called when |window| has been removed. virtual void OnWindowRemoved(WaylandWindow* window); + // Called when |window| has been ack configured. + virtual void OnWindowConfigured(WaylandWindow* window); + 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 9a1112a1bd8..d7c533edc09 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc @@ -98,25 +98,6 @@ class WaylandWindowTest : public WaylandTest { } protected: - void SendConfigureEvent(int width, - int height, - uint32_t serial, - struct wl_array* states) { - // In xdg_shell_v6+, both surfaces send serial configure event and toplevel - // surfaces send other data like states, heights and widths. - if (GetParam() == kXdgShellV6) { - zxdg_surface_v6_send_configure(xdg_surface_->resource(), serial); - ASSERT_TRUE(xdg_surface_->xdg_toplevel()); - zxdg_toplevel_v6_send_configure(xdg_surface_->xdg_toplevel()->resource(), - width, height, states); - } else { - xdg_surface_send_configure(xdg_surface_->resource(), serial); - ASSERT_TRUE(xdg_surface_->xdg_toplevel()); - xdg_toplevel_send_configure(xdg_surface_->xdg_toplevel()->resource(), - width, height, states); - } - } - void SendConfigureEventPopup(gfx::AcceleratedWidget menu_widget, const gfx::Rect bounds) { auto* popup = GetPopupByWidget(menu_widget); @@ -253,12 +234,20 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { const auto kNormalBounds = gfx::Rect{0, 0, 500, 300}; const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600}; + uint32_t serial = 0; + // Make sure the window has normal state initially. EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); window_->SetBounds(kNormalBounds); EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); VerifyAndClearExpectations(); + // Deactivate the surface. + auto empty_state = MakeStateArray({}); + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get()); + + Sync(); + auto active_maximized = MakeStateArray( {XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_MAXIMIZED}); EXPECT_CALL(*GetXdgToplevel(), SetMaximized()); @@ -268,7 +257,8 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds)); EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); window_->Maximize(); - SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 1, + SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(), + kMaximizedBounds.height(), ++serial, active_maximized.get()); Sync(); VerifyAndClearExpectations(); @@ -278,7 +268,8 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { kMaximizedBounds.height())); EXPECT_CALL(delegate_, OnActivationChanged(Eq(false))); EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0); - SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 2, + SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(), + kMaximizedBounds.height(), ++serial, inactive_maximized.get()); Sync(); VerifyAndClearExpectations(); @@ -287,7 +278,8 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { kMaximizedBounds.height())); EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0); - SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 3, + SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(), + kMaximizedBounds.height(), ++serial, active_maximized.get()); Sync(); VerifyAndClearExpectations(); @@ -301,7 +293,7 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { window_->Restore(); // Reinitialize wl_array, which removes previous old states. auto active = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 4, active.get()); + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, active.get()); Sync(); } @@ -310,7 +302,7 @@ TEST_P(WaylandWindowTest, Minimize) { // Make sure the window is initialized to normal state from the beginning. EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); - SendConfigureEvent(0, 0, 1, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get()); Sync(); EXPECT_CALL(*GetXdgToplevel(), SetMinimized()); @@ -320,24 +312,24 @@ TEST_P(WaylandWindowTest, Minimize) { // Reinitialize wl_array, which removes previous old states. states = ScopedWlArray(); - SendConfigureEvent(0, 0, 2, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get()); Sync(); // Wayland compositor doesn't notify clients about minimized state, but rather - // if a window is not activated. Thus, a WaylandSurface marks itself as being - // minimized and and sets state to minimized. Thus, the state mustn't change - // after the configuration event is sent. + // if a window is not activated. Thus, a WaylandToplevelWindow marks itself as + // being minimized and and sets state to minimized. Thus, the state mustn't + // change after the configuration event is sent. EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized); // Send one additional empty configuration event (which means the surface is // not maximized, fullscreen or activated) to ensure, WaylandWindow stays in // the same minimized state and doesn't notify its delegate. EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); - SendConfigureEvent(0, 0, 3, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get()); Sync(); // And one last time to ensure the behaviour. - SendConfigureEvent(0, 0, 4, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get()); Sync(); } @@ -346,7 +338,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); ScopedWlArray states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 1, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get()); Sync(); AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); @@ -358,7 +350,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { // comment in the WaylandWindow::ToggleFullscreen. EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kFullScreen); - SendConfigureEvent(0, 0, 2, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get()); Sync(); EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen()); @@ -367,7 +359,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); // Reinitialize wl_array, which removes previous old states. states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 3, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get()); Sync(); EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); } @@ -408,7 +400,7 @@ TEST_P(WaylandWindowTest, StartWithFullscreen) { // Activate the surface. ScopedWlArray states = InitializeWlArrayWithActivatedState(); AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); - SendConfigureEvent(0, 0, 1, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get()); Sync(); @@ -454,7 +446,7 @@ TEST_P(WaylandWindowTest, StartMaximized) { // Activate the surface. ScopedWlArray states = InitializeWlArrayWithActivatedState(); AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); - SendConfigureEvent(0, 0, 1, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get()); Sync(); @@ -467,7 +459,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { ScopedWlArray states = InitializeWlArrayWithActivatedState(); AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); - SendConfigureEvent(2000, 2000, 1, states.get()); + SendConfigureEvent(xdg_surface_, 2000, 2000, 1, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kMaximized))) @@ -480,7 +472,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { // Unmaximize states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 2, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal))) .Times(1); @@ -489,7 +481,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { // Now, set to fullscreen. AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); - SendConfigureEvent(2005, 2005, 3, states.get()); + SendConfigureEvent(xdg_surface_, 2005, 2005, 3, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))) .Times(1); @@ -499,7 +491,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { // Unfullscreen states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 4, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal))) .Times(1); @@ -511,7 +503,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { // Now, maximize, fullscreen and restore. states = InitializeWlArrayWithActivatedState(); AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); - SendConfigureEvent(2000, 2000, 1, states.get()); + SendConfigureEvent(xdg_surface_, 2000, 2000, 1, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kMaximized))) @@ -521,7 +513,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { Sync(); AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); - SendConfigureEvent(2005, 2005, 1, states.get()); + SendConfigureEvent(xdg_surface_, 2005, 2005, 1, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))) @@ -530,7 +522,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { // Restore states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 4, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get()); EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal))) .Times(1); @@ -544,12 +536,19 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { const auto kNormalBounds = gfx::Rect{0, 0, 500, 300}; const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600}; + uint32_t serial = 0; + // Make sure the window has normal state initially. EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); window_->SetBounds(kNormalBounds); EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); VerifyAndClearExpectations(); + // Deactivate the surface. + ScopedWlArray empty_state; + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get()); + Sync(); + auto active_maximized = MakeStateArray( {XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_MAXIMIZED}); EXPECT_CALL(*GetXdgToplevel(), SetMaximized()); @@ -561,7 +560,8 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { window_->Maximize(); // State changes are synchronous. EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState()); - SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 2, + SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(), + kMaximizedBounds.height(), ++serial, active_maximized.get()); Sync(); // Verify that the state has not been changed. @@ -578,7 +578,8 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { EXPECT_EQ(PlatformWindowState::kFullScreen, window_->GetPlatformWindowState()); AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, active_maximized.get()); - SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 3, + SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(), + kMaximizedBounds.height(), ++serial, active_maximized.get()); Sync(); // Verify that the state has not been changed. @@ -596,7 +597,7 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); // Reinitialize wl_array, which removes previous old states. auto active = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 4, active.get()); + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, active.get()); Sync(); EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); } @@ -614,8 +615,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) { EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds))); window_->Maximize(); AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); - SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 1, - states.get()); + SendConfigureEvent(xdg_surface_, maximized_bounds.width(), + maximized_bounds.height(), 1, states.get()); Sync(); restored_bounds = window_->GetRestoredBoundsInPixels(); EXPECT_EQ(bounds, restored_bounds); @@ -629,7 +630,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) { window_->Restore(); // Reinitialize wl_array, which removes previous old states. states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 2, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get()); Sync(); bounds = window_->GetBounds(); EXPECT_EQ(bounds, restored_bounds); @@ -641,7 +642,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) { const gfx::Rect current_bounds = window_->GetBounds(); ScopedWlArray states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 1, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get()); Sync(); gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels(); @@ -652,8 +653,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) { EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds))); window_->ToggleFullscreen(); AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); - SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 2, - states.get()); + SendConfigureEvent(xdg_surface_, fullscreen_bounds.width(), + fullscreen_bounds.height(), 2, states.get()); Sync(); restored_bounds = window_->GetRestoredBoundsInPixels(); EXPECT_EQ(bounds, restored_bounds); @@ -667,7 +668,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) { window_->Restore(); // Reinitialize wl_array, which removes previous old states. states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 3, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get()); Sync(); bounds = window_->GetBounds(); EXPECT_EQ(bounds, restored_bounds); @@ -688,8 +689,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds))); window_->Maximize(); AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); - SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 1, - states.get()); + SendConfigureEvent(xdg_surface_, maximized_bounds.width(), + maximized_bounds.height(), 1, states.get()); Sync(); restored_bounds = window_->GetRestoredBoundsInPixels(); EXPECT_EQ(bounds, restored_bounds); @@ -698,8 +699,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds))); window_->ToggleFullscreen(); AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); - SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 2, - states.get()); + SendConfigureEvent(xdg_surface_, fullscreen_bounds.width(), + fullscreen_bounds.height(), 2, states.get()); Sync(); gfx::Rect fullscreen_restore_bounds = window_->GetRestoredBoundsInPixels(); EXPECT_EQ(restored_bounds, fullscreen_restore_bounds); @@ -709,8 +710,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { // Reinitialize wl_array, which removes previous old states. states = InitializeWlArrayWithActivatedState(); AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); - SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 3, - states.get()); + SendConfigureEvent(xdg_surface_, maximized_bounds.width(), + maximized_bounds.height(), 3, states.get()); Sync(); restored_bounds = window_->GetRestoredBoundsInPixels(); EXPECT_EQ(restored_bounds, fullscreen_restore_bounds); @@ -724,7 +725,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { window_->Restore(); // Reinitialize wl_array, which removes previous old states. states = InitializeWlArrayWithActivatedState(); - SendConfigureEvent(0, 0, 4, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get()); Sync(); bounds = window_->GetBounds(); EXPECT_EQ(bounds, restored_bounds); @@ -747,7 +748,7 @@ TEST_P(WaylandWindowTest, SendsBoundsOnRequest) { EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, new_bounds.width(), new_bounds.height())) .Times(2); - SendConfigureEvent(0, 0, 2, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get()); Sync(); // Restored bounds should keep empty value. @@ -757,7 +758,7 @@ TEST_P(WaylandWindowTest, SendsBoundsOnRequest) { // Second case is when Wayland sends a configure event with 1, 1 height and // width. It looks more like a bug in Gnome Shell with Wayland as long as the // documentation says it must be set to 0, 0, when wayland requests bounds. - SendConfigureEvent(0, 0, 3, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get()); Sync(); // Restored bounds should keep empty value. @@ -818,14 +819,14 @@ TEST_P(WaylandWindowTest, ConfigureEvent) { // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5. EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1000, 1000)).Times(1); EXPECT_CALL(*xdg_surface_, AckConfigure(12)); - SendConfigureEvent(1000, 1000, 12, states.get()); + SendConfigureEvent(xdg_surface_, 1000, 1000, 12, states.get()); Sync(); EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000)))); EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1500, 1000)).Times(1); EXPECT_CALL(*xdg_surface_, AckConfigure(13)); - SendConfigureEvent(1500, 1000, 13, states.get()); + SendConfigureEvent(xdg_surface_, 1500, 1000, 13, states.get()); } TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) { @@ -834,7 +835,7 @@ TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) { // If Wayland sends configure event with 0 width and 0 size, client should // call back with desired sizes. In this case, that's the actual size of // the window. - SendConfigureEvent(0, 0, 14, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, 14, states.get()); // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5. @@ -843,16 +844,23 @@ TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) { } TEST_P(WaylandWindowTest, OnActivationChanged) { + uint32_t serial = 0; + + // Deactivate the surface. + ScopedWlArray empty_state; + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get()); + Sync(); + { ScopedWlArray states = InitializeWlArrayWithActivatedState(); EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); - SendConfigureEvent(0, 0, 1, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, states.get()); Sync(); } ScopedWlArray states; EXPECT_CALL(delegate_, OnActivationChanged(Eq(false))); - SendConfigureEvent(0, 0, 2, states.get()); + SendConfigureEvent(xdg_surface_, 0, 0, ++serial, states.get()); Sync(); } @@ -1275,7 +1283,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) { TEST_P(WaylandWindowTest, DispatchWindowMove) { EXPECT_CALL(*GetXdgToplevel(), Move(_)); - ui::GetWmMoveResizeHandler(*window_)->DispatchHostWindowDragMovement(HTCAPTION, gfx::Point()); + ui::GetWmMoveResizeHandler(*window_)->DispatchHostWindowDragMovement( + HTCAPTION, gfx::Point()); } // Makes sure hit tests are converted into right edges. @@ -1482,7 +1491,7 @@ TEST_P(WaylandWindowTest, SetOpaqueRegion) { gfx::Rect new_bounds(0, 0, 500, 600); auto state_array = MakeStateArray({XDG_TOPLEVEL_STATE_ACTIVATED}); - SendConfigureEvent(new_bounds.width(), new_bounds.height(), 1, + SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 1, state_array.get()); SkIRect rect = @@ -1494,7 +1503,7 @@ TEST_P(WaylandWindowTest, SetOpaqueRegion) { VerifyAndClearExpectations(); new_bounds.set_size(gfx::Size(1000, 534)); - SendConfigureEvent(new_bounds.width(), new_bounds.height(), 2, + SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 2, state_array.get()); rect = SkIRect::MakeXYWH(0, 0, new_bounds.width(), new_bounds.height()); @@ -1922,8 +1931,8 @@ TEST_P(WaylandWindowTest, SetsPropertiesOnShow) { // We can't mock all those methods above as long as the xdg_toplevel is // created and destroyed on each show and hide call. However, it is the same - // WaylandSurface object that cached the values we set and must restore them - // on Show(). + // WaylandToplevelWindow object that cached the values we set and must restore + // them on Show(). EXPECT_EQ(mock_xdg_toplevel->min_size(), min_size.value()); EXPECT_EQ(mock_xdg_toplevel->max_size(), max_size.value()); EXPECT_EQ(std::string(kAppId), mock_xdg_toplevel->app_id()); @@ -2018,6 +2027,59 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) { EXPECT_EQ(test_popup->grab_serial(), touch_down_serial); } +// Tests nested menu windows get the topmost window in the stack of windows +// within the same family/tree. +TEST_P(WaylandWindowTest, NestedPopupWindowsGetCorrectParent) { + VerifyAndClearExpectations(); + + gfx::Rect menu_window_bounds(gfx::Rect(10, 20, 20, 20)); + std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds, + &delegate_); + EXPECT_TRUE(menu_window); + + EXPECT_TRUE(menu_window->parent_window() == window_.get()); + + gfx::Rect menu_window_bounds2(gfx::Rect(20, 40, 30, 20)); + std::unique_ptr<WaylandWindow> menu_window2 = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds2, + &delegate_); + EXPECT_TRUE(menu_window2); + + EXPECT_TRUE(menu_window2->parent_window() == menu_window.get()); + + gfx::Rect menu_window_bounds3(gfx::Rect(30, 40, 30, 20)); + std::unique_ptr<WaylandWindow> menu_window3 = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds3, + &delegate_); + EXPECT_TRUE(menu_window3); + + EXPECT_TRUE(menu_window3->parent_window() == menu_window2.get()); + + gfx::Rect menu_window_bounds4(gfx::Rect(40, 40, 30, 20)); + std::unique_ptr<WaylandWindow> menu_window4 = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds4, + &delegate_); + EXPECT_TRUE(menu_window4); + + EXPECT_TRUE(menu_window4->parent_window() == menu_window3.get()); +} + +TEST_P(WaylandWindowTest, DoesNotGrabPopupIfNoSeat) { + // Create a popup window and verify the grab serial is not set. + MockPlatformWindowDelegate delegate; + auto popup = CreateWaylandWindowWithParams( + PlatformWindowType::kMenu, window_->GetWidget(), gfx::Rect(0, 0, 50, 50), + &delegate); + ASSERT_TRUE(popup); + + Sync(); + + auto* test_popup = GetPopupByWidget(popup->GetWidget()); + ASSERT_TRUE(test_popup); + EXPECT_EQ(test_popup->grab_serial(), 0u); +} + 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 1e81f70b003..758ae1f222e 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 @@ -18,7 +18,7 @@ #include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include "ui/ozone/platform/wayland/host/wayland_popup.h" -#include "ui/ozone/platform/wayland/host/wayland_surface.h" +#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" namespace ui { @@ -286,8 +286,8 @@ bool XDGPopupWrapperImpl::Initialize(WaylandConnection* connection, static_cast<XDGPopupWrapperImpl*>(wayland_popup->shell_popup()); parent_xdg_surface = popup->xdg_surface(); } else { - WaylandSurface* wayland_surface = - static_cast<WaylandSurface*>(wayland_window_->parent_window()); + WaylandToplevelWindow* wayland_surface = + static_cast<WaylandToplevelWindow*>(wayland_window_->parent_window()); parent_xdg_surface = static_cast<XDGSurfaceWrapperImpl*>(wayland_surface->shell_surface()); } @@ -324,41 +324,8 @@ bool XDGPopupWrapperImpl::InitializeStable( xdg_positioner_destroy(positioner); - // According to the spec, the grab call can only be done on a key press, mouse - // press or touch down. However, there is a problem with popup windows and - // touch events so long as Chromium creates them only on consequent touch up - // events. That results in Wayland compositors dismissing popups. To fix - // the issue, do not use grab with touch events. Please note that explicit - // grab means that a Wayland compositor dismisses a popup whenever the user - // clicks outside the created surfaces. If the explicit grab is not used, the - // popups are not dismissed in such cases. What is more, current non-ozone X11 - // implementation does the same. This means there is no functionality changes - // and we do things right. - // - // We cannot know what was the last event. Instead, we can check if the window - // has pointer or keyboard focus. If so, the popup will be explicitly grabbed. - // - // There is a bug in the gnome/mutter - if explicit grab is not used, - // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a - // window results in weird behaviour. That is, a popup continues to be visible - // on a display and it results in a crash of the entire session. Thus, just - // continue to use grab here and avoid showing popups for touch events on - // gnome/mutter. That is better than crashing the entire system. Otherwise, - // Chromium has to change the way how it reacts on touch events - instead of - // creating a menu on touch up, it must do it on touch down events. - // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601. - std::unique_ptr<base::Environment> env(base::Environment::Create()); - if ((base::nix::GetDesktopEnvironment(env.get()) == - base::nix::DESKTOP_ENVIRONMENT_GNOME) || - (wayland_window_->parent_window()->has_pointer_focus() || - wayland_window_->parent_window()->has_keyboard_focus())) { - // When drag process starts, as described the protocol - - // https://goo.gl/1Mskq3, the client must have an active implicit grab. If - // we try to create a popup and grab it, it will be immediately dismissed. - // Thus, do not take explicit grab during drag process. - if (!connection->IsDragInProgress()) - xdg_popup_grab(xdg_popup_.get(), connection->seat(), - connection->serial()); + if (CanGrabPopup(connection)) { + xdg_popup_grab(xdg_popup_.get(), connection->seat(), connection->serial()); } xdg_popup_add_listener(xdg_popup_.get(), &xdg_popup_listener, this); @@ -375,18 +342,11 @@ struct xdg_positioner* XDGPopupWrapperImpl::CreatePositionerStable( if (!positioner) return nullptr; - bool is_right_click_menu = - connection->event_source()->IsPointerButtonPressed(EF_RIGHT_MOUSE_BUTTON); + auto menu_type = GetMenuTypeForPositioner(connection, parent_window); - // Different types of menu require different anchors, constraint adjustments, - // gravity and etc. - MenuType menu_type = MenuType::TYPE_UNKNOWN; - if (is_right_click_menu) - menu_type = MenuType::TYPE_RIGHT_CLICK; - else if (wl::IsMenuType(parent_window->type())) - menu_type = MenuType::TYPE_3DOT_CHILD_MENU; - else - menu_type = MenuType::TYPE_3DOT_PARENT_MENU; + // The parent we got must be the topmost in the stack of the same family + // windows. + DCHECK_EQ(parent_window->GetTopMostChildWindow(), parent_window); // Place anchor to the end of the possible position. gfx::Rect anchor_rect = GetAnchorRect( @@ -426,34 +386,7 @@ bool XDGPopupWrapperImpl::InitializeV6( zxdg_positioner_v6_destroy(positioner); - // According to the spec, the grab call can only be done on a key press, mouse - // press or touch down. However, there is a problem with popup windows and - // touch events so long as Chromium creates them only on consequent touch up - // events. That results in Wayland compositors dismissing popups. To fix - // the issue, do not use grab with touch events. Please note that explicit - // grab means that a Wayland compositor dismisses a popup whenever the user - // clicks outside the created surfaces. If the explicit grab is not used, the - // popups are not dismissed in such cases. What is more, current non-ozone X11 - // implementation does the same. This means there is no functionality changes - // and we do things right. - // - // We cannot know what was the last event. Instead, we can check if the window - // has pointer or keyboard focus. If so, the popup will be explicitly grabbed. - // - // There is a bug in the gnome/mutter - if explicit grab is not used, - // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a - // window results in weird behaviour. That is, a popup continues to be visible - // on a display and it results in a crash of the entire session. Thus, just - // continue to use grab here and avoid showing popups for touch events on - // gnome/mutter. That is better than crashing the entire system. Otherwise, - // Chromium has to change the way how it reacts on touch events - instead of - // creating a menu on touch up, it must do it on touch down events. - // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601. - std::unique_ptr<base::Environment> env(base::Environment::Create()); - if ((base::nix::GetDesktopEnvironment(env.get()) == - base::nix::DESKTOP_ENVIRONMENT_GNOME) || - (wayland_window_->parent_window()->has_pointer_focus() || - wayland_window_->parent_window()->has_keyboard_focus())) { + if (CanGrabPopup(connection)) { zxdg_popup_v6_grab(zxdg_popup_v6_.get(), connection->seat(), connection->serial()); } @@ -473,18 +406,11 @@ zxdg_positioner_v6* XDGPopupWrapperImpl::CreatePositionerV6( if (!positioner) return nullptr; - bool is_right_click_menu = - connection->event_source()->IsPointerButtonPressed(EF_RIGHT_MOUSE_BUTTON); + auto menu_type = GetMenuTypeForPositioner(connection, parent_window); - // Different types of menu require different anchors, constraint adjustments, - // gravity and etc. - MenuType menu_type = MenuType::TYPE_UNKNOWN; - if (is_right_click_menu) - menu_type = MenuType::TYPE_RIGHT_CLICK; - else if (wl::IsMenuType(parent_window->type())) - menu_type = MenuType::TYPE_3DOT_CHILD_MENU; - else - menu_type = MenuType::TYPE_3DOT_PARENT_MENU; + // The parent we got must be the topmost in the stack of the same family + // windows. + DCHECK_EQ(parent_window->GetTopMostChildWindow(), parent_window); // Place anchor to the end of the possible position. gfx::Rect anchor_rect = GetAnchorRect( @@ -505,6 +431,61 @@ zxdg_positioner_v6* XDGPopupWrapperImpl::CreatePositionerV6( return positioner; } +MenuType XDGPopupWrapperImpl::GetMenuTypeForPositioner( + WaylandConnection* connection, + WaylandWindow* parent_window) const { + bool is_right_click_menu = + connection->event_source()->last_pointer_button_pressed() & + EF_RIGHT_MOUSE_BUTTON; + + // Different types of menu require different anchors, constraint adjustments, + // gravity and etc. + if (is_right_click_menu) + return MenuType::TYPE_RIGHT_CLICK; + else if (!wl::IsMenuType(parent_window->type())) + return MenuType::TYPE_3DOT_PARENT_MENU; + else + return MenuType::TYPE_3DOT_CHILD_MENU; +} + +bool XDGPopupWrapperImpl::CanGrabPopup(WaylandConnection* connection) const { + // When drag process starts, as described the protocol - + // https://goo.gl/1Mskq3, the client must have an active implicit grab. If + // we try to create a popup and grab it, it will be immediately dismissed. + // Thus, do not take explicit grab during drag process. + if (connection->IsDragInProgress() || !connection->seat()) + return false; + + // According to the spec, the grab call can only be done on a key press, mouse + // press or touch down. However, there is a problem with popup windows and + // touch events so long as Chromium creates them only on consequent touch up + // events. That results in Wayland compositors dismissing popups. To fix the + // issue, do not use grab with touch events. Please note that explicit grab + // means that a Wayland compositor dismisses a popup whenever the user clicks + // outside the created surfaces. If the explicit grab is not used, the popups + // are not dismissed in such cases. What is more, current non-ozone X11 + // implementation does the same. This means there is no functionality changes + // and we do things right. + // + // We cannot know what was the last event. Instead, we can check if the window + // has pointer or keyboard focus. If so, the popup will be explicitly grabbed. + // + // There is a bug in the gnome/mutter - if explicit grab is not used, + // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a + // window results in weird behaviour. That is, a popup continues to be visible + // on a display and it results in a crash of the entire session. Thus, just + // continue to use grab here and avoid showing popups for touch events on + // gnome/mutter. That is better than crashing the entire system. Otherwise, + // Chromium has to change the way how it reacts on touch events - instead of + // creating a menu on touch up, it must do it on touch down events. + // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601. + std::unique_ptr<base::Environment> env(base::Environment::Create()); + return (base::nix::GetDesktopEnvironment(env.get()) == + base::nix::DESKTOP_ENVIRONMENT_GNOME) || + (wayland_window_->parent_window()->has_pointer_focus() || + wayland_window_->parent_window()->has_keyboard_focus()); +} + void XDGPopupWrapperImpl::Configure(void* data, int32_t x, int32_t y, diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h index c0479cfff7f..62f38a2dc6a 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h +++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/macros.h" #include "ui/ozone/platform/wayland/host/shell_popup_wrapper.h" namespace ui { @@ -41,6 +42,11 @@ class XDGPopupWrapperImpl : public ShellPopupWrapper { WaylandWindow* parent_window, const gfx::Rect& bounds); + MenuType GetMenuTypeForPositioner(WaylandConnection* connection, + WaylandWindow* parent_window) const; + + bool CanGrabPopup(WaylandConnection* connection) const; + // xdg_popup_listener static void Configure(void* data, int32_t x, 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 e7df772ae47..ae1ca159fd4 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 @@ -119,6 +119,8 @@ void XDGSurfaceWrapperImpl::AckConfigure() { zxdg_surface_v6_ack_configure(zxdg_surface_v6_.get(), pending_configure_serial_); } + connection_->wayland_window_manager()->NotifyWindowConfigured( + wayland_window_); } void XDGSurfaceWrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) { diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc index 8ffb52c5213..894166db3f9 100644 --- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -14,6 +14,7 @@ #include "base/message_loop/message_pump_type.h" #include "base/threading/sequenced_task_runner_handle.h" #include "ui/base/buildflags.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/base/ime/linux/input_method_auralinux.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" @@ -98,9 +99,7 @@ class OzonePlatformWayland : public OzonePlatform { return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } InputController* GetInputController() override { return input_controller_.get(); @@ -249,7 +248,7 @@ class OzonePlatformWayland : public OzonePlatform { std::unique_ptr<KeyboardLayoutEngine> keyboard_layout_engine_; std::unique_ptr<WaylandConnection> connection_; std::unique_ptr<WaylandSurfaceFactory> surface_factory_; - std::unique_ptr<BitmapCursorFactoryOzone> cursor_factory_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<StubOverlayManager> overlay_manager_; std::unique_ptr<InputController> input_controller_; std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; diff --git a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc index d24c13658aa..cd87cb8aafc 100644 --- a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc +++ b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc @@ -36,6 +36,12 @@ MockWpPresentation::MockWpPresentation() MockWpPresentation::~MockWpPresentation() {} +wl_resource* MockWpPresentation::ReleasePresentationCallback() { + auto* presentation_callback = presentation_callback_; + presentation_callback_ = nullptr; + return presentation_callback; +} + void MockWpPresentation::SendPresentationCallback() { if (!presentation_callback_) return; @@ -50,4 +56,14 @@ void MockWpPresentation::SendPresentationCallback() { presentation_callback_ = nullptr; } +void MockWpPresentation::SendPresentationCallbackDiscarded() { + if (!presentation_callback_) + return; + + wp_presentation_feedback_send_discarded(presentation_callback_); + wl_client_flush(wl_resource_get_client(presentation_callback_)); + wl_resource_destroy(presentation_callback_); + presentation_callback_ = nullptr; +} + } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h index 616cd6e3fe1..64d09ab4f8e 100644 --- a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h +++ b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h @@ -7,7 +7,7 @@ #include <presentation-time-server-protocol.h> -#include "base/logging.h" +#include "base/check.h" #include "base/macros.h" #include "testing/gmock/include/gmock/gmock.h" #include "ui/ozone/platform/wayland/test/global_object.h" @@ -34,7 +34,10 @@ class MockWpPresentation : public GlobalObject { presentation_callback_ = callback_resource; } + wl_resource* ReleasePresentationCallback(); + void SendPresentationCallback(); + void SendPresentationCallbackDiscarded(); private: wl_resource* presentation_callback_ = nullptr; diff --git a/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc new file mode 100644 index 00000000000..9977ca1db17 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc @@ -0,0 +1,41 @@ +// 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/test/scoped_wl_array.h" + +#include <wayland-server-core.h> + +namespace wl { + +ScopedWlArray::ScopedWlArray(const std::vector<int32_t> states) { + wl_array_init(&array_); + for (const auto& state : states) + AddStateToWlArray(state); +} + +ScopedWlArray::ScopedWlArray(ScopedWlArray&& rhs) { + array_ = rhs.array_; + // wl_array_init sets rhs.array_'s fields to nullptr, so that + // the free() in wl_array_release() is a no-op. + wl_array_init(&rhs.array_); +} + +ScopedWlArray& ScopedWlArray::operator=(ScopedWlArray&& rhs) { + wl_array_release(&array_); + array_ = rhs.array_; + // wl_array_init sets rhs.array_'s fields to nullptr, so that + // the free() in wl_array_release() is a no-op. + wl_array_init(&rhs.array_); + return *this; +} + +ScopedWlArray::~ScopedWlArray() { + wl_array_release(&array_); +} + +void ScopedWlArray::AddStateToWlArray(uint32_t state) { + *static_cast<uint32_t*>(wl_array_add(&array_, sizeof array_)) = state; +} + +} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h new file mode 100644 index 00000000000..d9c86f99265 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h @@ -0,0 +1,31 @@ +// 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_TEST_SCOPED_WL_ARRAY_H_ +#define UI_OZONE_PLATFORM_WAYLAND_TEST_SCOPED_WL_ARRAY_H_ + +#include <wayland-server-core.h> + +#include <vector> + +namespace wl { + +class ScopedWlArray { + public: + explicit ScopedWlArray(const std::vector<int32_t> states); + ScopedWlArray(ScopedWlArray&& rhs); + ScopedWlArray& operator=(ScopedWlArray&& rhs); + ~ScopedWlArray(); + + wl_array* get() { return &array_; } + + void AddStateToWlArray(uint32_t state); + + private: + wl_array array_; +}; + +} // namespace wl + +#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_SCOPED_WL_ARRAY_H_ diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_device.cc b/chromium/ui/ozone/platform/wayland/test/test_data_device.cc index fcd71d6e9c5..6fc8a3e464d 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_data_device.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_data_device.cc @@ -6,8 +6,13 @@ #include <wayland-server-core.h> +#include <cstdint> + #include "base/notreached.h" +#include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/server_object.h" #include "ui/ozone/platform/wayland/test/test_data_offer.h" +#include "ui/ozone/platform/wayland/test/test_data_source.h" namespace wl { @@ -19,7 +24,11 @@ void DataDeviceStartDrag(wl_client* client, wl_resource* origin, wl_resource* icon, uint32_t serial) { - NOTIMPLEMENTED(); + auto* data_source = GetUserDataAs<TestDataSource>(source); + auto* origin_surface = GetUserDataAs<MockSurface>(origin); + + GetUserDataAs<TestDataDevice>(resource)->StartDrag(data_source, + origin_surface, serial); } void DataDeviceSetSelection(wl_client* client, @@ -50,6 +59,16 @@ void TestDataDevice::SetSelection(TestDataSource* data_source, NOTIMPLEMENTED(); } +void TestDataDevice::StartDrag(TestDataSource* source, + MockSurface* origin, + uint32_t serial) { + DCHECK(source); + DCHECK(origin); + if (delegate_) + delegate_->StartDrag(source, origin, serial); + wl_client_flush(client_); +} + TestDataOffer* TestDataDevice::OnDataOffer() { wl_resource* data_offer_resource = CreateResourceWithImpl<TestDataOffer>( client_, &wl_data_offer_interface, wl_resource_get_version(resource()), diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_device.h b/chromium/ui/ozone/platform/wayland/test/test_data_device.h index 9ac3357ed53..85ad4af518a 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_data_device.h +++ b/chromium/ui/ozone/platform/wayland/test/test_data_device.h @@ -7,7 +7,10 @@ #include <wayland-server-protocol.h> +#include <cstdint> + #include "base/macros.h" +#include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/server_object.h" struct wl_client; @@ -22,10 +25,21 @@ class TestDataSource; class TestDataDevice : public ServerObject { public: + struct Delegate { + virtual void StartDrag(TestDataSource* source, + MockSurface* origin, + uint32_t serial) = 0; + }; + TestDataDevice(wl_resource* resource, wl_client* client); ~TestDataDevice() override; + void set_delegate(Delegate* delegate) { delegate_ = delegate; } + void SetSelection(TestDataSource* data_source, uint32_t serial); + void StartDrag(TestDataSource* data_source, + MockSurface* origin, + uint32_t serial); TestDataOffer* OnDataOffer(); void OnEnter(uint32_t serial, @@ -41,6 +55,7 @@ class TestDataDevice : public ServerObject { private: TestDataOffer* data_offer_; wl_client* client_ = nullptr; + Delegate* delegate_ = nullptr; DISALLOW_COPY_AND_ASSIGN(TestDataDevice); }; diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_source.cc b/chromium/ui/ozone/platform/wayland/test/test_data_source.cc index 530a2e443c7..4d11ec8e9b1 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_data_source.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_data_source.cc @@ -5,6 +5,8 @@ #include "ui/ozone/platform/wayland/test/test_data_source.h" #include <wayland-server-core.h> + +#include <cstdint> #include <utility> #include "base/bind.h" @@ -51,16 +53,16 @@ void DataSourceDestroy(wl_client* client, wl_resource* resource) { wl_resource_destroy(resource); } -void SetActions(wl_client* client, - wl_resource* resource, - uint32_t dnd_actions) { - NOTIMPLEMENTED(); +void DataSourceSetActions(wl_client* client, + wl_resource* resource, + uint32_t dnd_actions) { + GetUserDataAs<TestDataSource>(resource)->SetActions(dnd_actions); } } // namespace const struct wl_data_source_interface kTestDataSourceImpl = { - DataSourceOffer, DataSourceDestroy, SetActions}; + DataSourceOffer, DataSourceDestroy, DataSourceSetActions}; TestDataSource::TestDataSource(wl_resource* resource) : ServerObject(resource), @@ -70,7 +72,11 @@ TestDataSource::TestDataSource(wl_resource* resource) TestDataSource::~TestDataSource() {} void TestDataSource::Offer(const std::string& mime_type) { - NOTIMPLEMENTED(); + mime_types_.push_back(mime_type); +} + +void TestDataSource::SetActions(uint32_t dnd_actions) { + actions_ |= dnd_actions; } void TestDataSource::ReadData(const std::string& mime_type, diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_source.h b/chromium/ui/ozone/platform/wayland/test/test_data_source.h index 3cfdbff7a69..bcf66c3c26a 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_data_source.h +++ b/chromium/ui/ozone/platform/wayland/test/test_data_source.h @@ -7,10 +7,12 @@ #include <wayland-server-protocol.h> +#include <cstdint> #include <memory> #include <string> #include <vector> +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "ui/ozone/platform/wayland/test/server_object.h" @@ -31,15 +33,23 @@ class TestDataSource : public ServerObject { ~TestDataSource() override; void Offer(const std::string& mime_type); + void SetActions(uint32_t dnd_actions); using ReadDataCallback = base::OnceCallback<void(std::vector<uint8_t>&&)>; void ReadData(const std::string& mime_type, ReadDataCallback callback); void OnCancelled(); + std::vector<std::string> mime_types() const { return mime_types_; } + + uint32_t actions() const { return actions_; } + private: const scoped_refptr<base::SequencedTaskRunner> task_runner_; + std::vector<std::string> mime_types_; + uint32_t actions_ = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + DISALLOW_COPY_AND_ASSIGN(TestDataSource); }; diff --git a/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc b/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc index 1609bb2012a..73773c773b1 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc @@ -4,7 +4,7 @@ #include "ui/ozone/platform/wayland/test/mock_surface.h" -#include "base/logging.h" +#include "base/notreached.h" namespace wl { diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc index 2866ab6a07f..67c8f2bbaf2 100644 --- a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc +++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc @@ -10,6 +10,7 @@ #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_surface.h" +#include "ui/ozone/platform/wayland/test/scoped_wl_array.h" #include "ui/platform_window/platform_window_init_properties.h" #if BUILDFLAG(USE_XKBCOMMON) @@ -66,6 +67,11 @@ void WaylandTest::SetUp() { surface_ = server_.GetObject<wl::MockSurface>(widget_); ASSERT_TRUE(surface_); + // The surface must be activated before buffers are attached. + ActivateSurface(server_.GetObject<wl::MockSurface>(widget_)->xdg_surface()); + + Sync(); + initialized_ = true; } @@ -86,4 +92,33 @@ void WaylandTest::Sync() { server_.Pause(); } +void WaylandTest::SendConfigureEvent(wl::MockXdgSurface* xdg_surface, + int width, + int height, + uint32_t serial, + struct wl_array* states) { + // In xdg_shell_v6+, both surfaces send serial configure event and toplevel + // surfaces send other data like states, heights and widths. + // Please note that toplevel surfaces may not exist if the surface was created + // for the popup role. + if (GetParam() == kXdgShellV6) { + zxdg_surface_v6_send_configure(xdg_surface->resource(), serial); + if (xdg_surface->xdg_toplevel()) { + zxdg_toplevel_v6_send_configure(xdg_surface->xdg_toplevel()->resource(), + width, height, states); + } + } else { + xdg_surface_send_configure(xdg_surface->resource(), serial); + if (xdg_surface->xdg_toplevel()) { + xdg_toplevel_send_configure(xdg_surface->xdg_toplevel()->resource(), + width, height, states); + } + } +} + +void WaylandTest::ActivateSurface(wl::MockXdgSurface* xdg_surface) { + wl::ScopedWlArray state({XDG_TOPLEVEL_STATE_ACTIVATED}); + SendConfigureEvent(xdg_surface, 0, 0, 1, state.get()); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.h b/chromium/ui/ozone/platform/wayland/test/wayland_test.h index fc8161eb51d..97df3efbade 100644 --- a/chromium/ui/ozone/platform/wayland/test/wayland_test.h +++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.h @@ -24,6 +24,7 @@ namespace wl { class MockSurface; +class MockXdgSurface; } // namespace wl namespace ui { @@ -47,6 +48,18 @@ class WaylandTest : public ::testing::TestWithParam<uint32_t> { void Sync(); protected: + // Sends configure event for the |xdg_surface|. + void SendConfigureEvent(wl::MockXdgSurface* xdg_surface, + int width, + int height, + uint32_t serial, + struct wl_array* states); + + // Sends XDG_TOPLEVEL_STATE_ACTIVATED to the |xdg_surface| with width and + // height set to 0, which results in asking the client to set the width and + // height of the surface. + void ActivateSurface(wl::MockXdgSurface* xdg_surface); + base::test::TaskEnvironment task_environment_; wl::TestWaylandServerThread server_; diff --git a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc index de10370fe69..acf7594d5e2 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc @@ -125,7 +125,7 @@ class WaylandBufferManagerTest : public WaylandTest { } } - void CreateDmabufBasedBufferAndSetTerminateExpecation( + void CreateDmabufBasedBufferAndSetTerminateExpectation( bool fail, uint32_t buffer_id, base::ScopedFD fd = base::ScopedFD(), @@ -187,11 +187,14 @@ class WaylandBufferManagerTest : public WaylandTest { } } - std::unique_ptr<WaylandWindow> CreateWindow() { + std::unique_ptr<WaylandWindow> CreateWindow( + PlatformWindowType type = PlatformWindowType::kWindow, + gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget) { testing::Mock::VerifyAndClearExpectations(&delegate_); PlatformWindowInitProperties properties; properties.bounds = gfx::Rect(0, 0, 800, 600); - properties.type = PlatformWindowType::kWindow; + properties.type = type; + properties.parent_widget = parent_widget; auto new_window = WaylandWindow::Create(&delegate_, connection_.get(), std::move(properties)); EXPECT_TRUE(new_window); @@ -214,8 +217,8 @@ TEST_P(WaylandBufferManagerTest, CreateDmabufBasedBuffers) { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kDmabufBufferId); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kDmabufBufferId); DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, kDmabufBufferId, false /*fail*/); } @@ -255,7 +258,7 @@ TEST_P(WaylandBufferManagerTest, VerifyModifiers) { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation( + CreateDmabufBasedBufferAndSetTerminateExpectation( false /*fail*/, kDmabufBufferId, base::ScopedFD(), kDefaultSize, {1}, {2}, {kFormatModiferLinear}, kFourccFormatR8, 1); @@ -304,7 +307,7 @@ TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) { for (const auto& bad : kBadInputs) { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(0); base::ScopedFD dummy; - CreateDmabufBasedBufferAndSetTerminateExpecation( + CreateDmabufBasedBufferAndSetTerminateExpectation( true /*fail*/, bad.buffer_id, bad.has_file ? MakeFD() : std::move(dummy), bad.size, bad.strides, bad.offsets, bad.modifiers, bad.format, bad.planes_count); @@ -321,25 +324,27 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { // id if they haven't been assigned to any surfaces yet. { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId2); // Can't create buffer with existing id. - CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/, + kBufferId2); } // ... impossible to create buffers with the same id if one of them // has already been attached to a surface. { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId1); buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds()); - CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/, + kBufferId1); } // ... impossible to destroy non-existing buffer. @@ -356,8 +361,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { // specified. { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId1); buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds()); @@ -369,22 +374,22 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { // widgets. { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId1); DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); } // ... impossible to destroy buffers twice. { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId1); // Attach to a surface. buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds()); // Created non-attached buffer as well. - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId2); DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); // Can't destroy the buffer with non-existing id (the manager cleared the @@ -397,8 +402,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { kBufferId2, true /*fail*/); // Create and destroy non-attached buffer twice. - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kBufferId2); DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, kBufferId2, false /*fail*/); DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, @@ -408,7 +413,7 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) { EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, 1u); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, 1u); // Can't commit for non-existing buffer id. SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/); @@ -421,7 +426,7 @@ TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) { TEST_P(WaylandBufferManagerTest, CommitBufferNullWidget) { constexpr uint32_t kBufferId = 1; EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId); // Can't commit for non-existing widget. SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/); @@ -443,8 +448,8 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) { auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); Sync(); @@ -551,9 +556,9 @@ TEST_P(WaylandBufferManagerTest, auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(3); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId3); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3); Sync(); @@ -650,7 +655,149 @@ TEST_P(WaylandBufferManagerTest, DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); } -TEST_P(WaylandBufferManagerTest, MultiplePendingPresentationsForSameBuffer) {} +// This test ensures that a discarded presentation feedback sent prior receiving +// results for the previous presentation feedback does not make them +// automatically failed. +TEST_P(WaylandBufferManagerTest, + EnsureDiscardedPresentationDoesNotMakePreviousFeedbacksFailed) { + constexpr uint32_t kBufferId1 = 1; + constexpr uint32_t kBufferId2 = 2; + constexpr uint32_t kBufferId3 = 3; + + // Enable wp_presentation support. + auto* mock_wp_presentation = server_.EnsureWpPresentation(); + ASSERT_TRUE(mock_wp_presentation); + + const gfx::AcceleratedWidget widget = window_->GetWidget(); + const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize); + window_->SetBounds(bounds); + + MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_); + + auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); + EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(3); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3); + + Sync(); + + ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */, + false /* fail */); + + auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + + constexpr uint32_t kNumberOfCommits = 3; + EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits); + EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits); + EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits); + + // All the other expectations must come in order. + ::testing::InSequence sequence; + EXPECT_CALL(mock_surface_gpu, + OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK)) + .Times(1); + EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); + + // Commit first buffer + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + + Sync(); + + // Will be sent later. + auto* presentation_callback1 = + mock_wp_presentation->ReleasePresentationCallback(); + + mock_surface->SendFrameCallback(); + + Sync(); + + EXPECT_CALL(mock_surface_gpu, + OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK)) + .Times(1); + EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); + + // Commit second buffer + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + + Sync(); + + // Will be sent later. + auto* presentation_callback2 = + mock_wp_presentation->ReleasePresentationCallback(); + + // Release previous buffer and commit third buffer. + mock_surface->ReleasePrevAttachedBuffer(); + mock_surface->SendFrameCallback(); + + Sync(); + + EXPECT_CALL(mock_surface_gpu, + OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK)) + .Times(1); + EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); + + // Commit third buffer + buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds); + + Sync(); + + mock_surface->ReleasePrevAttachedBuffer(); + + Sync(); + + // Even though WaylandBufferManagerHost stores the previous stores + // presentation feedbacks and waits for their value, the current last one + // mustn't result in the previous marked as failed. Thus, no feedback must be + // actually sent to the MockSurfaceGpu as it's required to send feedbacks in + // order. + EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); + + mock_wp_presentation->SendPresentationCallbackDiscarded(); + + Sync(); + + // Now, start to send all the previous callbacks. + EXPECT_CALL(mock_surface_gpu, + OnPresentation( + kBufferId1, + ::testing::Field( + &gfx::PresentationFeedback::flags, + ::testing::Eq(gfx::PresentationFeedback::Flags::kVSync)))) + .Times(1); + + mock_wp_presentation->set_presentation_callback(presentation_callback1); + mock_wp_presentation->SendPresentationCallback(); + + Sync(); + + // Now, send the second presentation feedback. It will send both second and + // third feedback that was discarded. + EXPECT_CALL(mock_surface_gpu, + OnPresentation( + kBufferId2, + ::testing::Field( + &gfx::PresentationFeedback::flags, + ::testing::Eq(gfx::PresentationFeedback::Flags::kVSync)))) + .Times(1); + EXPECT_CALL( + mock_surface_gpu, + OnPresentation( + kBufferId3, + ::testing::Field( + &gfx::PresentationFeedback::flags, + ::testing::Eq(gfx::PresentationFeedback::Flags::kFailure)))) + .Times(1); + + mock_wp_presentation->set_presentation_callback(presentation_callback2); + mock_wp_presentation->SendPresentationCallback(); + + Sync(); + + DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/); + DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/); +} TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { constexpr uint32_t kDmabufBufferId = 1; @@ -662,8 +809,8 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kDmabufBufferId); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kDmabufBufferId); // Part 1: the surface mustn't have a buffer attached until // zwp_linux_buffer_params_v1_send_created is called. Instead, the buffer must @@ -694,8 +841,8 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { // sent by the server. EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, - kDmabufBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kDmabufBufferId2); ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */, false /* fail */); @@ -726,6 +873,77 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { false /*fail*/); } +// Tests the surface does not have buffers attached until it's configured at +// least once. +TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) { + constexpr uint32_t kDmabufBufferId = 1; + + // Exercise three window types that create different windows - toplevel, popup + // and subsurface. + std::vector<PlatformWindowType> window_types{PlatformWindowType::kWindow, + PlatformWindowType::kPopup, + PlatformWindowType::kTooltip}; + + for (const auto& type : window_types) { + // If the type is not kWindow, provide default created window as parent of + // the newly created window. + auto temp_window = CreateWindow(type, type != PlatformWindowType::kWindow + ? widget_ + : gfx::kNullAcceleratedWidget); + auto widget = temp_window->GetWidget(); + + // Subsurface doesn't have an interface for sending configure events. + // Thus, WaylandSubsurface notifies the manager that the window is + // activated upon creation of the subsurface. Skip calling Show() and call + // later then. + if (type != PlatformWindowType::kTooltip) + temp_window->Show(false); + + Sync(); + + auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + + auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); + EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1); + + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, + kDmabufBufferId); + + Sync(); + + ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */, + false /* fail */); + + EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0); + EXPECT_CALL(*mock_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_surface, Commit()).Times(0); + + buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId, + window_->GetBounds()); + Sync(); + + if (type != PlatformWindowType::kTooltip) { + DCHECK(mock_surface->xdg_surface()); + ActivateSurface(mock_surface->xdg_surface()); + } else { + // See the comment near Show() call above. + temp_window->Show(false); + } + + EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_surface, Commit()).Times(1); + + Sync(); + + temp_window.reset(); + DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId, + false /*fail*/); + + Sync(); + } +} + // The buffer that is not originally attached to any of the surfaces, // must be attached when a commit request comes. Also, it must setup a buffer // release listener and OnSubmission must be called for that buffer if it is @@ -743,7 +961,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) { auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); Sync(); @@ -776,7 +994,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) { // Now synchronously create a second buffer and commit it. The release // callback must be setup and OnSubmission must be called. EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); Sync(); @@ -804,7 +1022,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) { // released once the buffer is committed and processed (that is, it must be // able to setup a buffer release callback). EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId3); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3); Sync(); @@ -845,7 +1063,7 @@ TEST_P(WaylandBufferManagerTest, DestroyBufferForDestroyedWindow) { auto widget = temp_window->GetWidget(); EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId); Sync(); @@ -866,7 +1084,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionSingleBuffer) { auto bounds = temp_window->GetBounds(); EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId); ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */, false /* fail */); @@ -894,11 +1112,22 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) { constexpr uint32_t kBufferId2 = 2; auto temp_window = CreateWindow(); + temp_window->Show(false); + + Sync(); + auto widget = temp_window->GetWidget(); auto bounds = temp_window->GetBounds(); + auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + ASSERT_TRUE(mock_surface); + + ActivateSurface(mock_surface->xdg_surface()); + + Sync(); + EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */, false /* fail */); @@ -915,13 +1144,12 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) { Sync(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); mock_surface->SendFrameCallback(); Sync(); EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */, false /* fail */); @@ -966,8 +1194,8 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) { auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1); - CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); Sync(); diff --git a/chromium/ui/ozone/platform/windows/BUILD.gn b/chromium/ui/ozone/platform/windows/BUILD.gn index 766c32795be..72fb3aae39a 100644 --- a/chromium/ui/ozone/platform/windows/BUILD.gn +++ b/chromium/ui/ozone/platform/windows/BUILD.gn @@ -24,6 +24,8 @@ source_set("windows") { "//base", "//skia", "//ui/base", + "//ui/base/cursor", + "//ui/base/cursor:cursor_base", "//ui/display/fake", "//ui/events", "//ui/events/ozone/layout", diff --git a/chromium/ui/ozone/platform/windows/ozone_platform_windows.cc b/chromium/ui/ozone/platform/windows/ozone_platform_windows.cc index d6dee8a511c..5a686f01622 100644 --- a/chromium/ui/ozone/platform/windows/ozone_platform_windows.cc +++ b/chromium/ui/ozone/platform/windows/ozone_platform_windows.cc @@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/display/fake/fake_display_delegate.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" @@ -20,7 +21,6 @@ #include "ui/ozone/platform/windows/windows_surface_factory.h" #include "ui/ozone/platform/windows/windows_window.h" #include "ui/ozone/platform/windows/windows_window_manager.h" -#include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/ozone_platform.h" @@ -56,9 +56,7 @@ class OzonePlatformWindows : public OzonePlatform { OverlayManagerOzone* GetOverlayManager() override { return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_ozone_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } InputController* GetInputController() override { return input_controller_.get(); } @@ -96,7 +94,7 @@ class OzonePlatformWindows : public OzonePlatform { overlay_manager_ = std::make_unique<StubOverlayManager>(); input_controller_ = CreateStubInputController(); - cursor_factory_ozone_ = std::make_unique<BitmapCursorFactoryOzone>(); + cursor_factory_ = std::make_unique<BitmapCursorFactory>(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); } @@ -110,7 +108,7 @@ class OzonePlatformWindows : public OzonePlatform { std::unique_ptr<WindowsWindowManager> window_manager_; std::unique_ptr<WindowsSurfaceFactory> surface_factory_; std::unique_ptr<PlatformEventSource> platform_event_source_; - std::unique_ptr<CursorFactoryOzone> cursor_factory_ozone_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<InputController> input_controller_; std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; std::unique_ptr<OverlayManagerOzone> overlay_manager_; diff --git a/chromium/ui/ozone/platform/x11/BUILD.gn b/chromium/ui/ozone/platform/x11/BUILD.gn index fcdb99cdf60..35b64eb2caa 100644 --- a/chromium/ui/ozone/platform/x11/BUILD.gn +++ b/chromium/ui/ozone/platform/x11/BUILD.gn @@ -24,18 +24,12 @@ source_set("x11") { "x11_canvas_surface.h", "x11_clipboard_ozone.cc", "x11_clipboard_ozone.h", - "x11_cursor_factory_ozone.cc", - "x11_cursor_factory_ozone.h", - "x11_cursor_ozone.cc", - "x11_cursor_ozone.h", "x11_screen_ozone.cc", "x11_screen_ozone.h", "x11_surface_factory.cc", "x11_surface_factory.h", ] - public_deps = [ "//ui/base/cursor/mojom:cursor_type" ] - deps = [ "//base", "//build:chromecast_buildflags", @@ -45,6 +39,7 @@ source_set("x11") { "//ui/base:buildflags", "//ui/base:data_exchange", "//ui/base/clipboard:clipboard_types", + "//ui/base/cursor:cursor_base", "//ui/base/ime", "//ui/base/x", "//ui/base/x:gl", @@ -68,17 +63,11 @@ source_set("x11") { ] if (is_chromeos) { - sources += [ - "x11_window_ozone_chromeos.cc", - "x11_window_ozone_chromeos.h", - ] deps += [ "//ui/base/ime/chromeos" ] } else { sources += [ "x11_os_exchange_data_provider_ozone.cc", "x11_os_exchange_data_provider_ozone.h", - "x11_window_ozone.cc", - "x11_window_ozone.h", ] deps += [ "//ui/base/ime/linux" ] } @@ -101,7 +90,6 @@ source_set("x11") { source_set("x11_unittests") { testonly = true sources = [ - "x11_cursor_factory_ozone_unittest.cc", "x11_screen_ozone_unittest.cc", "x11_window_ozone_unittest.cc", ] diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc index 2d26ced5587..394ef374c81 100644 --- a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -11,9 +11,11 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "ui/base/buildflags.h" +#include "ui/base/cursor/cursor_factory.h" #include "ui/base/dragdrop/os_exchange_data_provider_factory.h" #include "ui/base/dragdrop/os_exchange_data_provider_factory_ozone.h" -#include "ui/base/ime/linux/linux_input_method_context_factory.h" +#include "ui/base/x/x11_cursor_factory.h" +#include "ui/base/x/x11_error_handler.h" #include "ui/base/x/x11_util.h" #include "ui/display/fake/fake_display_delegate.h" #include "ui/events/devices/x11/touch_factory_x11.h" @@ -25,22 +27,22 @@ #include "ui/ozone/common/stub_overlay_manager.h" #include "ui/ozone/platform/x11/gl_egl_utility_x11.h" #include "ui/ozone/platform/x11/x11_clipboard_ozone.h" -#include "ui/ozone/platform/x11/x11_cursor_factory_ozone.h" #include "ui/ozone/platform/x11/x11_screen_ozone.h" #include "ui/ozone/platform/x11/x11_surface_factory.h" -#include "ui/ozone/platform/x11/x11_window_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/system_input_injector.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_init_properties.h" +#include "ui/platform_window/x11/x11_window.h" #if defined(OS_CHROMEOS) -#include "ui/base/dragdrop/os_exchange_data_provider_aura.h" +#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" #include "ui/base/ime/chromeos/input_method_chromeos.h" #else -#include "ui/base/ime/linux/input_method_auralinux.h" +#include "ui/base/ime/linux/input_method_auralinux.h" // nogncheck +#include "ui/base/ime/linux/linux_input_method_context_factory.h" // nogncheck #include "ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h" #endif @@ -88,9 +90,7 @@ class OzonePlatformX11 : public OzonePlatform, return overlay_manager_.get(); } - CursorFactoryOzone* GetCursorFactoryOzone() override { - return cursor_factory_ozone_.get(); - } + CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); } std::unique_ptr<SystemInputInjector> CreateSystemInputInjector() override { return nullptr; @@ -107,8 +107,7 @@ class OzonePlatformX11 : public OzonePlatform, std::unique_ptr<PlatformWindow> CreatePlatformWindow( PlatformWindowDelegate* delegate, PlatformWindowInitProperties properties) override { - std::unique_ptr<X11WindowOzone> window = - std::make_unique<X11WindowOzone>(delegate); + auto window = std::make_unique<X11Window>(delegate); window->Initialize(std::move(properties)); window->SetTitle(base::ASCIIToUTF16("Ozone X11")); return std::move(window); @@ -151,7 +150,7 @@ class OzonePlatformX11 : public OzonePlatform, std::unique_ptr<OSExchangeDataProvider> CreateProvider() override { #if defined(OS_CHROMEOS) - return std::make_unique<OSExchangeDataProviderAura>(); + return std::make_unique<OSExchangeDataProviderNonBacked>(); #else return std::make_unique<X11OSExchangeDataProviderOzone>(); #endif @@ -167,7 +166,7 @@ class OzonePlatformX11 : public OzonePlatform, overlay_manager_ = std::make_unique<StubOverlayManager>(); input_controller_ = CreateStubInputController(); clipboard_ = std::make_unique<X11ClipboardOzone>(); - cursor_factory_ozone_ = std::make_unique<X11CursorFactoryOzone>(); + cursor_factory_ = std::make_unique<X11CursorFactory>(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); #if BUILDFLAG(USE_XKBCOMMON) @@ -202,6 +201,28 @@ class OzonePlatformX11 : public OzonePlatform, gl_egl_utility_ = std::make_unique<GLEGLUtilityX11>(); } + void PostMainMessageLoopStart( + base::OnceCallback<void()> shutdown_cb) override { + // Installs the X11 error handlers for the UI process after the + // main message loop has started. This will allow us to exit cleanly + // if X exits before we do. + SetErrorHandlers(std::move(shutdown_cb)); + } + + void PostMainMessageLoopRun() override { + // Unset the X11 error handlers. The X11 error handlers log the errors using + // a |PostTask()| on the message-loop. But since the message-loop is in the + // process of terminating, this can cause errors. + SetEmptyErrorHandlers(); + } + + void PreEarlyInitialize() override { + // Installs the X11 error handlers for the browser process used during + // startup. They simply print error messages and exit because + // we can't shutdown properly while creating and initializing services. + SetNullErrorHandlers(); + } + private: // Performs initialization steps need by both UI and GPU. void InitializeCommon(const InitParams& params) { @@ -238,7 +259,7 @@ class OzonePlatformX11 : public OzonePlatform, std::unique_ptr<OverlayManagerOzone> overlay_manager_; std::unique_ptr<InputController> input_controller_; std::unique_ptr<X11ClipboardOzone> clipboard_; - std::unique_ptr<X11CursorFactoryOzone> cursor_factory_ozone_; + std::unique_ptr<CursorFactory> cursor_factory_; std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; // Objects in the GPU process. diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc index 0f8b04c4f59..bea9cffe465 100644 --- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc @@ -8,10 +8,14 @@ #include <vector> #include "base/containers/span.h" +#include "base/logging.h" #include "base/stl_util.h" #include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/x/extension_manager.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/xproto.h" using base::Contains; @@ -42,13 +46,6 @@ void ExpandTypes(std::vector<std::string>* list) { list->push_back(kMimeTypeTextUtf8); } -XID FindXEventTarget(const XEvent& xev) { - XID target = xev.xany.window; - if (xev.type == GenericEvent) - target = static_cast<XIDeviceEvent*>(xev.xcookie.data)->event; - return target; -} - } // namespace // Maintains state of a single selection (aka system clipboard buffer). @@ -79,7 +76,7 @@ struct X11ClipboardOzone::SelectionState { PlatformClipboard::RequestDataClosure request_clipboard_data_callback; // The time that this instance took ownership of the clipboard. - Time acquired_selection_timestamp; + x11::Time acquired_selection_timestamp; }; X11ClipboardOzone::X11ClipboardOzone() @@ -87,32 +84,21 @@ X11ClipboardOzone::X11ClipboardOzone() atom_targets_(gfx::GetAtom(kTargets)), atom_timestamp_(gfx::GetAtom(kTimestamp)), x_property_(gfx::GetAtom(kChromeSelection)), - x_display_(gfx::GetXDisplay()), - x_window_(XCreateSimpleWindow(x_display_, - DefaultRootWindow(x_display_), - /*x=*/-100, - /*y=*/-100, - /*width=*/10, - /*height=*/10, - /*border_width=*/0, - /*border=*/0, - /*background=*/0)) { - int ignored; // xfixes_error_base. - if (!XFixesQueryExtension(x_display_, &xfixes_event_base_, &ignored)) { - LOG(ERROR) << "X server does not support XFixes."; + connection_(x11::Connection::Get()), + x_window_(CreateDummyWindow("Chromium Clipboard Window")) { + if (!connection_->xfixes().present()) return; - } using_xfixes_ = true; // Register to receive standard X11 events. X11EventSource::GetInstance()->AddXEventDispatcher(this); - for (auto atom : {atom_clipboard_, XA_PRIMARY}) { + for (auto atom : {atom_clipboard_, x11::Atom::PRIMARY}) { // Register the selection state. selection_state_.emplace(atom, std::make_unique<SelectionState>()); // Register to receive XFixes notification when selection owner changes. - XFixesSelectSelectionInput(x_display_, x_window_, atom, - XFixesSetSelectionOwnerNotifyMask); + connection_->xfixes().SelectSelectionInput( + {x_window_, atom, x11::XFixes::SelectionEventMask::SetSelectionOwner}); // Prefetch the current remote clipboard contents. QueryTargets(atom); } @@ -122,21 +108,13 @@ X11ClipboardOzone::~X11ClipboardOzone() { X11EventSource::GetInstance()->RemoveXEventDispatcher(this); } -bool X11ClipboardOzone::DispatchXEvent(XEvent* xev) { - if (FindXEventTarget(*xev) != x_window_) - return false; - - switch (xev->type) { - case SelectionRequest: - return OnSelectionRequest(xev->xselectionrequest); - case SelectionNotify: - return OnSelectionNotify(xev->xselection); - } - - if (using_xfixes_ && - xev->type == xfixes_event_base_ + XFixesSetSelectionOwnerNotify) { - return OnSetSelectionOwnerNotify(xev); - } +bool X11ClipboardOzone::DispatchXEvent(x11::Event* xev) { + if (auto* request = xev->As<x11::SelectionRequestEvent>()) + return request->owner == x_window_ && OnSelectionRequest(*request); + if (auto* notify = xev->As<x11::SelectionNotifyEvent>()) + return notify->requestor == x_window_ && OnSelectionNotify(*notify); + if (auto* notify = xev->As<x11::XFixes::SelectionNotifyEvent>()) + return notify->owner == x_window_ && OnSetSelectionOwnerNotify(*notify); return false; } @@ -146,15 +124,17 @@ bool X11ClipboardOzone::DispatchXEvent(XEvent* xev) { // TIMESTAMP: Time when we took ownership of the clipboard. // <mime-type>: Mime type to receive clipboard as. bool X11ClipboardOzone::OnSelectionRequest( - const XSelectionRequestEvent& event) { + const x11::SelectionRequestEvent& event) { // The property must be set. - if (event.property == x11::None) + if (event.property == x11::Atom::None) return false; // target=TARGETS. - auto& selection_state = GetSelectionState(event.selection); + auto& selection_state = + GetSelectionState(static_cast<x11::Atom>(event.selection)); + auto target = static_cast<x11::Atom>(event.target); PlatformClipboard::DataMap& offer_data_map = selection_state.offer_data_map; - if (event.target == atom_targets_) { + if (target == atom_targets_) { std::vector<std::string> targets; // Add TIMESTAMP. targets.push_back(kTimestamp); @@ -163,26 +143,21 @@ bool X11ClipboardOzone::OnSelectionRequest( } // Expand types, then convert from string to atom. ExpandTypes(&targets); - std::vector<XAtom> atoms; - for (auto& entry : targets) { + std::vector<x11::Atom> atoms; + for (auto& entry : targets) atoms.push_back(gfx::GetAtom(entry.c_str())); - } - XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, - /*format=*/32, PropModeReplace, - reinterpret_cast<unsigned char*>(atoms.data()), - atoms.size()); + ui::SetArrayProperty(event.requestor, event.property, x11::Atom::ATOM, + atoms); - } else if (event.target == atom_timestamp_) { + } else if (target == atom_timestamp_) { // target=TIMESTAMP. - XChangeProperty(x_display_, event.requestor, event.property, XA_INTEGER, - /*format=*/32, PropModeReplace, - reinterpret_cast<unsigned char*>( - &selection_state.acquired_selection_timestamp), - 1); - + ui::SetProperty(event.requestor, event.property, x11::Atom::INTEGER, + selection_state.acquired_selection_timestamp); } else { // Send clipboard data. - char* target_name = XGetAtomName(x_display_, event.target); + std::string target_name; + if (auto reply = connection_->GetAtomName({event.target}).Sync()) + target_name = std::move(reply->name); std::string key = target_name; // Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING. @@ -193,58 +168,41 @@ bool X11ClipboardOzone::OnSelectionRequest( } auto it = offer_data_map.find(key); if (it != offer_data_map.end()) { - XChangeProperty(x_display_, event.requestor, event.property, event.target, - /*format=*/8, PropModeReplace, - const_cast<unsigned char*>(it->second.data()), - it->second.size()); + ui::SetArrayProperty(event.requestor, event.property, event.target, + it->second); } - XFree(target_name); } // Notify remote peer that clipboard has been sent. - XSelectionEvent selection_event; - selection_event.type = SelectionNotify; - selection_event.display = event.display; - selection_event.requestor = event.requestor; - selection_event.selection = event.selection; - selection_event.target = event.target; - selection_event.property = event.property; - selection_event.time = event.time; - XSendEvent(x_display_, selection_event.requestor, /*propagate=*/x11::False, - /*event_mask=*/0, reinterpret_cast<XEvent*>(&selection_event)); + x11::SelectionNotifyEvent selection_event{ + .time = event.time, + .requestor = event.requestor, + .selection = event.selection, + .target = event.target, + .property = event.property, + }; + SendEvent(selection_event, selection_event.requestor, + x11::EventMask::NoEvent); return true; } // A remote peer owns the clipboard. This event is received in response to // our request for TARGETS (GetAvailableMimeTypes), or a specific mime type // (RequestClipboardData). -bool X11ClipboardOzone::OnSelectionNotify(const XSelectionEvent& event) { +bool X11ClipboardOzone::OnSelectionNotify( + const x11::SelectionNotifyEvent& event) { // GetAvailableMimeTypes. - auto& selection_state = GetSelectionState(event.selection); - if (event.target == atom_targets_) { - XAtom type; - int format; - unsigned long item_count, after; - unsigned char* data = nullptr; - - if (XGetWindowProperty(x_display_, x_window_, x_property_, - /*long_offset=*/0, - /*long_length=*/256 * sizeof(XAtom), - /*delete=*/x11::False, XA_ATOM, &type, &format, - &item_count, &after, &data) != x11::Success) { - return false; - } + auto selection = static_cast<x11::Atom>(event.selection); + auto& selection_state = GetSelectionState(selection); + if (static_cast<x11::Atom>(event.target) == atom_targets_) { + std::vector<x11::Atom> targets; + ui::GetArrayProperty(x_window_, x_property_, &targets); selection_state.mime_types.clear(); - base::span<XAtom> targets(reinterpret_cast<XAtom*>(data), item_count); for (auto target : targets) { - char* atom_name = XGetAtomName(x_display_, target); - if (atom_name) { - selection_state.mime_types.push_back(atom_name); - XFree(atom_name); - } + if (auto reply = connection_->GetAtomName({target}).Sync()) + selection_state.mime_types.push_back(std::move(reply->name)); } - XFree(data); // If we have a saved callback, invoke it now with expanded types, otherwise // guess that we will want 'text/plain' and fetch it now. @@ -255,27 +213,20 @@ bool X11ClipboardOzone::OnSelectionNotify(const XSelectionEvent& event) { .Run(std::move(result)); } else { selection_state.data_mime_type = kMimeTypeText; - ReadRemoteClipboard(event.selection); + ReadRemoteClipboard(selection); } return true; } // RequestClipboardData. - if (event.property == x_property_) { - XAtom type; - int format; - unsigned long item_count, after; - unsigned char* data; - XGetWindowProperty(x_display_, x_window_, x_property_, - /*long_offset=*/0, /*long_length=*/~0L, - /*delete=*/x11::True, AnyPropertyType, &type, &format, - &item_count, &after, &data); - if (type != x11::None && format == 8) { - std::vector<unsigned char> tmp(data, data + item_count); - selection_state.data = tmp; - } - XFree(data); + if (static_cast<x11::Atom>(event.property) == x_property_) { + x11::Atom type; + std::vector<uint8_t> data; + ui::GetArrayProperty(x_window_, x_property_, &data, &type); + ui::DeleteProperty(x_window_, x_property_); + if (type != x11::Atom::None) + selection_state.data = std::move(data); // If we have a saved callback, invoke it now, otherwise this was a prefetch // and we have already saved |data_| for the next call to @@ -292,41 +243,41 @@ bool X11ClipboardOzone::OnSelectionNotify(const XSelectionEvent& event) { return false; } -bool X11ClipboardOzone::OnSetSelectionOwnerNotify(XEvent* xev) { - XFixesSelectionNotifyEvent* event = - reinterpret_cast<XFixesSelectionNotifyEvent*>(xev); - +bool X11ClipboardOzone::OnSetSelectionOwnerNotify( + const x11::XFixes::SelectionNotifyEvent& event) { // Reset state and fetch remote clipboard if there is a new remote owner. - if (!IsSelectionOwner(BufferForSelectionAtom(event->selection))) { - auto& selection_state = GetSelectionState(event->selection); + x11::Atom selection = event.selection; + if (!IsSelectionOwner(BufferForSelectionAtom(selection))) { + auto& selection_state = GetSelectionState(selection); selection_state.mime_types.clear(); selection_state.data_mime_type.clear(); selection_state.data.clear(); - QueryTargets(event->selection); + QueryTargets(selection); } // Increase the sequence number if the callback is set. if (update_sequence_cb_) - update_sequence_cb_.Run(BufferForSelectionAtom(event->selection)); + update_sequence_cb_.Run(BufferForSelectionAtom(selection)); return true; } -XAtom X11ClipboardOzone::SelectionAtomForBuffer(ClipboardBuffer buffer) const { +x11::Atom X11ClipboardOzone::SelectionAtomForBuffer( + ClipboardBuffer buffer) const { switch (buffer) { case ClipboardBuffer::kCopyPaste: return atom_clipboard_; case ClipboardBuffer::kSelection: - return XA_PRIMARY; + return x11::Atom::PRIMARY; default: NOTREACHED(); - return x11::None; + return x11::Atom::None; } } ClipboardBuffer X11ClipboardOzone::BufferForSelectionAtom( - XAtom selection) const { - if (selection == XA_PRIMARY) + x11::Atom selection) const { + if (selection == x11::Atom::PRIMARY) return ClipboardBuffer::kSelection; if (selection == atom_clipboard_) return ClipboardBuffer::kCopyPaste; @@ -335,18 +286,18 @@ ClipboardBuffer X11ClipboardOzone::BufferForSelectionAtom( } X11ClipboardOzone::SelectionState& X11ClipboardOzone::GetSelectionState( - XAtom selection) { + x11::Atom selection) { DCHECK(Contains(selection_state_, selection)); return *selection_state_[selection]; } -void X11ClipboardOzone::QueryTargets(XAtom selection) { +void X11ClipboardOzone::QueryTargets(x11::Atom selection) { GetSelectionState(selection).mime_types.clear(); - XConvertSelection(x_display_, selection, atom_targets_, x_property_, - x_window_, x11::CurrentTime); + connection_->ConvertSelection({x_window_, selection, atom_targets_, + x_property_, x11::Time::CurrentTime}); } -void X11ClipboardOzone::ReadRemoteClipboard(XAtom selection) { +void X11ClipboardOzone::ReadRemoteClipboard(x11::Atom selection) { auto& selection_state = GetSelectionState(selection); selection_state.data.clear(); // Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING. @@ -359,23 +310,24 @@ void X11ClipboardOzone::ReadRemoteClipboard(XAtom selection) { } } - XConvertSelection(x_display_, selection, gfx::GetAtom(target.c_str()), - x_property_, x_window_, x11::CurrentTime); + connection_->ConvertSelection({x_window_, selection, gfx::GetAtom(target), + x_property_, x11::Time::CurrentTime}); } void X11ClipboardOzone::OfferClipboardData( ClipboardBuffer buffer, const PlatformClipboard::DataMap& data_map, PlatformClipboard::OfferDataClosure callback) { - const XAtom selection = SelectionAtomForBuffer(buffer); + const x11::Atom selection = SelectionAtomForBuffer(buffer); auto& selection_state = GetSelectionState(selection); - const auto timestamp = X11EventSource::GetInstance()->GetTimestamp(); + const auto timestamp = + static_cast<x11::Time>(X11EventSource::GetInstance()->GetTimestamp()); selection_state.acquired_selection_timestamp = timestamp; selection_state.offer_data_map = data_map; // Only take ownership if we are using xfixes. // TODO(joelhockey): Make clipboard work without xfixes. if (using_xfixes_) { - XSetSelectionOwner(x_display_, selection, x_window_, timestamp); + connection_->SetSelectionOwner({x_window_, selection, timestamp}); } std::move(callback).Run(); } @@ -385,7 +337,7 @@ void X11ClipboardOzone::RequestClipboardData( const std::string& mime_type, PlatformClipboard::DataMap* data_map, PlatformClipboard::RequestDataClosure callback) { - const XAtom selection = SelectionAtomForBuffer(buffer); + const x11::Atom selection = SelectionAtomForBuffer(buffer); auto& selection_state = GetSelectionState(selection); // If we are not using xfixes, return empty data. // TODO(joelhockey): Make clipboard work without xfixes. @@ -408,7 +360,7 @@ void X11ClipboardOzone::RequestClipboardData( void X11ClipboardOzone::GetAvailableMimeTypes( ClipboardBuffer buffer, PlatformClipboard::GetMimeTypesClosure callback) { - const XAtom selection = SelectionAtomForBuffer(buffer); + const x11::Atom selection = SelectionAtomForBuffer(buffer); auto& selection_state = GetSelectionState(selection); // If we are not using xfixes, return empty data. // TODO(joelhockey): Make clipboard work without xfixes. @@ -432,8 +384,9 @@ bool X11ClipboardOzone::IsSelectionOwner(ClipboardBuffer buffer) { if (!using_xfixes_) return true; - return XGetSelectionOwner(x_display_, SelectionAtomForBuffer(buffer)) == - x_window_; + auto reply = + connection_->GetSelectionOwner({SelectionAtomForBuffer(buffer)}).Sync(); + return reply && reply->owner == x_window_; } void X11ClipboardOzone::SetSequenceNumberUpdateCb( diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h index 8743c6cc53a..478205cd472 100644 --- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h @@ -11,8 +11,11 @@ #include "base/callback.h" #include "base/containers/flat_map.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/event.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/xfixes.h" +#include "ui/gfx/x/xproto.h" #include "ui/ozone/public/platform_clipboard.h" namespace ui { @@ -51,61 +54,59 @@ class X11ClipboardOzone : public PlatformClipboard, public XEventDispatcher { struct SelectionState; // XEventDispatcher: - bool DispatchXEvent(XEvent* xev) override; + bool DispatchXEvent(x11::Event* xev) override; - bool OnSelectionRequest(const XSelectionRequestEvent& event); - bool OnSelectionNotify(const XSelectionEvent& event); - bool OnSetSelectionOwnerNotify(XEvent* xev); + bool OnSelectionRequest(const x11::SelectionRequestEvent& event); + bool OnSelectionNotify(const x11::SelectionNotifyEvent& event); + bool OnSetSelectionOwnerNotify( + const x11::XFixes::SelectionNotifyEvent& event); // Returns an X atom for a clipboard buffer type. - XAtom SelectionAtomForBuffer(ClipboardBuffer buffer) const; + x11::Atom SelectionAtomForBuffer(ClipboardBuffer buffer) const; // Returns a clipboard buffer type for an X atom for a selection name of the // system clipboard buffer. - ClipboardBuffer BufferForSelectionAtom(XAtom selection) const; + ClipboardBuffer BufferForSelectionAtom(x11::Atom selection) const; // Returns the state for the given selection; - SelectionState& GetSelectionState(XAtom selection); + SelectionState& GetSelectionState(x11::Atom selection); // Queries the current clipboard owner for what mime types are available by // sending XConvertSelection with target=TARGETS. After sending this, we // will receive a SelectionNotify event with xselection.target=TARGETS which // is processed in |OnSelectionNotify|. - void QueryTargets(XAtom selection); + void QueryTargets(x11::Atom selection); // Reads the contents of the remote clipboard by sending XConvertSelection // with target=<mime-type>. After sending this, we will receive a // SelectionNotify event with xselection.target=<mime-type> which is processed // in |OnSelectionNotify|. - void ReadRemoteClipboard(XAtom selection); + void ReadRemoteClipboard(x11::Atom selection); // Local cache of atoms. - const XAtom atom_clipboard_; - const XAtom atom_targets_; - const XAtom atom_timestamp_; + const x11::Atom atom_clipboard_; + const x11::Atom atom_targets_; + const x11::Atom atom_timestamp_; // The property on |x_window_| which will receive remote clipboard contents. - const XAtom x_property_; + const x11::Atom x_property_; // Our X11 state. - Display* const x_display_; + x11::Connection* connection_; // Input-only window used as a selection owner. - const XID x_window_; + const x11::Window x_window_; // If XFixes is unavailable, this clipboard window will not register to // receive events and no processing will take place. // TODO(joelhockey): Make clipboard work without xfixes. bool using_xfixes_ = false; - // The event base returned by XFixesQueryExtension(). - int xfixes_event_base_; - // Notifies whenever clipboard sequence number is changed. PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_; // State of selections served by this instance. - base::flat_map<XAtom, std::unique_ptr<SelectionState>> selection_state_; + base::flat_map<x11::Atom, std::unique_ptr<SelectionState>> selection_state_; DISALLOW_COPY_AND_ASSIGN(X11ClipboardOzone); }; diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.cc b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.cc deleted file mode 100644 index 1b9b47ff5f9..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2016 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/x11/x11_cursor_factory_ozone.h" - -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/base/cursor/cursor_lookup.h" -#include "ui/base/cursor/cursors_aura.h" -#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" -#include "ui/gfx/geometry/point.h" - -namespace ui { - -namespace { - -X11CursorOzone* ToX11CursorOzone(PlatformCursor cursor) { - return static_cast<X11CursorOzone*>(cursor); -} - -PlatformCursor ToPlatformCursor(X11CursorOzone* cursor) { - return static_cast<PlatformCursor>(cursor); -} - -// Gets default aura cursor bitmap/hotspot and creates a X11CursorOzone with it. -scoped_refptr<X11CursorOzone> CreateAuraX11Cursor(mojom::CursorType type) { - Cursor cursor(type); - cursor.set_image_scale_factor(1); - SkBitmap bitmap = GetCursorBitmap(cursor); - gfx::Point hotspot = GetCursorHotspot(cursor); - if (!bitmap.isNull()) - return new X11CursorOzone(bitmap, hotspot); - return nullptr; -} - -} // namespace - -X11CursorFactoryOzone::X11CursorFactoryOzone() - : invisible_cursor_(X11CursorOzone::CreateInvisible()) {} - -X11CursorFactoryOzone::~X11CursorFactoryOzone() {} - -PlatformCursor X11CursorFactoryOzone::GetDefaultCursor(mojom::CursorType type) { - return ToPlatformCursor(GetDefaultCursorInternal(type).get()); -} - -PlatformCursor X11CursorFactoryOzone::CreateImageCursor( - const SkBitmap& bitmap, - const gfx::Point& hotspot, - float bitmap_dpi) { - // There is a problem with custom cursors that have no custom data. The - // resulting SkBitmap is empty and X crashes when creating a zero size cursor - // image. Return invisible cursor here instead. - if (bitmap.drawsNothing()) { - // The result of |invisible_cursor_| is owned by the caller, and will be - // Unref()ed by code far away. (Usually in web_cursor.cc in content, among - // others.) If we don't manually add another reference before we cast this - // to a void*, we can end up with |invisible_cursor_| being freed out from - // under us. - invisible_cursor_->AddRef(); - return ToPlatformCursor(invisible_cursor_.get()); - } - - X11CursorOzone* cursor = new X11CursorOzone(bitmap, hotspot); - cursor->AddRef(); - return ToPlatformCursor(cursor); -} - -PlatformCursor X11CursorFactoryOzone::CreateAnimatedCursor( - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& hotspot, - int frame_delay_ms, - float bitmap_dpi) { - X11CursorOzone* cursor = new X11CursorOzone(bitmaps, hotspot, frame_delay_ms); - cursor->AddRef(); - return ToPlatformCursor(cursor); -} - -void X11CursorFactoryOzone::RefImageCursor(PlatformCursor cursor) { - ToX11CursorOzone(cursor)->AddRef(); -} - -void X11CursorFactoryOzone::UnrefImageCursor(PlatformCursor cursor) { - ToX11CursorOzone(cursor)->Release(); -} - -scoped_refptr<X11CursorOzone> X11CursorFactoryOzone::GetDefaultCursorInternal( - mojom::CursorType type) { - if (type == mojom::CursorType::kNone) - return invisible_cursor_; - - if (!default_cursors_.count(type)) { - // First try to load a predefined X11 cursor. - auto cursor = - base::MakeRefCounted<X11CursorOzone>(CursorCssNameFromId(type)); - if (cursor->xcursor() != x11::None) { - default_cursors_[type] = cursor; - return cursor; - } - - // Loads the default aura cursor bitmap for cursor type. Falls back on - // pointer cursor then invisible cursor if this fails. - cursor = CreateAuraX11Cursor(type); - if (!cursor.get()) { - if (type != mojom::CursorType::kPointer) { - cursor = GetDefaultCursorInternal(mojom::CursorType::kPointer); - } else { - NOTREACHED() << "Failed to load default cursor bitmap"; - } - } - default_cursors_[type] = cursor; - } - - // Returns owned default cursor for this type. - return default_cursors_[type]; -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.h b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.h deleted file mode 100644 index 76efeeb526d..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 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_X11_X11_CURSOR_FACTORY_OZONE_H_ -#define UI_OZONE_PLATFORM_X11_X11_CURSOR_FACTORY_OZONE_H_ - -#include <map> -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "ui/base/cursor/cursor.h" -#include "ui/base/cursor/mojom/cursor_type.mojom-forward.h" -#include "ui/gfx/x/x11.h" -#include "ui/ozone/platform/x11/x11_cursor_ozone.h" -#include "ui/ozone/public/cursor_factory_ozone.h" - -namespace ui { - -// CursorFactoryOzone implementation for X11 cursors. -class X11CursorFactoryOzone : public CursorFactoryOzone { - public: - X11CursorFactoryOzone(); - ~X11CursorFactoryOzone() override; - - // CursorFactoryOzone: - PlatformCursor GetDefaultCursor(mojom::CursorType type) override; - PlatformCursor CreateImageCursor(const SkBitmap& bitmap, - const gfx::Point& hotspot, - float bitmap_dpi) override; - PlatformCursor CreateAnimatedCursor(const std::vector<SkBitmap>& bitmaps, - const gfx::Point& hotspot, - int frame_delay_ms, - float bitmap_dpi) override; - void RefImageCursor(PlatformCursor cursor) override; - void UnrefImageCursor(PlatformCursor cursor) override; - - private: - // Loads/caches default cursor or returns cached version. - scoped_refptr<X11CursorOzone> GetDefaultCursorInternal( - mojom::CursorType type); - - // Holds a single instance of the invisible cursor. X11 has no way to hide - // the cursor so an invisible cursor mimics that. - scoped_refptr<X11CursorOzone> invisible_cursor_; - - std::map<mojom::CursorType, scoped_refptr<X11CursorOzone>> default_cursors_; - - DISALLOW_COPY_AND_ASSIGN(X11CursorFactoryOzone); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_X11_X11_CURSOR_FACTORY_OZONE_H_ diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone_unittest.cc deleted file mode 100644 index 8b065eb100b..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_cursor_factory_ozone_unittest.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 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/x11/x11_cursor_factory_ozone.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/geometry/point.h" - -namespace ui { - -TEST(X11CursorFactoryOzoneTest, InvisibleRefcount) { - X11CursorFactoryOzone factory; - - // Building an image cursor with an invalid SkBitmap should return the - // invisible cursor in X11. The invisible cursor instance should have more - // than a single reference since the factory should hold a reference and - // CreateImageCursor should return an incremented refcount. - X11CursorOzone* invisible_cursor = static_cast<X11CursorOzone*>( - factory.CreateImageCursor(SkBitmap(), gfx::Point(), 1.0f)); - ASSERT_FALSE(invisible_cursor->HasOneRef()); - - // Release our refcount on the cursor - factory.UnrefImageCursor(invisible_cursor); - - // The invisible cursor should still exist. - EXPECT_TRUE(invisible_cursor->HasOneRef()); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_ozone.cc b/chromium/ui/ozone/platform/x11/x11_cursor_ozone.cc deleted file mode 100644 index 64eb043d301..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_cursor_ozone.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 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/x11/x11_cursor_ozone.h" - -#include "base/check_op.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/base/x/x11_util.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/x/x11.h" - -namespace ui { - -X11CursorOzone::X11CursorOzone(const SkBitmap& bitmap, - const gfx::Point& hotspot) { - XcursorImage* image = SkBitmapToXcursorImage(bitmap, hotspot); - xcursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image); - XcursorImageDestroy(image); -} - -X11CursorOzone::X11CursorOzone(const std::vector<SkBitmap>& bitmaps, - const gfx::Point& hotspot, - int frame_delay_ms) { - // Initialize an XCursorImage for each frame, store all of them in - // XCursorImages and load the cursor from that. - XcursorImages* images = XcursorImagesCreate(bitmaps.size()); - images->nimage = bitmaps.size(); - for (size_t frame = 0; frame < bitmaps.size(); ++frame) { - XcursorImage* x_image = SkBitmapToXcursorImage(bitmaps[frame], hotspot); - x_image->delay = frame_delay_ms; - images->images[frame] = x_image; - } - - xcursor_ = XcursorImagesLoadCursor(gfx::GetXDisplay(), images); - XcursorImagesDestroy(images); -} - -X11CursorOzone::X11CursorOzone(const char* name) { - xcursor_ = XcursorLibraryLoadCursor(gfx::GetXDisplay(), name); -} - -// static -scoped_refptr<X11CursorOzone> X11CursorOzone::CreateInvisible() { - scoped_refptr<X11CursorOzone> invisible_ = new X11CursorOzone(); - invisible_->xcursor_ = CreateInvisibleCursor(); - return invisible_; -} - -X11CursorOzone::X11CursorOzone() {} - -X11CursorOzone::~X11CursorOzone() { - XFreeCursor(gfx::GetXDisplay(), xcursor_); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_cursor_ozone.h b/chromium/ui/ozone/platform/x11/x11_cursor_ozone.h deleted file mode 100644 index 6718abae17d..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_cursor_ozone.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 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_X11_X11_CURSOR_OZONE_H_ -#define UI_OZONE_PLATFORM_X11_X11_CURSOR_OZONE_H_ - -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "ui/base/cursor/cursor.h" -#include "ui/gfx/x/x11.h" - -class SkBitmap; - -namespace gfx { -class Point; -} - -namespace ui { - -// Ref counted class to hold an X11 cursor resource. Clears the X11 resources -// on destruction -class X11CursorOzone : public base::RefCounted<X11CursorOzone> { - public: - // Handles creating X11 cursor resources from SkBitmap/hotspot. - X11CursorOzone(const SkBitmap& bitmap, const gfx::Point& hotspot); - X11CursorOzone(const std::vector<SkBitmap>& bitmaps, - const gfx::Point& hotspot, - int frame_delay_ms); - // Loads an X11 cursor named |name| by calling XcursorLibraryLoadCursor(). - // May end up wrapping an x11::None so validity must be checked after using - // this constructor. - explicit X11CursorOzone(const char* name); - - // Creates a new cursor that is invisible. - static scoped_refptr<X11CursorOzone> CreateInvisible(); - - XID xcursor() const { return xcursor_; } - - private: - friend class base::RefCounted<X11CursorOzone>; - - X11CursorOzone(); - ~X11CursorOzone(); - - XID xcursor_ = x11::None; - - DISALLOW_COPY_AND_ASSIGN(X11CursorOzone); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_X11_X11_CURSOR_OZONE_H_ diff --git a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc index 7a31cb62ec9..7ede65f0e9c 100644 --- a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc @@ -6,13 +6,14 @@ #include <utility> -#include "base/logging.h" +#include "base/check.h" +#include "base/notreached.h" #include "ui/base/x/selection_utils.h" namespace ui { X11OSExchangeDataProviderOzone::X11OSExchangeDataProviderOzone( - XID x_window, + x11::Window x_window, const SelectionFormatMap& selection) : XOSExchangeDataProvider(x_window, selection) {} @@ -34,13 +35,14 @@ std::unique_ptr<OSExchangeDataProvider> X11OSExchangeDataProviderOzone::Clone() return std::move(ret); } -bool X11OSExchangeDataProviderOzone::DispatchXEvent(XEvent* xev) { - if (xev->xany.window != x_window()) +bool X11OSExchangeDataProviderOzone::DispatchXEvent(x11::Event* x11_event) { + XEvent* xev = &x11_event->xlib_event(); + if (xev->xany.window != static_cast<uint32_t>(x_window())) return false; switch (xev->type) { - case SelectionRequest: - selection_owner().OnSelectionRequest(*xev); + case x11::SelectionRequestEvent::opcode: + selection_owner().OnSelectionRequest(*x11_event); return true; default: NOTIMPLEMENTED(); diff --git a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h index 7a0637318fc..65542ea14d3 100644 --- a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h @@ -7,6 +7,7 @@ #include "ui/base/x/x11_os_exchange_data_provider.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/event.h" namespace ui { @@ -14,7 +15,7 @@ namespace ui { class X11OSExchangeDataProviderOzone : public XOSExchangeDataProvider, public XEventDispatcher { public: - X11OSExchangeDataProviderOzone(XID x_window, + X11OSExchangeDataProviderOzone(x11::Window x_window, const SelectionFormatMap& selection); X11OSExchangeDataProviderOzone(); ~X11OSExchangeDataProviderOzone() override; @@ -27,7 +28,7 @@ class X11OSExchangeDataProviderOzone : public XOSExchangeDataProvider, std::unique_ptr<OSExchangeDataProvider> Clone() const override; // XEventDispatcher: - bool DispatchXEvent(XEvent* xev) override; + bool DispatchXEvent(x11::Event* xev) override; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc b/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc index 5e9cf05b0a3..76aeb9e1c14 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc @@ -10,8 +10,9 @@ #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/font_render_params.h" #include "ui/gfx/geometry/dip_util.h" -#include "ui/ozone/platform/x11/x11_window_ozone.h" +#include "ui/gfx/native_widget_types.h" #include "ui/platform_window/x11/x11_topmost_window_finder.h" +#include "ui/platform_window/x11/x11_window.h" #include "ui/platform_window/x11/x11_window_manager.h" namespace ui { @@ -86,14 +87,15 @@ gfx::Point X11ScreenOzone::GetCursorScreenPoint() const { gfx::AcceleratedWidget X11ScreenOzone::GetAcceleratedWidgetAtScreenPoint( const gfx::Point& point) const { X11TopmostWindowFinder finder; - return finder.FindWindowAt(point); + return static_cast<gfx::AcceleratedWidget>(finder.FindWindowAt(point)); } gfx::AcceleratedWidget X11ScreenOzone::GetLocalProcessWidgetAtPoint( const gfx::Point& point, const std::set<gfx::AcceleratedWidget>& ignore) const { X11TopmostWindowFinder finder; - return finder.FindLocalProcessWindowAt(point, ignore); + return static_cast<gfx::AcceleratedWidget>( + finder.FindLocalProcessWindowAt(point, ignore)); } display::Display X11ScreenOzone::GetDisplayNearestPoint( @@ -125,7 +127,7 @@ std::string X11ScreenOzone::GetCurrentWorkspace() { return x11_display_manager_->GetCurrentWorkspace(); } -bool X11ScreenOzone::DispatchXEvent(XEvent* xev) { +bool X11ScreenOzone::DispatchXEvent(x11::Event* xev) { return x11_display_manager_->ProcessEvent(xev); } diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone.h b/chromium/ui/ozone/platform/x11/x11_screen_ozone.h index 02806ba53fc..de7d392d17b 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone.h @@ -14,6 +14,7 @@ #include "ui/base/x/x11_display_manager.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/x/event.h" #include "ui/ozone/public/platform_screen.h" namespace ui { @@ -51,7 +52,7 @@ class X11ScreenOzone : public PlatformScreen, std::string GetCurrentWorkspace() override; // Overridden from ui::XEventDispatcher: - bool DispatchXEvent(XEvent* event) override; + bool DispatchXEvent(x11::Event* event) override; private: friend class X11ScreenOzoneTest; diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc index 121a6ccc77c..5b2ac245341 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc @@ -13,10 +13,10 @@ #include "ui/display/display.h" #include "ui/display/display_observer.h" #include "ui/events/platform/x11/x11_event_source.h" -#include "ui/ozone/platform/x11/x11_window_ozone.h" #include "ui/ozone/test/mock_platform_window_delegate.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" +#include "ui/platform_window/x11/x11_window.h" #include "ui/platform_window/x11/x11_window_manager.h" using ::testing::_; @@ -95,14 +95,14 @@ class X11ScreenOzoneTest : public testing::Test { manager->displays_); } - std::unique_ptr<X11WindowOzone> CreatePlatformWindow( + std::unique_ptr<X11Window> CreatePlatformWindow( MockPlatformWindowDelegate* delegate, const gfx::Rect& bounds, gfx::AcceleratedWidget* widget = nullptr) { EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_)) .WillOnce(StoreWidget(widget)); PlatformWindowInitProperties init_params(bounds); - auto window = std::make_unique<X11WindowOzone>(delegate); + auto window = std::make_unique<X11Window>(delegate); window->Initialize(std::move(init_params)); return window; } diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc index a0daef0e754..d747464e40f 100644 --- a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc +++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc @@ -43,7 +43,8 @@ class GLOzoneEGLX11 : public GLOzoneEGL { base::MakeRefCounted<GLSurfaceEglReadbackX11>(window)); } else { return gl::InitializeGLSurface( - base::MakeRefCounted<gl::NativeViewGLSurfaceEGLX11GLES2>(window)); + base::MakeRefCounted<gl::NativeViewGLSurfaceEGLX11GLES2>( + static_cast<x11::Window>(window))); } } diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone.cc deleted file mode 100644 index 66658dc1d92..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone.cc +++ /dev/null @@ -1,158 +0,0 @@ -// 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/x11/x11_window_ozone.h" - -#include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/base/x/x11_os_exchange_data_provider.h" -#include "ui/base/x/x11_pointer_grab.h" -#include "ui/base/x/x11_topmost_window_finder.h" -#include "ui/events/event.h" -#include "ui/events/platform/scoped_event_dispatcher.h" -#include "ui/events/platform/x11/x11_event_source.h" -#include "ui/events/x/x11_window_event_manager.h" -#include "ui/ozone/platform/x11/x11_cursor_ozone.h" -#include "ui/platform_window/platform_window_delegate.h" -#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" -#include "ui/platform_window/x11/x11_topmost_window_finder.h" -#include "ui/platform_window/x11/x11_window_manager.h" - -namespace ui { - -X11WindowOzone::X11WindowOzone(PlatformWindowDelegate* delegate) - : X11Window(delegate) {} - -X11WindowOzone::~X11WindowOzone() = default; - -void X11WindowOzone::SetCursor(PlatformCursor cursor) { - X11CursorOzone* cursor_ozone = static_cast<X11CursorOzone*>(cursor); - XWindow::SetCursor(cursor_ozone->xcursor()); -} - -void X11WindowOzone::Initialize(PlatformWindowInitProperties properties) { - X11Window::Initialize(std::move(properties)); - // Set a class property key that allows |this| to be used for drag action. - SetWmDragHandler(this, this); - - drag_drop_client_ = - std::make_unique<XDragDropClient>(this, display(), window()); -} - -void X11WindowOzone::StartDrag(const ui::OSExchangeData& data, - int operation, - gfx::NativeCursor cursor, - base::OnceCallback<void(int)> callback) { - DCHECK(drag_drop_client_); - - end_drag_callback_ = std::move(callback); - drag_drop_client_->InitDrag(operation, &data); - - SetCapture(); - - dragging_ = true; -} - -std::unique_ptr<ui::XTopmostWindowFinder> X11WindowOzone::CreateWindowFinder() { - return std::make_unique<X11TopmostWindowFinder>(); -} - -int X11WindowOzone::UpdateDrag(const gfx::Point& screen_point) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return ui::DragDropTypes::DRAG_NONE; - // TODO: calculate the appropriate drag operation here. - return drop_handler->OnDragMotion(gfx::PointF(screen_point), - ui::DragDropTypes::DRAG_COPY); -} - -void X11WindowOzone::UpdateCursor( - ui::DragDropTypes::DragOperation negotiated_operation) { - NOTIMPLEMENTED_LOG_ONCE(); -} - -void X11WindowOzone::OnBeginForeignDrag(XID window) { - NOTIMPLEMENTED_LOG_ONCE(); -} - -void X11WindowOzone::OnEndForeignDrag() { - NOTIMPLEMENTED_LOG_ONCE(); -} - -void X11WindowOzone::OnBeforeDragLeave() { - NOTIMPLEMENTED_LOG_ONCE(); -} - -int X11WindowOzone::PerformDrop() { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return ui::DragDropTypes::DRAG_NONE; - - DCHECK(drag_drop_client_); - auto* target_current_context = drag_drop_client_->target_current_context(); - DCHECK(target_current_context); - - int drag_operation = ui::DragDropTypes::DRAG_NONE; - - drop_handler->OnDragDrop(std::make_unique<ui::OSExchangeData>( - std::make_unique<ui::XOSExchangeDataProvider>( - drag_drop_client_->xwindow(), - target_current_context->fetched_targets()))); - return drag_operation; -} - -void X11WindowOzone::EndMoveLoop() { - std::move(end_drag_callback_).Run(0); -} - -bool X11WindowOzone::DispatchDraggingUiEvent(ui::Event* event) { - // Drag and drop have a priority over other processing. - if (dragging_) { - DCHECK(drag_drop_client_); - - switch (event->type()) { - case ui::ET_MOUSE_MOVED: - case ui::ET_MOUSE_DRAGGED: { - drag_drop_client_->HandleMouseMovement( - event->AsLocatedEvent()->root_location(), - event->AsMouseEvent()->flags(), - event->AsMouseEvent()->time_stamp()); - return true; - } - case ui::ET_MOUSE_RELEASED: - if (!event->AsMouseEvent()->IsLeftMouseButton()) - break; - // Assume that drags are being done with the left mouse button. Only - // break the drag if the left mouse button was released. - drag_drop_client_->HandleMouseReleased(); - dragging_ = false; - ReleaseCapture(); - return true; - case ui::ET_KEY_PRESSED: - if (event->AsKeyEvent()->key_code() != ui::VKEY_ESCAPE) - break; - EndMoveLoop(); - drag_drop_client_->HandleMoveLoopEnded(); - dragging_ = false; - ReleaseCapture(); - return true; - default: - break; - } - } - return false; -} - -void X11WindowOzone::OnXWindowSelectionEvent(XEvent* xev) { - X11Window::OnXWindowSelectionEvent(xev); - DCHECK(drag_drop_client_); - drag_drop_client_->OnSelectionNotify(xev->xselection); -} - -void X11WindowOzone::OnXWindowDragDropEvent(XEvent* xev) { - X11Window::OnXWindowDragDropEvent(xev); - DCHECK(drag_drop_client_); - drag_drop_client_->HandleXdndEvent(xev->xclient); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone.h b/chromium/ui/ozone/platform/x11/x11_window_ozone.h deleted file mode 100644 index 3e2a83fe42d..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone.h +++ /dev/null @@ -1,66 +0,0 @@ -// 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_X11_X11_WINDOW_OZONE_H_ -#define UI_OZONE_PLATFORM_X11_X11_WINDOW_OZONE_H_ - -#include "ui/base/x/x11_drag_drop_client.h" -#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" -#include "ui/platform_window/x11/x11_window.h" - -namespace ui { - -class PlatformWindowDelegate; - -// PlatformWindow implementation for non-ChromeOS X11 Ozone. -// PlatformEvents are ui::Events. -class X11WindowOzone : public X11Window, - public WmDragHandler, - public XDragDropClient::Delegate { - public: - explicit X11WindowOzone(PlatformWindowDelegate* delegate); - ~X11WindowOzone() override; - - X11WindowOzone(const X11WindowOzone&) = delete; - X11WindowOzone& operator=(const X11WindowOzone&) = delete; - - // Overridden from PlatformWindow: - void SetCursor(PlatformCursor cursor) override; - - // Overridden from X11Window: - void Initialize(PlatformWindowInitProperties properties) override; - - private: - // WmDragHandler - void StartDrag(const ui::OSExchangeData& data, - int operation, - gfx::NativeCursor cursor, - base::OnceCallback<void(int)> callback) override; - - // ui::XDragDropClient::Delegate - std::unique_ptr<ui::XTopmostWindowFinder> CreateWindowFinder() override; - int UpdateDrag(const gfx::Point& screen_point) override; - void UpdateCursor( - ui::DragDropTypes::DragOperation negotiated_operation) override; - void OnBeginForeignDrag(XID window) override; - void OnEndForeignDrag() override; - void OnBeforeDragLeave() override; - int PerformDrop() override; - void EndMoveLoop() override; - - // X11Window: - bool DispatchDraggingUiEvent(ui::Event* event) override; - void OnXWindowSelectionEvent(XEvent* xev) override; - void OnXWindowDragDropEvent(XEvent* xev) override; - - // True while the drag initiated in this window is in progress. - bool dragging_ = false; - - std::unique_ptr<XDragDropClient> drag_drop_client_; - base::OnceCallback<void(int)> end_drag_callback_; -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_X11_X11_WINDOW_OZONE_H_ diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone_chromeos.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone_chromeos.cc deleted file mode 100644 index d4f22451189..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone_chromeos.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 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/x11/x11_window_ozone_chromeos.h" - -#include "ui/events/event.h" -#include "ui/ozone/platform/x11/x11_cursor_ozone.h" -#include "ui/platform_window/platform_window_delegate.h" -#include "ui/platform_window/x11/x11_window_manager.h" - -namespace ui { - -X11WindowOzone::X11WindowOzone(PlatformWindowDelegate* delegate) - : X11Window(delegate) {} - -X11WindowOzone::~X11WindowOzone() = default; - -void X11WindowOzone::SetCursor(PlatformCursor cursor) { - X11CursorOzone* cursor_ozone = static_cast<X11CursorOzone*>(cursor); - XWindow::SetCursor(cursor_ozone->xcursor()); -} - -void X11WindowOzone::Initialize(PlatformWindowInitProperties properties) { - X11Window::Initialize(std::move(properties)); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone_chromeos.h b/chromium/ui/ozone/platform/x11/x11_window_ozone_chromeos.h deleted file mode 100644 index 3bcdec53c3c..00000000000 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone_chromeos.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 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_X11_X11_WINDOW_OZONE_CHROMEOS_H_ -#define UI_OZONE_PLATFORM_X11_X11_WINDOW_OZONE_CHROMEOS_H_ - -#include "base/macros.h" -#include "ui/platform_window/x11/x11_window.h" - -namespace ui { - -class PlatformWindowDelegate; - -// PlatformWindow implementation for ChromeOS X11 Ozone. -// PlatformEvents are ui::Events. -class X11WindowOzone : public X11Window { - public: - explicit X11WindowOzone(PlatformWindowDelegate* delegate); - ~X11WindowOzone() override; - - // Overridden from PlatformWindow: - void SetCursor(PlatformCursor cursor) override; - - // Overridden from X11Window: - void Initialize(PlatformWindowInitProperties properties) override; - - DISALLOW_COPY_AND_ASSIGN(X11WindowOzone); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_X11_X11_WINDOW_OZONE_CHROMEOS_H_ diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc index e2c5fb1f609..ddf6448c669 100644 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc +++ b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/ozone/platform/x11/x11_window_ozone.h" +#include "ui/platform_window/x11/x11_window.h" #include <memory> #include <utility> @@ -16,6 +16,7 @@ #include "ui/events/event.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/events/test/events_test_utils_x11.h" +#include "ui/gfx/x/event.h" #include "ui/ozone/test/mock_platform_window_delegate.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -45,9 +46,7 @@ ACTION_P(CloneEvent, event_ptr) { // is more than enough. class TestScreen : public display::ScreenBase { public: - TestScreen() { - ProcessDisplayChanged({}, true); - } + TestScreen() { ProcessDisplayChanged({}, true); } ~TestScreen() override = default; TestScreen(const TestScreen& screen) = delete; TestScreen& operator=(const TestScreen& screen) = delete; @@ -88,16 +87,18 @@ class X11WindowOzoneTest : public testing::Test { EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_)) .WillOnce(StoreWidget(widget)); PlatformWindowInitProperties init_params(bounds); - auto window = std::make_unique<X11WindowOzone>(delegate); + auto window = std::make_unique<X11Window>(delegate); window->Initialize(std::move(init_params)); return std::move(window); } - void DispatchXEvent(XEvent* event, gfx::AcceleratedWidget widget) { - DCHECK_EQ(GenericEvent, event->type); + void DispatchXEvent(x11::Event* event, gfx::AcceleratedWidget widget) { + DCHECK_EQ(x11::GeGenericEvent::opcode, event->xlib_event().type); XIDeviceEvent* device_event = - static_cast<XIDeviceEvent*>(event->xcookie.data); + static_cast<XIDeviceEvent*>(event->xlib_event().xcookie.data); device_event->event = widget; + event->As<x11::Input::DeviceEvent>()->event = + static_cast<x11::Window>(widget); event_source_->ProcessXEvent(event); } @@ -186,8 +187,8 @@ TEST_F(X11WindowOzoneTest, SendPlatformEventToCapturedWindow) { EXPECT_EQ(gfx::Point(-277, 215), event->AsLocatedEvent()->location()); } -// This test case ensures window_manager properly provides X11WindowOzone -// instances as they are created/destroyed. +// This test case ensures window_manager properly provides X11Window instances +// as they are created/destroyed. TEST_F(X11WindowOzoneTest, GetWindowFromAcceleratedWigets) { MockPlatformWindowDelegate delegate; gfx::Rect bounds(0, 0, 100, 100); @@ -225,12 +226,12 @@ TEST_F(X11WindowOzoneTest, MouseEnterAndDelete) { auto window_2 = CreatePlatformWindow(&delegate_2, bounds_2, &widget_2); EXPECT_CALL(delegate_1, OnMouseEnter()).Times(1); - window_manager()->MouseOnWindow(static_cast<X11WindowOzone*>(window_1.get())); + window_manager()->MouseOnWindow(static_cast<X11Window*>(window_1.get())); // The mouse is already on window_1, and this should not call OnMouseEnter. - window_manager()->MouseOnWindow(static_cast<X11WindowOzone*>(window_1.get())); + window_manager()->MouseOnWindow(static_cast<X11Window*>(window_1.get())); EXPECT_CALL(delegate_2, OnMouseEnter()).Times(1); - window_manager()->MouseOnWindow(static_cast<X11WindowOzone*>(window_2.get())); + window_manager()->MouseOnWindow(static_cast<X11Window*>(window_2.get())); EXPECT_EQ(window_2.get(), window_manager()->window_mouse_currently_on_for_test()); diff --git a/chromium/ui/ozone/public/cursor_factory_ozone.cc b/chromium/ui/ozone/public/cursor_factory_ozone.cc deleted file mode 100644 index 95caf2a8a0b..00000000000 --- a/chromium/ui/ozone/public/cursor_factory_ozone.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 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/public/cursor_factory_ozone.h" - -#include "base/check_op.h" -#include "base/notreached.h" - -namespace ui { - -namespace { - -CursorFactoryOzone* g_instance = nullptr; - -} // namespace - -CursorFactoryOzone::CursorFactoryOzone() { - DCHECK(!g_instance) - << "There should only be a single CursorFactoryOzone per thread."; - g_instance = this; -} - -CursorFactoryOzone::~CursorFactoryOzone() { - DCHECK_EQ(g_instance, this); - g_instance = nullptr; -} - -CursorFactoryOzone* CursorFactoryOzone::GetInstance() { - DCHECK(g_instance); - return g_instance; -} - -PlatformCursor CursorFactoryOzone::GetDefaultCursor(mojom::CursorType type) { - NOTIMPLEMENTED(); - return NULL; -} - -PlatformCursor CursorFactoryOzone::CreateImageCursor(const SkBitmap& bitmap, - const gfx::Point& hotspot, - float bitmap_dpi) { - NOTIMPLEMENTED(); - return NULL; -} - -PlatformCursor CursorFactoryOzone::CreateAnimatedCursor( - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& hotspot, - int frame_delay_ms, - float bitmap_dpi) { - NOTIMPLEMENTED(); - return NULL; -} - -void CursorFactoryOzone::RefImageCursor(PlatformCursor cursor) { - NOTIMPLEMENTED(); -} - -void CursorFactoryOzone::UnrefImageCursor(PlatformCursor cursor) { - NOTIMPLEMENTED(); -} - -} // namespace ui diff --git a/chromium/ui/ozone/public/cursor_factory_ozone.h b/chromium/ui/ozone/public/cursor_factory_ozone.h deleted file mode 100644 index a179bf08e73..00000000000 --- a/chromium/ui/ozone/public/cursor_factory_ozone.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014 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_PUBLIC_CURSOR_FACTORY_OZONE_H_ -#define UI_OZONE_PUBLIC_CURSOR_FACTORY_OZONE_H_ - -#include <vector> - -#include "base/component_export.h" -#include "ui/base/cursor/mojom/cursor_type.mojom-forward.h" -#include "ui/gfx/native_widget_types.h" - -namespace gfx { -class Point; -} - -namespace ui { - -typedef void* PlatformCursor; - -class COMPONENT_EXPORT(OZONE_BASE) CursorFactoryOzone { - public: - CursorFactoryOzone(); - virtual ~CursorFactoryOzone(); - - // Returns the thread-local instance. - static CursorFactoryOzone* GetInstance(); - - // Return the default cursor of the specified type. The types are listed in - // ui/base/cursor/cursor.h. Default cursors are managed by the implementation - // and must live indefinitely; there's no way to know when to free them. - virtual PlatformCursor GetDefaultCursor(mojom::CursorType type); - - // Return a image cursor from the specified image & hotspot. Image cursors - // are referenced counted and have an initial refcount of 1. Therefore, each - // CreateImageCursor call must be matched with a call to UnrefImageCursor. - virtual PlatformCursor CreateImageCursor(const SkBitmap& bitmap, - const gfx::Point& hotspot, - float bitmap_dpi); - - // Return a animated cursor from the specified image & hotspot. Animated - // cursors are referenced counted and have an initial refcount of 1. - // Therefore, each CreateAnimatedCursor call must be matched with a call to - // UnrefImageCursor. - virtual PlatformCursor CreateAnimatedCursor( - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& hotspot, - int frame_delay_ms, - float bitmap_dpi); - - // Increment platform image cursor refcount. - virtual void RefImageCursor(PlatformCursor cursor); - - // Decrement platform image cursor refcount. - virtual void UnrefImageCursor(PlatformCursor cursor); -}; - -} // namespace ui - -#endif // UI_OZONE_PUBLIC_CURSOR_FACTORY_OZONE_H_ diff --git a/chromium/ui/ozone/public/input_controller.cc b/chromium/ui/ozone/public/input_controller.cc index 2f2bd8688a3..6b12bb5dff6 100644 --- a/chromium/ui/ozone/public/input_controller.cc +++ b/chromium/ui/ozone/public/input_controller.cc @@ -46,6 +46,8 @@ class StubInputController : public InputController { void SetPrimaryButtonRight(bool right) override {} void SetMouseReverseScroll(bool enabled) override {} void SetMouseAcceleration(bool enabled) override {} + void SuspendMouseAcceleration() override {} + void EndMouseAccelerationSuspension() override {} void SetMouseScrollAcceleration(bool enabled) override {} void SetTouchpadAcceleration(bool enabled) override {} void SetTouchpadScrollAcceleration(bool enabled) override {} diff --git a/chromium/ui/ozone/public/input_controller.h b/chromium/ui/ozone/public/input_controller.h index cc7c323f683..6de884cb29f 100644 --- a/chromium/ui/ozone/public/input_controller.h +++ b/chromium/ui/ozone/public/input_controller.h @@ -73,6 +73,8 @@ class COMPONENT_EXPORT(OZONE_BASE) InputController { virtual void SetPrimaryButtonRight(bool right) = 0; virtual void SetMouseReverseScroll(bool enabled) = 0; virtual void SetMouseAcceleration(bool enabled) = 0; + virtual void SuspendMouseAcceleration() = 0; + virtual void EndMouseAccelerationSuspension() = 0; virtual void SetMouseScrollAcceleration(bool enabled) = 0; // Touch log collection. diff --git a/chromium/ui/ozone/public/mojom/drm_device.mojom b/chromium/ui/ozone/public/mojom/drm_device.mojom index c9b31a6c68c..9f67565431c 100644 --- a/chromium/ui/ozone/public/mojom/drm_device.mojom +++ b/chromium/ui/ozone/public/mojom/drm_device.mojom @@ -6,6 +6,7 @@ module ui.ozone.mojom; import "mojo/public/mojom/base/file.mojom"; import "mojo/public/mojom/base/file_path.mojom"; +import "ui/display/mojom/display_configuration_params.mojom"; import "ui/display/mojom/display_constants.mojom"; import "ui/display/mojom/display_mode.mojom"; import "ui/display/mojom/display_snapshot.mojom"; @@ -50,13 +51,9 @@ interface DrmDevice { // Instructs the GPU to abandon a DRM device. RemoveGraphicsDevice(mojo_base.mojom.FilePath path); - // Instructs the GPU to disable a DRM device. - DisableNativeDisplay(int64 display_id) => (int64 display_id, bool success); - - // Configures a DRM display returning true on success. - ConfigureNativeDisplay(int64 display_id, - display.mojom.DisplayMode display_mode, - gfx.mojom.Point point) => + // Configures (Enables/Disables) a DRM display, returning true on success. + ConfigureNativeDisplay( + display.mojom.DisplayConfigurationParams display_config_params) => (int64 display_id, bool success); // Gets or sets high-definition content protection (HDCP) (DRM as in diff --git a/chromium/ui/ozone/public/ozone_platform.cc b/chromium/ui/ozone/public/ozone_platform.cc index 0309fc67128..f08c81e9fc3 100644 --- a/chromium/ui/ozone/public/ozone_platform.cc +++ b/chromium/ui/ozone/public/ozone_platform.cc @@ -18,7 +18,6 @@ namespace ui { namespace { - OzonePlatform* g_instance = nullptr; void EnsureInstance() { @@ -45,6 +44,15 @@ OzonePlatform::OzonePlatform() { OzonePlatform::~OzonePlatform() = default; // static +void OzonePlatform::PreEarlyInitialization() { + EnsureInstance(); + if (g_instance->prearly_initialized_) + return; + g_instance->prearly_initialized_ = true; + g_instance->PreEarlyInitialize(); +} + +// static void OzonePlatform::InitializeForUI(const InitParams& args) { EnsureInstance(); if (g_instance->initialized_ui_) @@ -115,4 +123,11 @@ void OzonePlatform::AfterSandboxEntry() { DCHECK(!single_process_); } +void OzonePlatform::PostMainMessageLoopStart( + base::OnceCallback<void()> shutdown_cb) {} + +void OzonePlatform::PostMainMessageLoopRun() {} + +void OzonePlatform::PreEarlyInitialize() {} + } // namespace ui diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h index afb9099af3b..f1bbc12cf0c 100644 --- a/chromium/ui/ozone/public/ozone_platform.h +++ b/chromium/ui/ozone/public/ozone_platform.h @@ -23,8 +23,7 @@ class NativeDisplayDelegate; } namespace ui { - -class CursorFactoryOzone; +class CursorFactory; class InputController; class GpuPlatformSupportHost; class OverlayManagerOzone; @@ -104,6 +103,30 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { bool supports_overlays = false; }; + // Corresponds to chrome_browser_main_extra_parts.h. + // + // The browser process' initialization involves several steps - + // PreEarlyInitialization, PostMainMessageLoopStart, PostMainMessageLoopRun, + // etc. In order to be consistent with that and allow platform specific + // initialization steps, the OzonePlatform has three methods - one static + // PreEarlyInitialization that is expected to do some early non-ui + // initialization (like error handlers that X11 sets), and two non-static + // methods - PostMainmessageLoopStart and PostMainMessageLoopRun. The latter + // two are supposed to be called on a post start and a post-run of the + // MessageLoop. Please note that this methods must be run on the browser' UI + // thread. + // + // Creates OzonePlatform and does pre-early initialization (internally, sets + // error handlers if supported so that we can print errors during the browser + // process' start up). + static void PreEarlyInitialization(); + // Sets error handlers if supported for the browser process after the message + // loop started. It's required to call this so that we can exit cleanly if the + // server can exit before we do. + virtual void PostMainMessageLoopStart(base::OnceCallback<void()> shutdown_cb); + // Resets the error handlers if set. + virtual void PostMainMessageLoopRun(); + // Initializes the subsystems/resources necessary for the UI process (e.g. // events) with additional properties to customize the ozone platform // implementation. Ozone will not retain InitParams after returning from @@ -129,7 +152,7 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // inject these objects themselves. Ownership is retained by OzonePlatform. virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() = 0; virtual ui::OverlayManagerOzone* GetOverlayManager() = 0; - virtual ui::CursorFactoryOzone* GetCursorFactoryOzone() = 0; + virtual ui::CursorFactory* GetCursorFactory() = 0; virtual ui::InputController* GetInputController() = 0; virtual ui::GpuPlatformSupportHost* GetGpuPlatformSupportHost() = 0; virtual std::unique_ptr<SystemInputInjector> CreateSystemInputInjector() = 0; @@ -189,11 +212,16 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { bool single_process() const { return single_process_; } private: + // Optional method for pre-early initialization. In case of X11, sets X11 + // error handlers so that errors can be caught if early initialization fails. + virtual void PreEarlyInitialize(); + virtual void InitializeUI(const InitParams& params) = 0; virtual void InitializeGPU(const InitParams& params) = 0; bool initialized_ui_ = false; bool initialized_gpu_ = false; + bool prearly_initialized_ = false; bool single_process_ = false; diff --git a/chromium/ui/ozone/public/platform_clipboard.h b/chromium/ui/ozone/public/platform_clipboard.h index 0962bc9fd78..5ef3fb3d939 100644 --- a/chromium/ui/ozone/public/platform_clipboard.h +++ b/chromium/ui/ozone/public/platform_clipboard.h @@ -9,6 +9,7 @@ #include <unordered_map> #include <vector> +#include "base/callback_forward.h" #include "base/component_export.h" #include "base/macros.h" #include "base/optional.h" diff --git a/chromium/ui/ozone/public/platform_screen.cc b/chromium/ui/ozone/public/platform_screen.cc index a95cfae1322..c2440a5b237 100644 --- a/chromium/ui/ozone/public/platform_screen.cc +++ b/chromium/ui/ozone/public/platform_screen.cc @@ -4,7 +4,7 @@ #include "ui/ozone/public/platform_screen.h" -#include "base/logging.h" +#include "base/notreached.h" namespace ui { diff --git a/chromium/ui/ozone/public/platform_window_surface.h b/chromium/ui/ozone/public/platform_window_surface.h index f804dd1f795..b316e3ecbdb 100644 --- a/chromium/ui/ozone/public/platform_window_surface.h +++ b/chromium/ui/ozone/public/platform_window_surface.h @@ -6,6 +6,11 @@ #define UI_OZONE_PUBLIC_PLATFORM_WINDOW_SURFACE_H_ #include "base/component_export.h" +#include "build/build_config.h" + +#if defined(OS_FUCHSIA) +#include <fuchsia/images/cpp/fidl.h> +#endif // defined(OS_FUCHSIA) namespace ui { @@ -28,9 +33,14 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformWindowSurface { public: virtual ~PlatformWindowSurface() {} - // Note: GL & Vulkan surface are created through the GLOzone & - // VulkanImplementation interfaces, respectively. - // +#if defined(OS_FUCHSIA) + // Sets the texture of the surface to a new image pipe. + virtual bool SetTextureToNewImagePipe( + fidl::InterfaceRequest<fuchsia::images::ImagePipe2> + image_pipe_request) = 0; +#endif // defined(OS_FUCHSIA) + + // Note: GL surface may be created through the GLOzone interface. // However, you must still create a PlatformWindowSurface and keep it alive in // order to present. }; |