From f3c55a72b9beeef268e156a94be95c431bb46c54 Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Mon, 1 Oct 2018 10:42:58 +0300 Subject: [PATCH 01/17] [TEMP] Revert "Revert "[ozone/common] Make gbm_wrapper to be compiled with system libgbm"" This reverts commit 816f0e1e5c15d7fce9389a428cbd49bbdf158501. This is a temporary revert of the above mentioned commit to make it possible to compile chromium with a system libgbm. Before relanding this, we have to fix ChromeOS. --- ui/ozone/common/linux/gbm_wrapper.cc | 155 +++++++++++++++++++++------ 1 file changed, 122 insertions(+), 33 deletions(-) diff --git a/ui/ozone/common/linux/gbm_wrapper.cc b/ui/ozone/common/linux/gbm_wrapper.cc index ba55b0e568565..c74545e4871d4 100644 --- a/ui/ozone/common/linux/gbm_wrapper.cc +++ b/ui/ozone/common/linux/gbm_wrapper.cc @@ -4,7 +4,9 @@ #include "ui/ozone/common/linux/gbm_wrapper.h" +#include #include +#include #include "base/posix/eintr_wrapper.h" #include "third_party/skia/include/core/SkSurface.h" @@ -15,6 +17,91 @@ namespace gbm_wrapper { +namespace { + +// Minigbm and system linux gbm have some differences. There is no clear way how +// to distinguish linux gbm (Mesa, basically) and minigbm, which can be both +// third_party and minigbm. Thus, use GBM_BO_IMPORT_FD_PLANAR define to +// identify, which gbm is used. +#if defined(GBM_BO_IMPORT_FD_PLANAR) +// Minigbm defines GBM_BO_IMPORT_FD_PLANAR, which is unknown in linux gbm. +// Redefine it in a common define. +#define GBM_BO_IMPORT_FD_DATA GBM_BO_IMPORT_FD_PLANAR + +// There are some methods, which require knowing whether minigbm is used or not. +#ifndef USING_MINIGBM +#define USING_MINIGBM +#endif // USING_MINIGBM + +// Minigbm and system linux gbm have alike gbm_bo_import* structures, but some +// of the data variables have different type. +using gbm_bo_import_fd_data_with_modifier = struct gbm_import_fd_planar_data; +#else +// See comment above. Linux defines GBM_BO_IMPORT_FD_MODIFIER, thus, redefine it +// in a common define. +#define GBM_BO_IMPORT_FD_DATA GBM_BO_IMPORT_FD_MODIFIER +// See comment above. +using gbm_bo_import_fd_data_with_modifier = struct gbm_import_fd_modifier_data; +#endif + +void InitializeImportData(uint32_t format, + const gfx::Size& size, + const std::vector& fds, + const std::vector& planes, + gbm_bo_import_fd_data_with_modifier* fd_data) { + fd_data->width = size.width(); + fd_data->height = size.height(); + fd_data->format = format; + + DCHECK_LE(planes.size(), 3u); + for (size_t i = 0; i < planes.size(); ++i) { + fd_data->fds[i] = fds[i < fds.size() ? i : 0].get(); + fd_data->strides[i] = planes[i].stride; + fd_data->offsets[i] = planes[i].offset; +#if defined(USING_MINIGBM) + fd_data->format_modifiers[i] = planes[i].modifier; +#else + fd_data->modifier = planes[i].modifier; +#endif + } +} + +int GetPlaneFdForBo(gbm_bo* bo, size_t plane) { + DCHECK(plane < gbm_bo_get_plane_count(bo)); + + // System linux gbm (or Mesa gbm) does not provide fds per plane basis. Thus, + // get plane handle and use drm ioctl to get a prime fd out of it avoid having + // two different branches for minigbm and Mesa gbm here. + gbm_device* gbm_dev = gbm_bo_get_device(bo); + int dev_fd = gbm_device_get_fd(gbm_dev); + if (dev_fd <= 0) { + LOG(ERROR) << "Unable to get device fd"; + return -1; + } + + const uint32_t plane_handle = gbm_bo_get_handle_for_plane(bo, plane).u32; + int fd = -1; + int ret; + // Use DRM_RDWR to allow the fd to be mappable in another process. + ret = drmPrimeHandleToFD(dev_fd, plane_handle, DRM_CLOEXEC | DRM_RDWR, &fd); + + // Older DRM implementations blocked DRM_RDWR, but gave a read/write mapping + // anyways + if (ret) + ret = drmPrimeHandleToFD(dev_fd, plane_handle, DRM_CLOEXEC, &fd); + + return ret ? ret : fd; +} + +size_t GetSizeOfPlane(gbm_bo* bo, size_t plane) { + // System linux gbm (or Mesa gbm) does not provide plane size. Thus, calculate + // it by ourselves and avoid having two different branches for minigbm and + // Mesa gbm here. + return gbm_bo_get_height(bo) * gbm_bo_get_stride_for_plane(bo, plane); +} + +} // namespace + class Buffer final : public ui::GbmBuffer { public: Buffer(struct gbm_bo* bo, @@ -76,7 +163,7 @@ class Buffer final : public ui::GbmBuffer { } uint32_t GetPlaneHandle(size_t plane) const override { DCHECK_LT(plane, planes_.size()); - return gbm_bo_get_plane_handle(bo_, plane).u32; + return gbm_bo_get_handle_for_plane(bo_, plane).u32; } uint32_t GetHandle() const override { return gbm_bo_get_handle(bo_).u32; } gfx::NativePixmapHandle ExportHandle() const override { @@ -106,7 +193,12 @@ class Buffer final : public ui::GbmBuffer { uint32_t stride; void* addr; addr = gbm_bo_map(bo_, 0, 0, gbm_bo_get_width(bo_), gbm_bo_get_height(bo_), - GBM_BO_TRANSFER_READ_WRITE, &stride, &mmap_data_, 0); + GBM_BO_TRANSFER_READ_WRITE, &stride, &mmap_data_ +#if defined(USING_MINIGBM) + , + 0 +#endif + ); if (!addr) return nullptr; @@ -147,11 +239,18 @@ std::unique_ptr CreateBufferForBO(struct gbm_bo* bo, std::vector fds; std::vector planes; - const uint64_t modifier = gbm_bo_get_format_modifier(bo); - for (size_t i = 0; i < gbm_bo_get_num_planes(bo); ++i) { + const uint64_t modifier = gbm_bo_get_modifier(bo); + const int plane_count = gbm_bo_get_plane_count(bo); + // The Mesa's gbm implementation explicitly checks whether plane count <= and + // returns 1 if the condition is true. Nevertheless, use a DCHECK here to make + // sure the condition is not broken there. + DCHECK(plane_count > 0); + // Ensure there are no differences in integer signs by casting any possible + // values to size_t. + for (size_t i = 0; i < static_cast(plane_count); ++i) { // The fd returned by gbm_bo_get_fd is not ref-counted and need to be // kept open for the lifetime of the buffer. - base::ScopedFD fd(gbm_bo_get_plane_fd(bo, i)); + base::ScopedFD fd(GetPlaneFdForBo(bo, i)); // TODO(dcastagna): support multiple fds. // crbug.com/642410 @@ -164,9 +263,9 @@ std::unique_ptr CreateBufferForBO(struct gbm_bo* bo, fds.emplace_back(std::move(fd)); } - planes.emplace_back(gbm_bo_get_plane_stride(bo, i), - gbm_bo_get_plane_offset(bo, i), - gbm_bo_get_plane_size(bo, i), modifier); + planes.emplace_back(gbm_bo_get_stride_for_plane(bo, i), + gbm_bo_get_offset(bo, i), GetSizeOfPlane(bo, i), + modifier); } return std::make_unique(bo, format, flags, modifier, std::move(fds), size, std::move(planes)); @@ -213,35 +312,25 @@ class Device final : public ui::GbmDevice { DCHECK_EQ(planes[0].offset, 0); // Try to use scanout if supported. - int gbm_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; + int gbm_flags = GBM_BO_USE_SCANOUT; +#if defined(USING_MINIGBM) + gbm_flags |= GBM_BO_USE_TEXTURING; +#endif if (!gbm_device_is_format_supported(device_, format, gbm_flags)) gbm_flags &= ~GBM_BO_USE_SCANOUT; struct gbm_bo* bo = nullptr; - if (!gbm_device_is_format_supported(device_, format, gbm_flags)) { - LOG(ERROR) << "gbm format not supported: " << format; - return nullptr; - } - - struct gbm_import_fd_planar_data fd_data; - fd_data.width = size.width(); - fd_data.height = size.height(); - fd_data.format = format; - - DCHECK_LE(planes.size(), 3u); - for (size_t i = 0; i < planes.size(); ++i) { - fd_data.fds[i] = fds[i < fds.size() ? i : 0].get(); - fd_data.strides[i] = planes[i].stride; - fd_data.offsets[i] = planes[i].offset; - fd_data.format_modifiers[i] = planes[i].modifier; - } - - // The fd passed to gbm_bo_import is not ref-counted and need to be - // kept open for the lifetime of the buffer. - bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_PLANAR, &fd_data, gbm_flags); - if (!bo) { - LOG(ERROR) << "nullptr returned from gbm_bo_import"; - return nullptr; + if (gbm_device_is_format_supported(device_, format, gbm_flags)) { + gbm_bo_import_fd_data_with_modifier fd_data; + InitializeImportData(format, size, fds, planes, &fd_data); + + // The fd passed to gbm_bo_import is not ref-counted and need to be + // kept open for the lifetime of the buffer. + bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_DATA, &fd_data, gbm_flags); + if (!bo) { + LOG(ERROR) << "nullptr returned from gbm_bo_import"; + return nullptr; + } } return std::make_unique(bo, format, gbm_flags, planes[0].modifier, From 6239398c0cd06421c5bad87d4789c2e1d5f488d8 Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Tue, 2 Oct 2018 09:40:57 +0300 Subject: [PATCH 02/17] [DoNotCarryForward] Enable native gpu memory buffers support This patch enables native gpu memory buffers support by fixing compile time assumptions in client_native_pixmap_factory_dmabuf.cc. Now, the supports_import_from_dmabuf_ field is added to the client_native_pixmap_factory.h interface, and it is used to identify whether *cpu_read_write buffer usages can be supported. Bug: 864914 Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel Change-Id: Ifd7d3c6d3906990a79521867ee4e6a826ce4c618 --- ...host_gpu_memory_buffer_manager_unittest.cc | 4 +- ui/gfx/client_native_pixmap_factory.cc | 5 +- ui/gfx/client_native_pixmap_factory.h | 8 +++ .../client_native_pixmap_factory_dmabuf.cc | 64 +++++++++++-------- .../client_native_pixmap_factory_dmabuf.h | 3 +- .../stub_client_native_pixmap_factory.cc | 2 +- .../cast/client_native_pixmap_factory_cast.cc | 5 ++ .../client_native_pixmap_factory_wayland.cc | 7 +- .../wayland/gpu/gbm_pixmap_wayland.cc | 8 +-- .../wayland/wayland_buffer_manager.cc | 7 +- 10 files changed, 71 insertions(+), 42 deletions(-) diff --git a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc index e534bb060cd25..e6db0b33dd4eb 100644 --- a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc +++ b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc @@ -177,7 +177,9 @@ class TestGpuService : public mojom::GpuService { class FakeClientNativePixmapFactory : public gfx::ClientNativePixmapFactory { public: explicit FakeClientNativePixmapFactory(bool allow_native_buffers) - : allow_native_buffers_(allow_native_buffers) {} + : gfx::ClientNativePixmapFactory( + false /* supports handle import from dmabuf */), + allow_native_buffers_(allow_native_buffers) {} ~FakeClientNativePixmapFactory() override {} // gfx::ClientNativePixmapFactory: diff --git a/ui/gfx/client_native_pixmap_factory.cc b/ui/gfx/client_native_pixmap_factory.cc index 7a4354f250738..c9770b39779c0 100644 --- a/ui/gfx/client_native_pixmap_factory.cc +++ b/ui/gfx/client_native_pixmap_factory.cc @@ -6,7 +6,10 @@ namespace gfx { -ClientNativePixmapFactory::ClientNativePixmapFactory() {} +ClientNativePixmapFactory::ClientNativePixmapFactory( + bool supports_native_pixmap_import_from_dmabuf) + : supports_native_pixmap_import_from_dmabuf_( + supports_native_pixmap_import_from_dmabuf) {} ClientNativePixmapFactory::~ClientNativePixmapFactory() {} diff --git a/ui/gfx/client_native_pixmap_factory.h b/ui/gfx/client_native_pixmap_factory.h index fff5b4cd179d8..349e065c9af0a 100644 --- a/ui/gfx/client_native_pixmap_factory.h +++ b/ui/gfx/client_native_pixmap_factory.h @@ -23,6 +23,7 @@ class Size; // provide a client pixmap for non-GPU processes. class GFX_EXPORT ClientNativePixmapFactory { public: + explicit ClientNativePixmapFactory(bool supports_import_from_dmabuf); virtual ~ClientNativePixmapFactory(); // Returns true if format/usage configuration is supported. @@ -40,7 +41,14 @@ class GFX_EXPORT ClientNativePixmapFactory { protected: ClientNativePixmapFactory(); + bool supports_native_pixmap_import_from_dmabuf() const { + return supports_native_pixmap_import_from_dmabuf_; + } + private: + // Says if ClientNativePixmapDmaBuf can be used to import handle from dmabuf. + const bool supports_native_pixmap_import_from_dmabuf_ = false; + DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapFactory); }; diff --git a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc index 0fdb9cb3f87e3..2001fd05fa1ed 100644 --- a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc +++ b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc @@ -12,12 +12,10 @@ #include "build/build_config.h" #include "ui/gfx/native_pixmap_handle.h" -#if defined(OS_CHROMEOS) -// This can be enabled on all linux but it is not a requirement to support -// glCreateImageChromium+Dmabuf since it uses gfx::BufferUsage::SCANOUT and -// the pixmap does not need to be mappable on the client side. +// Although, it's compiled for all linux platforms, it does not mean dmabuf +// will work there. Check the comment below in the +// ClientNativePixmapFactoryDmabuf for more details. #include "ui/gfx/linux/client_native_pixmap_dmabuf.h" -#endif namespace gfx { @@ -47,7 +45,9 @@ class ClientNativePixmapOpaque : public ClientNativePixmap { class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { public: - ClientNativePixmapFactoryDmabuf() {} + explicit ClientNativePixmapFactoryDmabuf( + bool supports_native_pixmap_import_from_dmabuf) + : ClientNativePixmapFactory(supports_native_pixmap_import_from_dmabuf) {} ~ClientNativePixmapFactoryDmabuf() override {} // ClientNativePixmapFactory: @@ -85,39 +85,34 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { return false; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: { -#if defined(OS_CHROMEOS) + if (!supports_native_pixmap_import_from_dmabuf()) + return false; return #if defined(ARCH_CPU_X86_FAMILY) - // Currently only Intel driver (i.e. minigbm and Mesa) supports R_8 - // RG_88 and NV12. https://crbug.com/356871 + // Currently only Intel driver (i.e. minigbm and + // Mesa) supports R_8 RG_88 and NV12. + // https://crbug.com/356871 format == gfx::BufferFormat::R_8 || format == gfx::BufferFormat::RG_88 || format == gfx::BufferFormat::YUV_420_BIPLANAR || #endif format == gfx::BufferFormat::BGRA_8888; -#else - return false; -#endif } case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: { -#if defined(OS_CHROMEOS) + if (!supports_native_pixmap_import_from_dmabuf()) + return false; // Each platform only supports one camera buffer type. We list the // supported buffer formats on all platforms here. When allocating a // camera buffer the caller is responsible for making sure a buffer is // successfully allocated. For example, allocating YUV420_BIPLANAR // for SCANOUT_CAMERA_READ_WRITE may only work on Intel boards. return format == gfx::BufferFormat::YUV_420_BIPLANAR; -#else - return false; -#endif } case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE: { -#if defined(OS_CHROMEOS) + if (!supports_native_pixmap_import_from_dmabuf()) + return false; // R_8 is used as the underlying pixel format for BLOB buffers. return format == gfx::BufferFormat::R_8; -#else - return false; -#endif } } NOTREACHED(); @@ -134,12 +129,11 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE: -#if defined(OS_CHROMEOS) - return ClientNativePixmapDmaBuf::ImportFromDmabuf(handle, size); -#else - NOTREACHED(); + if (supports_native_pixmap_import_from_dmabuf()) + return ClientNativePixmapDmaBuf::ImportFromDmabuf(handle, size); + NOTREACHED() + << "Native GpuMemoryBuffers are not supported on this platform"; return nullptr; -#endif case gfx::BufferUsage::GPU_READ: case gfx::BufferUsage::SCANOUT: case gfx::BufferUsage::SCANOUT_VDA_WRITE: @@ -155,8 +149,24 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapFactoryDmabuf); }; -ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf() { - return new ClientNativePixmapFactoryDmabuf(); +ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf( + bool supports_native_pixmap_import_from_dmabuf) { +// |supports_native_pixmap_import_from_dmabuf| can be enabled on all linux but +// it is not a requirement to support glCreateImageChromium+Dmabuf since it uses +// gfx::BufferUsage::SCANOUT and the pixmap does not need to be mappable on the +// client side. +// +// At the moment, only Ozone/Wayland platform running on Linux is able to import +// handle from dmabuf in addition to the ChromeOS. This is set in the ozone +// level in the ClientNativePixmapFactoryWayland class. +// +// This is not ideal. The ozone platform should probably set this. +// TODO(rjkroege): do something better here. +#if defined(OS_CHROMEOS) + supports_native_pixmap_import_from_dmabuf = true; +#endif + return new ClientNativePixmapFactoryDmabuf( + supports_native_pixmap_import_from_dmabuf); } } // namespace gfx diff --git a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h index 7f802a64a01f9..730b28debcc3d 100644 --- a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h +++ b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h @@ -10,7 +10,8 @@ namespace gfx { -GFX_EXPORT ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf(); +GFX_EXPORT ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf( + bool supports_import_from_dmabuf = false); } // namespace gfx diff --git a/ui/ozone/common/stub_client_native_pixmap_factory.cc b/ui/ozone/common/stub_client_native_pixmap_factory.cc index 93e5963166620..d97d56377f924 100644 --- a/ui/ozone/common/stub_client_native_pixmap_factory.cc +++ b/ui/ozone/common/stub_client_native_pixmap_factory.cc @@ -11,7 +11,7 @@ namespace { class StubClientNativePixmapFactory : public gfx::ClientNativePixmapFactory { public: - StubClientNativePixmapFactory() {} + StubClientNativePixmapFactory() : ClientNativePixmapFactory(false) {} ~StubClientNativePixmapFactory() override {} // ClientNativePixmapFactory: diff --git a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc index 4aa44c770ba72..49ede804972ab 100644 --- a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc +++ b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc @@ -35,6 +35,11 @@ class ClientNativePixmapCast : public gfx::ClientNativePixmap { class ClientNativePixmapFactoryCast : public gfx::ClientNativePixmapFactory { public: + ClientNativePixmapFactoryCast() + : gfx::ClientNativePixmapFactory( + true /* supports import handle from dmabufs */) {} + ~ClientNativePixmapFactoryCast() override {} + // ClientNativePixmapFactoryCast implementation: bool IsConfigurationSupported(gfx::BufferFormat format, gfx::BufferUsage usage) const override { diff --git a/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc b/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc index 110dc9f3ab2d9..b2a36d7c0c248 100644 --- a/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc +++ b/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc @@ -15,8 +15,11 @@ namespace ui { // support when Wayland dmabuf is used. class ClientNativePixmapFactoryWayland : public gfx::ClientNativePixmapFactory { public: - ClientNativePixmapFactoryWayland() { - dmabuf_factory_.reset(gfx::CreateClientNativePixmapFactoryDmabuf()); + ClientNativePixmapFactoryWayland() + : ClientNativePixmapFactory( + true /* supports handle import from dmabufs */) { + dmabuf_factory_.reset(gfx::CreateClientNativePixmapFactoryDmabuf( + supports_native_pixmap_import_from_dmabuf())); } ~ClientNativePixmapFactoryWayland() override {} diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc index c08798ab0f57e..62d42fcc06f2c 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc +++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc @@ -58,13 +58,7 @@ bool GbmPixmapWayland::InitializeBuffer(gfx::Size size, break; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: - // mmap cannot be used with gbm buffers on a different process. That is, - // Linux disallows this and "permission denied" is returned. To overcome - // this and make software rasterization working, buffers must be created - // on the browser process and gbm_bo_map must be used. - // TODO(msisov): add support fir these two buffer usage cases. - // https://crbug.com/864914 - LOG(FATAL) << "This scenario is not supported in Wayland now"; + flags = GBM_BO_USE_LINEAR; break; default: NOTREACHED() << "Not supported buffer format"; diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager.cc b/ui/ozone/platform/wayland/wayland_buffer_manager.cc index 59f2b12829786..12948651ffbbb 100644 --- a/ui/ozone/platform/wayland/wayland_buffer_manager.cc +++ b/ui/ozone/platform/wayland/wayland_buffer_manager.cc @@ -313,7 +313,11 @@ void WaylandBufferManager::CreateSucceededInternal( } } - DCHECK(buffer); + // It can happen that buffer was destroyed by a client while the Wayland + // compositor was processing a request to create a wl_buffer. + if (!buffer) + return; + buffer->wl_buffer.reset(new_buffer); buffer->params = nullptr; zwp_linux_buffer_params_v1_destroy(params); @@ -356,7 +360,6 @@ void WaylandBufferManager::CreateSucceeded( struct zwp_linux_buffer_params_v1* params, struct wl_buffer* new_buffer) { WaylandBufferManager* self = static_cast(data); - DCHECK(self); self->CreateSucceededInternal(params, new_buffer); } From 595d7f136f86c0118916f0de0506c8621280e2ea Mon Sep 17 00:00:00 2001 From: Julie Jeongeun Kim Date: Mon, 8 Oct 2018 01:51:46 +0900 Subject: [PATCH 03/17] [DoNotCarryForward] [Ozone] Implement starting dragging with ozone It introduces DesktopDragDropClientOzone class to manage drag-and-drop. 'DesktopWindowTreeHostPlatform::CreateDragDropClient' creates DesktopDragDropClientOzone and it's connected to platform windows and has an internal runloop like other DragDropClients. It adds WmDragHandler and sets the platform window as a WmDragHandler. When DesktopWindowTreeHostPlatform starts dragging, it gets WmDragHandler through the platform window and sends the dragging request to the platform layer. It also adds WmDropHandler and sets DesktopDragDropClientOzone as a WmDropHandler. When each platform receives messages related to the drop operation, it delivers them to DesktopDragDropClientOzone. It implements 'StartDragAndDrop' of 'DragDropClient' for Ozone and adds 'OnDragSessionClosed' to DesktopDragDropClientOzone to finish dragging session. As Wayland platform window has the drag and drop implementation at the platform layer already, it extends WaylandWindow interfaces, StartDrag. With this change, Ozone/Wayland build can start dragging from Chromium to other applications. Test=DesktopDragDropClientOzoneTest.StartDrag BUG=875164 Change-Id: I4ebaedfae5f6affb80b4d6c7c6d8ed3ba86ef4ca --- .../test/test_compositor_host_ozone.cc | 6 +- ui/ozone/platform/wayland/wayland_window.cc | 22 ++- ui/ozone/platform/wayland/wayland_window.h | 11 +- ui/platform_window/platform_window_delegate.h | 3 +- .../platform_window_handler/BUILD.gn | 4 + .../wm_drag_handler.cc | 26 ++++ .../platform_window_handler/wm_drag_handler.h | 35 +++++ .../wm_drop_handler.cc | 26 ++++ .../platform_window_handler/wm_drop_handler.h | 31 ++++ ui/views/BUILD.gn | 17 ++- .../desktop_drag_drop_client_ozone.cc | 117 ++++++++++++++++ .../desktop_drag_drop_client_ozone.h | 77 ++++++++++ ...desktop_drag_drop_client_ozone_unittest.cc | 132 ++++++++++++++++++ .../desktop_window_tree_host_platform.cc | 7 +- 14 files changed, 503 insertions(+), 11 deletions(-) create mode 100644 ui/platform_window/platform_window_handler/wm_drag_handler.cc create mode 100644 ui/platform_window/platform_window_handler/wm_drag_handler.h create mode 100644 ui/platform_window/platform_window_handler/wm_drop_handler.cc create mode 100644 ui/platform_window/platform_window_handler/wm_drop_handler.h create mode 100644 ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc create mode 100644 ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h create mode 100644 ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc diff --git a/ui/compositor/test/test_compositor_host_ozone.cc b/ui/compositor/test/test_compositor_host_ozone.cc index 38d067327fd38..54e62c3fffa9a 100644 --- a/ui/compositor/test/test_compositor_host_ozone.cc +++ b/ui/compositor/test/test_compositor_host_ozone.cc @@ -87,7 +87,11 @@ TestCompositorHostOzone::TestCompositorHostOzone( false /* enable_surface_synchronization */, false /* enable_pixel_canvas */) {} -TestCompositorHostOzone::~TestCompositorHostOzone() {} +TestCompositorHostOzone::~TestCompositorHostOzone() { + // |window_| should be destroyed earlier than |window_delegate_| as it refers + // to its delegate on destroying. + window_.reset(); +} void TestCompositorHostOzone::Show() { ui::PlatformWindowInitProperties properties; diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc index 0d02721728298..2587c6723675b 100644 --- a/ui/ozone/platform/wayland/wayland_window.cc +++ b/ui/ozone/platform/wayland/wayland_window.cc @@ -20,6 +20,7 @@ #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v6.h" #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v5.h" #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v6.h" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" #include "ui/platform_window/platform_window_init_properties.h" namespace ui { @@ -79,6 +80,9 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, // 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, AsWmDragHandler()); } WaylandWindow::~WaylandWindow() { @@ -216,6 +220,12 @@ void WaylandWindow::DispatchHostWindowDragMovement( connection_->ScheduleFlush(); } +void WaylandWindow::StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) { + connection_->StartDrag(data, operation); +} + void WaylandWindow::Show() { if (!is_tooltip_) // Tooltip windows should not get keyboard focus set_keyboard_focus(true); @@ -570,7 +580,13 @@ void WaylandWindow::OnDragLeave() { } void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) { - NOTIMPLEMENTED_LOG_ONCE(); + WmDropHandler* drop_handler = GetWmDropHandler(*delegate_); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return; + } + + drop_handler->OnDragSessionClosed(dnd_action); } bool WaylandWindow::IsMinimized() const { @@ -619,4 +635,8 @@ WmMoveResizeHandler* WaylandWindow::AsWmMoveResizeHandler() { return static_cast(this); } +WmDragHandler* WaylandWindow::AsWmDragHandler() { + return static_cast(this); +} + } // namespace ui diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h index ad9eb0c983e5e..3f63b248db8d0 100644 --- a/ui/ozone/platform/wayland/wayland_window.h +++ b/ui/ozone/platform/wayland/wayland_window.h @@ -12,6 +12,7 @@ #include "ui/ozone/platform/wayland/wayland_object.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" #include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" namespace gfx { @@ -35,7 +36,8 @@ class XDGShellObjectFactory; class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher, - public WmMoveResizeHandler { + public WmMoveResizeHandler, + public WmDragHandler { public: WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection); @@ -81,6 +83,11 @@ class WaylandWindow : public PlatformWindow, int hittest, const gfx::Point& pointer_location) override; + // WmDragHandler + void StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) override; + // PlatformWindow void Show() override; void Hide() override; @@ -143,6 +150,8 @@ class WaylandWindow : public PlatformWindow, WmMoveResizeHandler* AsWmMoveResizeHandler(); + WmDragHandler* AsWmDragHandler(); + PlatformWindowDelegate* delegate_; WaylandConnection* connection_; WaylandWindow* parent_window_ = nullptr; diff --git a/ui/platform_window/platform_window_delegate.h b/ui/platform_window/platform_window_delegate.h index bf8b46b61ee15..7dbb9ceb2ce72 100644 --- a/ui/platform_window/platform_window_delegate.h +++ b/ui/platform_window/platform_window_delegate.h @@ -5,6 +5,7 @@ #ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_DELEGATE_H_ #define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_DELEGATE_H_ +#include "ui/base/class_property.h" #include "ui/gfx/native_widget_types.h" namespace gfx { @@ -23,7 +24,7 @@ enum PlatformWindowState { PLATFORM_WINDOW_STATE_FULLSCREEN, }; -class PlatformWindowDelegate { +class PlatformWindowDelegate : public PropertyHandler { public: virtual ~PlatformWindowDelegate() {} diff --git a/ui/platform_window/platform_window_handler/BUILD.gn b/ui/platform_window/platform_window_handler/BUILD.gn index be75a75c702ee..10e53a4864636 100644 --- a/ui/platform_window/platform_window_handler/BUILD.gn +++ b/ui/platform_window/platform_window_handler/BUILD.gn @@ -8,6 +8,10 @@ jumbo_component("platform_window_handler") { output_name = "platform_window_handler_libs" sources = [ + "wm_drag_handler.cc", + "wm_drag_handler.h", + "wm_drop_handler.cc", + "wm_drop_handler.h", "wm_move_resize_handler.cc", "wm_move_resize_handler.h", "wm_platform_export.h", diff --git a/ui/platform_window/platform_window_handler/wm_drag_handler.cc b/ui/platform_window/platform_window_handler/wm_drag_handler.cc new file mode 100644 index 0000000000000..294fefecf90a7 --- /dev/null +++ b/ui/platform_window/platform_window_handler/wm_drag_handler.cc @@ -0,0 +1,26 @@ +// 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 "ui/platform_window/platform_window_handler/wm_drag_handler.h" + +#include "ui/base/class_property.h" +#include "ui/platform_window/platform_window.h" + +DEFINE_UI_CLASS_PROPERTY_TYPE(ui::WmDragHandler*) + +namespace ui { + +DEFINE_UI_CLASS_PROPERTY_KEY(WmDragHandler*, kWmDragHandlerKey, nullptr); + +void SetWmDragHandler(PlatformWindow* platform_window, + WmDragHandler* drag_handler) { + platform_window->SetProperty(kWmDragHandlerKey, drag_handler); +} + +WmDragHandler* GetWmDragHandler(const PlatformWindow& platform_window) { + return platform_window.GetProperty(kWmDragHandlerKey); + ; +} + +} // namespace ui diff --git a/ui/platform_window/platform_window_handler/wm_drag_handler.h b/ui/platform_window/platform_window_handler/wm_drag_handler.h new file mode 100644 index 0000000000000..15ff9267f6ab3 --- /dev/null +++ b/ui/platform_window/platform_window_handler/wm_drag_handler.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_ +#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_ + +#include "ui/gfx/native_widget_types.h" +#include "ui/platform_window/platform_window_handler/wm_platform_export.h" + +namespace ui { +class OSExchangeData; +class PlatformWindow; + +class WmDragHandler { + public: + // Starts dragging with |data| which it wants to deliver to the destination. + // |operation| is the suggested operation to the destination and the + // destination sets the final operation when the drop action is performed. + virtual void StartDrag(const OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) = 0; + + protected: + virtual ~WmDragHandler() {} +}; + +WM_PLATFORM_EXPORT void SetWmDragHandler(PlatformWindow* platform_window, + WmDragHandler* drag_handler); +WM_PLATFORM_EXPORT WmDragHandler* GetWmDragHandler( + const PlatformWindow& platform_window); + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_ diff --git a/ui/platform_window/platform_window_handler/wm_drop_handler.cc b/ui/platform_window/platform_window_handler/wm_drop_handler.cc new file mode 100644 index 0000000000000..804805a03a6c1 --- /dev/null +++ b/ui/platform_window/platform_window_handler/wm_drop_handler.cc @@ -0,0 +1,26 @@ +// 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 "ui/platform_window/platform_window_handler/wm_drop_handler.h" + +#include "ui/base/class_property.h" +#include "ui/platform_window/platform_window_delegate.h" + +DEFINE_UI_CLASS_PROPERTY_TYPE(ui::WmDropHandler*) + +namespace ui { + +DEFINE_UI_CLASS_PROPERTY_KEY(WmDropHandler*, kWmDropHandlerKey, nullptr); + +void SetWmDropHandler(PlatformWindowDelegate* delegate, + WmDropHandler* drop_handler) { + delegate->SetProperty(kWmDropHandlerKey, drop_handler); +} + +WmDropHandler* GetWmDropHandler(const PlatformWindowDelegate& delegate) { + return delegate.GetProperty(kWmDropHandlerKey); + ; +} + +} // namespace ui diff --git a/ui/platform_window/platform_window_handler/wm_drop_handler.h b/ui/platform_window/platform_window_handler/wm_drop_handler.h new file mode 100644 index 0000000000000..54cfa49b7b92d --- /dev/null +++ b/ui/platform_window/platform_window_handler/wm_drop_handler.h @@ -0,0 +1,31 @@ +// 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. + +#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_ +#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_ + +#include "ui/platform_window/platform_window_handler/wm_platform_export.h" + +namespace ui { +class PlatformWindowDelegate; + +class WmDropHandler { + public: + // Notifies that Drag and Drop is completed or canceled and the session is + // finished. If Drag and Drop is completed, |operation| has the result + // operation. + virtual void OnDragSessionClosed(int operation) = 0; + + protected: + virtual ~WmDropHandler() {} +}; + +WM_PLATFORM_EXPORT void SetWmDropHandler(PlatformWindowDelegate* delegate, + WmDropHandler* drop_handler); +WM_PLATFORM_EXPORT WmDropHandler* GetWmDropHandler( + const PlatformWindowDelegate& delegate); + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_ diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 01f9b8bb03b4d..36a139dc43007 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn @@ -717,8 +717,14 @@ jumbo_component("views") { ] deps += [ "//ui/events:dom_keyboard_layout" ] } else if (use_ozone) { - public += [ "widget/desktop_aura/desktop_screen_ozone.h" ] - sources += [ "widget/desktop_aura/desktop_screen_ozone.cc" ] + public += [ + "widget/desktop_aura/desktop_drag_drop_client_ozone.h", + "widget/desktop_aura/desktop_screen_ozone.h", + ] + sources += [ + "widget/desktop_aura/desktop_drag_drop_client_ozone.cc", + "widget/desktop_aura/desktop_screen_ozone.cc", + ] } if (is_linux) { sources += [ @@ -1175,8 +1181,10 @@ test("views_unittests") { } if (is_linux && !is_chromeos && !use_x11) { - sources += - [ "widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc" ] + sources += [ + "widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc", + "widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc", + ] } if (!is_chromeos) { @@ -1187,6 +1195,7 @@ test("views_unittests") { ":test_support", ":views_unittests_sources", "//mojo/core/embedder", + "//ui/platform_window/platform_window_handler", ] } diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc new file mode 100644 index 0000000000000..13c5ce44d05d7 --- /dev/null +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc @@ -0,0 +1,117 @@ +// 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 "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h" + +#include "base/run_loop.h" +#include "ui/aura/client/capture_client.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aura.h" +#include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" + +namespace views { + +DesktopDragDropClientOzone::DesktopDragDropClientOzone( + aura::Window* root_window, + views::DesktopNativeCursorManager* cursor_manager, + ui::WmDragHandler* drag_handler, + ui::PlatformWindowDelegate* delegate) + : root_window_(root_window), + cursor_manager_(cursor_manager), + drag_handler_(drag_handler) { + // Set a class property key, which allows |this| to be used for drop action. + SetWmDropHandler(delegate, AsWmDropHandler()); +} + +DesktopDragDropClientOzone::~DesktopDragDropClientOzone() = default; + +int DesktopDragDropClientOzone::StartDragAndDrop( + const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) { + if (!drag_handler_) { + LOG(ERROR) << "Failed to get dragHandler."; + return ui::DragDropTypes::DragOperation::DRAG_NONE; + } + + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + quit_closure_ = run_loop.QuitClosure(); + + // Chrome expects starting drag and drop to release capture. + aura::Window* capture_window = + aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow(); + if (capture_window) + capture_window->ReleaseCapture(); + + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(root_window); + + initial_cursor_ = source_window->GetHost()->last_cursor(); + drag_operation_ = operation; + cursor_client->SetCursor( + cursor_manager_->GetInitializedCursor(ui::CursorType::kGrabbing)); + + drag_handler_->StartDrag(data, operation, cursor_client->GetCursor()); + in_move_loop_ = true; + run_loop.Run(); + return drag_operation_; +} + +void DesktopDragDropClientOzone::DragCancel() { + QuitClosure(); + DragDropSessionCompleted(); +} + +bool DesktopDragDropClientOzone::IsDragDropInProgress() { + return in_move_loop_; +} + +void DesktopDragDropClientOzone::AddObserver( + aura::client::DragDropClientObserver* observer) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientOzone::RemoveObserver( + aura::client::DragDropClientObserver* observer) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientOzone::OnWindowDestroyed(aura::Window* window) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) { + drag_operation_ = dnd_action; + QuitClosure(); + DragDropSessionCompleted(); +} + +void DesktopDragDropClientOzone::DragDropSessionCompleted() { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(root_window_); + if (!cursor_client) + return; + + cursor_client->SetCursor(initial_cursor_); +} + +void DesktopDragDropClientOzone::QuitClosure() { + in_move_loop_ = false; + if (quit_closure_.is_null()) + return; + std::move(quit_closure_).Run(); +} + +ui::WmDropHandler* DesktopDragDropClientOzone::AsWmDropHandler() { + return static_cast(this); +} + +} // namespace views diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h new file mode 100644 index 0000000000000..3d90f5bc832ab --- /dev/null +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_ + +#include "base/callback.h" +#include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/window_observer.h" +#include "ui/base/cursor/cursor.h" +#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "ui/views/views_export.h" + +namespace views { +class DesktopNativeCursorManager; + +class VIEWS_EXPORT DesktopDragDropClientOzone + : public aura::client::DragDropClient, + public aura::WindowObserver, + public ui::WmDropHandler { + public: + explicit DesktopDragDropClientOzone( + aura::Window* root_window, + views::DesktopNativeCursorManager* cursor_manager, + ui::WmDragHandler* drag_handler, + ui::PlatformWindowDelegate* delegate); + ~DesktopDragDropClientOzone() override; + + // Overridden from aura::client::DragDropClient: + int StartDragAndDrop(const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) override; + void DragCancel() override; + bool IsDragDropInProgress() override; + void AddObserver(aura::client::DragDropClientObserver* observer) override; + void RemoveObserver(aura::client::DragDropClientObserver* observer) override; + + // Overridden from void aura::WindowObserver: + void OnWindowDestroyed(aura::Window* window) override; + + // Overridden from void ui::WmDropHandler: + void OnDragSessionClosed(int operation) override; + + private: + void DragDropSessionCompleted(); + void QuitClosure(); + ui::WmDropHandler* AsWmDropHandler(); + + aura::Window* root_window_; + + DesktopNativeCursorManager* cursor_manager_; + + ui::WmDragHandler* drag_handler_; + + // Cursor in use prior to the move loop starting. Restored when the move loop + // quits. + gfx::NativeCursor initial_cursor_; + + base::OnceClosure quit_closure_; + + // The operation bitfield. + int drag_operation_; + + // The flag that controls whether it has a nested run loop. + bool in_move_loop_ = false; + + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientOzone); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_OZONE_H_ diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc new file mode 100644 index 0000000000000..4498198fecfe4 --- /dev/null +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc @@ -0,0 +1,132 @@ +// 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 views_test_base.h first because the definition of None in X.h +// conflicts with the definition of None in gtest-type-util.h +#include "ui/views/test/views_test_base.h" + +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h" + +namespace views { + +namespace { + +class FakeWmDragHandler; + +static void FinishDrag(ui::PlatformWindowDelegate* delegate, + uint32_t dnd_action) { + ui::WmDropHandler* drop_handler = GetWmDropHandler(*delegate); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return; + } + + drop_handler->OnDragSessionClosed(dnd_action); +} + +// A fake handler, which initiates dragging. +class FakeWmDragHandler : public ui::WmDragHandler { + public: + explicit FakeWmDragHandler(ui::PlatformWindowDelegate* delegate) + : delegate_(delegate) {} + ~FakeWmDragHandler() override = default; + + // ui::WmDragHandler + void StartDrag(const OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) override { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(FinishDrag, delegate_, ui::DragDropTypes::DRAG_COPY)); + } + + private: + ui::PlatformWindowDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(FakeWmDragHandler); +}; + +} // namespace + +class DesktopDragDropClientOzoneTest : public ViewsTestBase { + public: + DesktopDragDropClientOzoneTest() = default; + ~DesktopDragDropClientOzoneTest() override = default; + + int StartDragAndDrop() { + ui::OSExchangeData data; + data.SetString(base::ASCIIToUTF16("Test")); + SkBitmap drag_bitmap; + drag_bitmap.allocN32Pixels(10, 10); + drag_bitmap.eraseARGB(0xFF, 0, 0, 0); + gfx::ImageSkia drag_image(gfx::ImageSkia::CreateFrom1xBitmap(drag_bitmap)); + data.provider().SetDragImage(drag_image, gfx::Vector2d()); + + return client_->StartDragAndDrop( + data, widget_->GetNativeWindow()->GetRootWindow(), + widget_->GetNativeWindow(), gfx::Point(), + ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE, + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + } + + // ViewsTestBase: + void SetUp() override { + ViewsTestBase::SetUp(); + test_views_delegate()->set_use_desktop_native_widgets(true); + + // Create widget to initiate the drags. + widget_.reset(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(widget_.get()); + params.bounds = gfx::Rect(100, 100); + widget_->Init(params); + widget_->Show(); + + aura::Window* window = widget_->GetNativeWindow(); + DesktopWindowTreeHostPlatform* host = + static_cast(window->GetHost()); + + cursor_manager_.reset(new DesktopNativeCursorManager()); + drag_handler_.reset(new FakeWmDragHandler(host)); + client_.reset(new DesktopDragDropClientOzone(window, cursor_manager_.get(), + drag_handler_.get(), host)); + } + + void TearDown() override { + client_.reset(); + cursor_manager_.reset(); + drag_handler_.reset(); + widget_.reset(); + ViewsTestBase::TearDown(); + } + + private: + std::unique_ptr client_; + std::unique_ptr cursor_manager_; + std::unique_ptr drag_handler_; + + // The widget used to initiate drags. + std::unique_ptr widget_; + + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientOzoneTest); +}; + +TEST_F(DesktopDragDropClientOzoneTest, StartDrag) { + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +} // namespace views diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index fbfa8df1f7d27..50327795ad492 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc @@ -14,6 +14,7 @@ #include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" #include "ui/platform_window/platform_window_init_properties.h" #include "ui/views/corewm/tooltip_aura.h" +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/desktop_aura/window_event_filter.h" #include "ui/views/widget/widget_aura_utils.h" @@ -121,9 +122,9 @@ DesktopWindowTreeHostPlatform::CreateTooltip() { std::unique_ptr DesktopWindowTreeHostPlatform::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { - // TODO: needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); - return nullptr; + ui::WmDragHandler* dragHandler = ui::GetWmDragHandler(*(platform_window())); + return std::make_unique(window(), cursor_manager, + dragHandler, this); } void DesktopWindowTreeHostPlatform::Close() { From d78195195a28365698ca1a3b43688a41207526af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?= Date: Wed, 5 Apr 2017 17:52:12 +0200 Subject: [PATCH 04/17] Add a README with newest development activities. --- README.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 865f9a7bad300..647d9cfc1b6b7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,110 @@ -# ![Logo](chrome/app/theme/chromium/product_logo_64.png) Chromium +# Chromium for Wayland -Chromium is an open-source browser project that aims to build a safer, faster, -and more stable way for all users to experience the web. +The goal of this project is to enable +[Chromium browser](https://www.chromium.org/) to run on +[Wayland](https://wayland.freedesktop.org/). Note that contrary to +[01.org/ozone-wayland](https://github.com/01org/ozone-wayland), the idea is +to keep it very close to upstream developments as well as aligned on Google's own +plans. In particular, this fork is rebased against +[Chromium ToT](https://chromium.googlesource.com/chromium/src.git) each week +and patches are upstreamed as soon as possible. -The project's web site is https://www.chromium.org. +The implementation also relies on actively developed Chromium technologies: + +* [Aura](https://www.chromium.org/developers/design-documents/aura/aura-overview) for the user interface and windowing. +* [Ozone](https://chromium.googlesource.com/chromium/src/+/master/docs/ozone_overview.md) as a platform abstraction layer together with the [upstream Wayland backend](https://chromium.googlesource.com/chromium/src.git/+/master/ui/ozone/platform/wayland/). +* [Mojo](https://chromium.googlesource.com/chromium/src/+/master/mojo) to perform IPC communication. + +Notice that the effort done here is also useful to run Chromium with Ozone on +Linux Desktop for X11/Wayland. + +# What is Chromium browser? + +![Logo](chrome/app/theme/chromium/product_logo_64.png) Chromium + +[Chromium](https://www.chromium.org) is an open-source browser project that aims to build a safer, faster, +and more stable way for all users to experience the Web. Documentation in the source is rooted in [docs/README.md](docs/README.md). Learn how to [Get Around the Chromium Source Code Directory Structure ](https://www.chromium.org/developers/how-tos/getting-around-the-chrome-source-code). + +# Building Chromium + +General information is provided by the upstream documentation for +[Chromium on Linux](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md) +and +[Ozone](https://chromium.googlesource.com/chromium/src/+/master/docs/ozone_overview.md). +Here is the summary of commands to build and run Chrome for Wayland: + +``` +gn args out/Ozone --args="use_ozone=true use_xkbcommon=true use_system_minigbm=true" +ninja -C out/Ozone chrome +./out/Ozone/chrome --ozone-platform=wayland + +Please note that Ozone/Wayland also supports GpuMemoryBuffers and in order to enable them, the --enable-native-gpu-buffers must be passed. + +Note that GN defaults to debug builds, which naturally take longer to finish and produce slower binaries at runtime. The 'is_debug=false' GN arguments disables it. + +Also note that some touch oriented Web pages like Google Maps, work better when the Touch Event API is explicitly enabled +in chrome://flags or a command line argument --touch-events=enabled is passed. + +It is also possible to enable proprietary codecs (so that mp4, h264 medias play) with the following GN args: 'proprietary_codecs=true ffmpeg_branding=\"Chrome\"'. +``` + +By default, the `headless`, `x11` and `wayland` Ozone backends are +compiled and X11 is selected when `--ozone-platform` is not specified. +Please refer to the +[GN Configuration notes](https://chromium.googlesource.com/chromium/src/+/master/docs/ozone_overview.md#GN-Configuration-notes) for details on how to change +that behavior. + +# Running Tests + +To be added. + +# Rebase Strategy + +The fork is rebased every week against Chromium ToT. +The goal is to be as close as possible to the latest code, which is +constantly receiving performance and stability fixes. + +Here is the current process: + +* Every week, a member of the Igalia Chromium team takes the rebase shift. + +* Commits that are complementary of each other, receive a "fixup!" prefix on +the commit title, and keep the rest of original commit title unchanged. + +For example: + +``` +$ git log --oneline +commit 1 +commit 2 +commit 3 +fixup! commit 1 +fixup! commit 2 +commit 4 +fixup! commit 2 +(..) +``` + +This allows an easy identification of "fixup" commits, which should be squashed into +their original counterpart commit as part of the next rebase cycle. That way we keep +our Git history clean, and commits as atomic as possible, for when upstreaming. + +Git has [an optimized flow for this](http://fle.github.io/git-tip-keep-your-branch-clean-with-fixup-and-autosquash.html) as well. + +* We always keep the 'ozone-wayland-dev' branch as our primarily development branch. + +This means that force pushes will happen. So every time one of the team members +rebases our branch, the developer should first back up the existing ozone-wayland-dev +browser, with the following naming: ozone-wayland-dev-rXXXX, where XXXX is the respective +Chromium baseline of the branch. + +* Branch acceptance criteria + +Make sure that the patches meet the [coding style](https://www.chromium.org/developers/coding-style) criteria. + +* Keep [our internal buildbot](https://build-chromium.igalia.com/) green. From 0c8315952c19028f6ceb60d7f5fc00894ef30243 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Tue, 10 Oct 2017 11:34:22 +0900 Subject: [PATCH 05/17] Add support for V4L2VDA on Linux This patch enables hardware assisted video decoding via the Chromium V4L2VDA. Including changes when Linux is used. In order to use this, use_linux_v4l2_only flag should be set to true. Signed-off-by: Ryo Kodama fixup! avoid building not declared formats "FRAME", "_SLICE", "V4L2_PIX_FMT_VP9" are not defined in mainline Linux headers. This patch avoids building these formats. Signed-off-by: Ryo Kodama Issue #437 --- media/gpu/BUILD.gn | 24 +++++++++++-------- media/gpu/args.gni | 4 ++++ .../gpu_jpeg_decode_accelerator_factory.cc | 3 ++- .../gpu_video_decode_accelerator_factory.cc | 8 +++++++ .../gpu_video_decode_accelerator_factory.h | 2 ++ media/gpu/v4l2/generic_v4l2_device.cc | 6 ++++- media/gpu/v4l2/v4l2_device.cc | 22 +++++++++++++++++ .../gpu/v4l2/v4l2_video_decode_accelerator.cc | 6 ++++- 8 files changed, 62 insertions(+), 13 deletions(-) diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index 07bfdefda5235..d28f291bb8b2e 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn @@ -16,6 +16,7 @@ buildflag_header("buildflags") { "USE_VAAPI=$use_vaapi", "USE_V4L2_CODEC=$use_v4l2_codec", "USE_LIBV4L2=$use_v4lplugin", + "USE_LINUX_V4L2=$use_linux_v4l2_only", ] } @@ -23,7 +24,7 @@ if (is_mac) { import("//build/config/mac/mac_sdk.gni") } -if (is_chromeos && use_v4lplugin) { +if (use_v4lplugin) { action("libv4l2_generate_stubs") { extra_header = "v4l2/v4l2_stub_header.fragment" @@ -214,12 +215,11 @@ component("gpu") { } } - if (use_v4lplugin) { - sources += get_target_outputs(":libv4l2_generate_stubs") - deps += [ ":libv4l2_generate_stubs" ] - } - if (use_v4l2_codec) { + if (use_v4lplugin) { + sources += get_target_outputs(":libv4l2_generate_stubs") + deps += [ ":libv4l2_generate_stubs" ] + } deps += [ "//third_party/libyuv", "//ui/ozone", @@ -231,15 +231,19 @@ component("gpu") { "v4l2/v4l2_device.h", "v4l2/v4l2_image_processor.cc", "v4l2/v4l2_image_processor.h", - "v4l2/v4l2_jpeg_decode_accelerator.cc", - "v4l2/v4l2_jpeg_decode_accelerator.h", - "v4l2/v4l2_slice_video_decode_accelerator.cc", - "v4l2/v4l2_slice_video_decode_accelerator.h", "v4l2/v4l2_video_decode_accelerator.cc", "v4l2/v4l2_video_decode_accelerator.h", "v4l2/v4l2_video_encode_accelerator.cc", "v4l2/v4l2_video_encode_accelerator.h", ] + if (!use_linux_v4l2_only) { + sources += [ + "v4l2_jpeg_decode_accelerator.cc", + "v4l2_jpeg_decode_accelerator.h", + "v4l2_slice_video_decode_accelerator.cc", + "v4l2_slice_video_decode_accelerator.h", + ] + } libs = [ "EGL", "GLESv2", diff --git a/media/gpu/args.gni b/media/gpu/args.gni index df4b0f980ba47..ac740ffeead49 100644 --- a/media/gpu/args.gni +++ b/media/gpu/args.gni @@ -10,6 +10,10 @@ declare_args() { # platforms which have v4l2 hardware encoder / decoder. use_v4l2_codec = false + # Indicates that only definitions available in the mainline linux kernel + # will be used. + use_linux_v4l2_only = false + # Indicates if VA-API-based hardware acceleration is to be used. This # is typically the case on x86-based ChromeOS devices. use_vaapi = false diff --git a/media/gpu/gpu_jpeg_decode_accelerator_factory.cc b/media/gpu/gpu_jpeg_decode_accelerator_factory.cc index c0dfb52cc775a..1148e752fcf4c 100644 --- a/media/gpu/gpu_jpeg_decode_accelerator_factory.cc +++ b/media/gpu/gpu_jpeg_decode_accelerator_factory.cc @@ -13,7 +13,8 @@ #include "media/gpu/buildflags.h" #include "media/gpu/fake_jpeg_decode_accelerator.h" -#if BUILDFLAG(USE_V4L2_CODEC) && defined(ARCH_CPU_ARM_FAMILY) +#if BUILDFLAG(USE_V4L2_CODEC) && defined(ARCH_CPU_ARM_FAMILY) && \ + !BUILDFLAG(USE_LINUX_V4L2) #define USE_V4L2_JDA #endif diff --git a/media/gpu/gpu_video_decode_accelerator_factory.cc b/media/gpu/gpu_video_decode_accelerator_factory.cc index 67ba4daed7879..4a2e1a9c145a5 100644 --- a/media/gpu/gpu_video_decode_accelerator_factory.cc +++ b/media/gpu/gpu_video_decode_accelerator_factory.cc @@ -24,7 +24,9 @@ #endif #if BUILDFLAG(USE_V4L2_CODEC) #include "media/gpu/v4l2/v4l2_device.h" +#if !BUILDFLAG(USE_LINUX_V4L2) #include "media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h" +#endif #include "media/gpu/v4l2/v4l2_video_decode_accelerator.h" #include "ui/gl/gl_surface_egl.h" #endif @@ -67,10 +69,12 @@ gpu::VideoDecodeAcceleratorCapabilities GetDecoderCapabilitiesInternal( vda_profiles = V4L2VideoDecodeAccelerator::GetSupportedProfiles(); GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( vda_profiles, &capabilities.supported_profiles); +#if !BUILDFLAG(USE_LINUX_V4L2) vda_profiles = V4L2SliceVideoDecodeAccelerator::GetSupportedProfiles(); GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( vda_profiles, &capabilities.supported_profiles); #endif +#endif #if BUILDFLAG(USE_VAAPI) vda_profiles = VaapiVideoDecodeAccelerator::GetSupportedProfiles(); GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( @@ -162,8 +166,10 @@ GpuVideoDecodeAcceleratorFactory::CreateVDA( #endif #if BUILDFLAG(USE_V4L2_CODEC) &GpuVideoDecodeAcceleratorFactory::CreateV4L2VDA, +#if !BUILDFLAG(USE_LINUX_V4L2) &GpuVideoDecodeAcceleratorFactory::CreateV4L2SVDA, #endif +#endif #if BUILDFLAG(USE_VAAPI) &GpuVideoDecodeAcceleratorFactory::CreateVaapiVDA, #endif @@ -217,6 +223,7 @@ GpuVideoDecodeAcceleratorFactory::CreateV4L2VDA( return decoder; } +#if !BUILDFLAG(USE_LINUX_V4L2) std::unique_ptr GpuVideoDecodeAcceleratorFactory::CreateV4L2SVDA( const gpu::GpuDriverBugWorkarounds& workarounds, @@ -232,6 +239,7 @@ GpuVideoDecodeAcceleratorFactory::CreateV4L2SVDA( return decoder; } #endif +#endif #if BUILDFLAG(USE_VAAPI) std::unique_ptr diff --git a/media/gpu/gpu_video_decode_accelerator_factory.h b/media/gpu/gpu_video_decode_accelerator_factory.h index 3c94bdcdc45c2..e9127bf559645 100644 --- a/media/gpu/gpu_video_decode_accelerator_factory.h +++ b/media/gpu/gpu_video_decode_accelerator_factory.h @@ -108,11 +108,13 @@ class MEDIA_GPU_EXPORT GpuVideoDecodeAcceleratorFactory { const gpu::GpuDriverBugWorkarounds& workarounds, const gpu::GpuPreferences& gpu_preferences, MediaLog* media_log) const; +#if !BUILDFLAG(USE_LINUX_V4L2) std::unique_ptr CreateV4L2SVDA( const gpu::GpuDriverBugWorkarounds& workarounds, const gpu::GpuPreferences& gpu_preferences, MediaLog* media_log) const; #endif +#endif #if BUILDFLAG(USE_VAAPI) std::unique_ptr CreateVaapiVDA( const gpu::GpuDriverBugWorkarounds& workarounds, diff --git a/media/gpu/v4l2/generic_v4l2_device.cc b/media/gpu/v4l2/generic_v4l2_device.cc index ca23436a81827..a1772f3cb338a 100644 --- a/media/gpu/v4l2/generic_v4l2_device.cc +++ b/media/gpu/v4l2/generic_v4l2_device.cc @@ -475,9 +475,13 @@ bool GenericV4L2Device::OpenDevicePath(const std::string& path, Type type) { return false; #if BUILDFLAG(USE_LIBV4L2) +#if BUILDFLAG(USE_LINUX_V4L2) + if ( +#else if (type == Type::kEncoder && +#endif HANDLE_EINTR(v4l2_fd_open(device_fd_.get(), V4L2_DISABLE_CONVERSION)) != - -1) { + -1) { VLOGF(2) << "Using libv4l2 for " << path; use_libv4l2_ = true; } diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc index eb8411ad5178f..4eb73b4cbfb62 100644 --- a/media/gpu/v4l2/v4l2_device.cc +++ b/media/gpu/v4l2/v4l2_device.cc @@ -891,6 +891,19 @@ uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format) { } // static +#if BUILDFLAG(USE_LINUX_V4L2) +uint32_t V4L2Device::VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile, + bool slice_based) { + if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { + return V4L2_PIX_FMT_H264; + } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { + return V4L2_PIX_FMT_VP8; + } else { + LOG(FATAL) << "Add more cases as needed"; + return 0; + } +} +#else uint32_t V4L2Device::VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile, bool slice_based) { if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { @@ -931,6 +944,7 @@ VideoCodecProfile V4L2Device::V4L2VP9ProfileToVideoCodecProfile( return VIDEO_CODEC_PROFILE_UNKNOWN; } } +#endif // static std::vector V4L2Device::V4L2PixFmtToVideoCodecProfiles( @@ -941,7 +955,9 @@ std::vector V4L2Device::V4L2PixFmtToVideoCodecProfiles( switch (pix_fmt) { case V4L2_PIX_FMT_H264: +#if !BUILDFLAG(USE_LINUX_V4L2) case V4L2_PIX_FMT_H264_SLICE: +#endif if (is_encoder) { // TODO(posciak): need to query the device for supported H.264 profiles, // for now choose Main as a sensible default. @@ -954,11 +970,14 @@ std::vector V4L2Device::V4L2PixFmtToVideoCodecProfiles( break; case V4L2_PIX_FMT_VP8: +#if !BUILDFLAG(USE_LINUX_V4L2) case V4L2_PIX_FMT_VP8_FRAME: +#endif min_profile = VP8PROFILE_MIN; max_profile = VP8PROFILE_MAX; break; +#if !BUILDFLAG(USE_LINUX_V4L2) case V4L2_PIX_FMT_VP9: case V4L2_PIX_FMT_VP9_FRAME: { v4l2_queryctrl query_ctrl = {}; @@ -985,6 +1004,7 @@ std::vector V4L2Device::V4L2PixFmtToVideoCodecProfiles( } break; } +#endif default: VLOGF(1) << "Unhandled pixelformat " << FourccToString(pix_fmt); @@ -1014,8 +1034,10 @@ uint32_t V4L2Device::V4L2PixFmtToDrmFormat(uint32_t format) { case V4L2_PIX_FMT_RGB32: return DRM_FORMAT_ARGB8888; +#if !BUILDFLAG(USE_LINUX_V4L2) case V4L2_PIX_FMT_MT21: return DRM_FORMAT_MT21; +#endif default: DVLOGF(1) << "Unrecognized format " << FourccToString(format); diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc index e3e7c8de227ed..9226a71184ac2 100644 --- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc @@ -27,6 +27,7 @@ #include "media/base/scopedfd_helper.h" #include "media/base/unaligned_shared_memory.h" #include "media/base/video_types.h" +#include "media/gpu/buildflags.h" #include "media/gpu/v4l2/v4l2_image_processor.h" #include "media/video/h264_parser.h" #include "ui/gfx/geometry/rect.h" @@ -69,7 +70,10 @@ namespace media { // static const uint32_t V4L2VideoDecodeAccelerator::supported_input_fourccs_[] = { - V4L2_PIX_FMT_H264, V4L2_PIX_FMT_VP8, V4L2_PIX_FMT_VP9, + V4L2_PIX_FMT_H264, V4L2_PIX_FMT_VP8, +#if !BUILDFLAG(USE_LINUX_V4L2) + V4L2_PIX_FMT_VP9, +#endif }; struct V4L2VideoDecodeAccelerator::BitstreamBufferRef { From 9352f7af80545dc277291f15dade7aa8721d353e Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Wed, 21 Mar 2018 13:18:17 +0200 Subject: [PATCH 06/17] Add mmap via libv4l to generic_v4l2_device Issue #437 --- media/gpu/v4l2/generic_v4l2_device.cc | 10 ++++++++++ media/gpu/v4l2/v4l2.sig | 2 ++ 2 files changed, 12 insertions(+) diff --git a/media/gpu/v4l2/generic_v4l2_device.cc b/media/gpu/v4l2/generic_v4l2_device.cc index a1772f3cb338a..1a9231601d643 100644 --- a/media/gpu/v4l2/generic_v4l2_device.cc +++ b/media/gpu/v4l2/generic_v4l2_device.cc @@ -103,10 +103,20 @@ void* GenericV4L2Device::Mmap(void* addr, int flags, unsigned int offset) { DCHECK(device_fd_.is_valid()); +#if BUILDFLAG(USE_LIBV4L2) + if (use_libv4l2_) + return v4l2_mmap(addr, len, prot, flags, device_fd_.get(), offset); +#endif return mmap(addr, len, prot, flags, device_fd_.get(), offset); } void GenericV4L2Device::Munmap(void* addr, unsigned int len) { +#if BUILDFLAG(USE_LIBV4L2) + if (use_libv4l2_) { + v4l2_munmap(addr, len); + return; + } +#endif munmap(addr, len); } diff --git a/media/gpu/v4l2/v4l2.sig b/media/gpu/v4l2/v4l2.sig index 4269fb48d592d..71b5b3787d566 100644 --- a/media/gpu/v4l2/v4l2.sig +++ b/media/gpu/v4l2/v4l2.sig @@ -8,3 +8,5 @@ LIBV4L_PUBLIC int v4l2_close(int fd); LIBV4L_PUBLIC int v4l2_ioctl(int fd, unsigned long int request, ...); LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags); +LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, int64_t offset); +LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); From 20d3f5a4fa20402624dbb7e5b4760b75c1efd414 Mon Sep 17 00:00:00 2001 From: Julie Jeongeun Kim Date: Wed, 9 May 2018 04:28:22 +0200 Subject: [PATCH 07/17] First attempt to remove decorations from small windows This patch sets XSetWindowAttributes::override_redirect to True, which removes different properties from windows. At this stage, all windows with width() less than 600 are treated as menus and override_redirect is set to True. It's almost same as we had with mus except services. Issue #38 Issue #417 --- ui/platform_window/x11/x11_window_base.cc | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc index bcbd46cf382e8..e9a2cc3e5e7a8 100644 --- a/ui/platform_window/x11/x11_window_base.cc +++ b/ui/platform_window/x11/x11_window_base.cc @@ -18,6 +18,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/platform_window_init_properties.h" namespace ui { @@ -71,6 +72,24 @@ void X11WindowBase::Create() { swa.background_pixmap = x11::None; swa.bit_gravity = NorthWestGravity; swa.override_redirect = UseTestConfigForPlatformWindows(); + + ::Atom window_type; + // There is now default initialization for this type. Initialize it + // to ::WINDOW here. It will be changed by delelgate if it know the + // type of the window. + ui::PlatformWindowType ui_window_type = ui::PlatformWindowType::kWindow; + // TODO(msisov, jkim): pass PlatformWindowInitProperties here. + if (ui_window_type != ui::PlatformWindowType::PLATFORM_WINDOW_TYPE_WINDOW) { + // Setting this to True, doesn't allow X server to set different + // properties, e.g. decorations. + // TODO(msisov): Investigate further. + // https://tronche.com/gui/x/xlib/window/attributes/override-redirect.html + swa.override_redirect = x11::True; + window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_MENU"); + } else { + window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_NORMAL"); + } + xwindow_ = XCreateWindow(xdisplay_, xroot_window_, bounds_.x(), bounds_.y(), bounds_.width(), bounds_.height(), @@ -80,6 +99,10 @@ void X11WindowBase::Create() { CopyFromParent, // visual CWBackPixmap | CWBitGravity | CWOverrideRedirect, &swa); + XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_WINDOW_TYPE"), + XA_ATOM, 32, PropModeReplace, + reinterpret_cast(&window_type), 1); + // Setup XInput event mask. long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | @@ -138,6 +161,13 @@ void X11WindowBase::Create() { size_hints.win_gravity = StaticGravity; XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); +// Disable native frame by default in non-ChromeOS builds for now. +// TODO(msisov, tonikitoo): check if native frame should be used by checking +// Widget::InitParams::remove_standard_frame. +#if !defined(OS_CHROMEOS) + ui::SetUseOSWindowFrame(xwindow_, false); +#endif + delegate_->OnAcceleratedWidgetAvailable(xwindow_); } From da28d17bbc1281b4092e5705e875e9422ee07c96 Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Tue, 29 Aug 2017 14:23:33 +0300 Subject: [PATCH 08/17] [ozone/x11] Add popup window type support. This commit adds a popup window type to ozone windows. Popup windows are drop down windows, for example. Issue #417 fixup! Add popup window type support. It added case handling on WindowTreeHostPlatform::GetWindowType(). --- ui/platform_window/x11/x11_window_base.cc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc index e9a2cc3e5e7a8..51f5e8e17c380 100644 --- a/ui/platform_window/x11/x11_window_base.cc +++ b/ui/platform_window/x11/x11_window_base.cc @@ -79,15 +79,18 @@ void X11WindowBase::Create() { // type of the window. ui::PlatformWindowType ui_window_type = ui::PlatformWindowType::kWindow; // TODO(msisov, jkim): pass PlatformWindowInitProperties here. - if (ui_window_type != ui::PlatformWindowType::PLATFORM_WINDOW_TYPE_WINDOW) { - // Setting this to True, doesn't allow X server to set different - // properties, e.g. decorations. - // TODO(msisov): Investigate further. - // https://tronche.com/gui/x/xlib/window/attributes/override-redirect.html - swa.override_redirect = x11::True; - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_MENU"); - } else { - window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_NORMAL"); + switch (ui_window_type) { + case ui::PlatformWindowType::kMenu: + window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_MENU"); + swa.override_redirect = x11::True; + break; + case ui::PlatformWindowType::kPopup: + swa.override_redirect = x11::True; + window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_NOTIFICATION"); + break; + default: + window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_NORMAL"); + break; } xwindow_ = From 749c4b5e0a422225778c3de847eed9af645a3ea5 Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Tue, 17 Oct 2017 15:13:32 +0300 Subject: [PATCH 09/17] Add inital support for a tab drag window. This patch adds an initial support to start a move loop and create a new window by dragging a tab. To start with, this commit extends WindowTree::PerformWindowMove, which calls WindowServer::StartMoveLoop -> PlatformWindow::RunMoveLoop. X11 Ozone implementation of PlatformWindow uses WindowMoveLoopClient, which instantiates the move loop and WholeScreenMoveLoop, which creates an invisible window and intercepts all the events from it. Then the system screen location is taken and sent to WindowMoveLoopClient, which updates actual bounds of X11WindowBase. The way it works is precisly the same as in stock X11, but further work to share the code is needed, because there are some difference between stock x11 and ozone x11 event handlings, bounds set and etc. Issue #264 fixup! [ozone/wayland] Add inital support for a tab drag window. adapt to https://crrev.com/c/774778 CanDispatchEvent is modified in such a way that it also tests if the client in a move loop right now. If so, it must dispatch events further once whole_screen_move_loop processes them in order to update mouse locations on aura side. fixup! [ozone/wayland] Add inital support for a tab drag window. It removed changes at services from previous change to make it available without mus and made WindowFinder with ozone work without mus with creating GetLocalProcessWindowAtPointOzone to get gfx::NativeWindow at input position. Issue #430 fixup! Add inital support for a tab drag window. Remove the non-existing code and fix ozone/drm build. Change-Id: I9d3a94a9e47ed2bddaef0253de193d92e01768b0 --- .../ui/views/tabs/window_finder_ozone.cc | 3 + ui/ozone/platform/drm/host/drm_window_host.cc | 6 + ui/ozone/platform/drm/host/drm_window_host.h | 2 + ui/ozone/platform/wayland/wayland_window.cc | 6 + ui/ozone/platform/wayland/wayland_window.h | 2 + ui/ozone/platform/x11/x11_window_ozone.cc | 33 ++- ui/ozone/platform/x11/x11_window_ozone.h | 9 + ui/platform_window/platform_window.h | 5 + ui/platform_window/stub/stub_window.cc | 6 + ui/platform_window/stub/stub_window.h | 2 + ui/platform_window/x11/BUILD.gn | 13 +- .../x11/whole_screen_move_loop.cc | 228 ++++++++++++++++++ .../x11/whole_screen_move_loop.h | 90 +++++++ .../x11/window_move_loop_client.cc | 62 +++++ .../x11/window_move_loop_client.h | 56 +++++ ui/platform_window/x11/x11_window_base.cc | 15 +- ui/platform_window/x11/x11_window_base.h | 3 + .../desktop_window_tree_host_platform.cc | 7 +- .../desktop_aura/x11_move_loop_delegate.h | 2 + 19 files changed, 541 insertions(+), 9 deletions(-) create mode 100644 ui/platform_window/x11/whole_screen_move_loop.cc create mode 100644 ui/platform_window/x11/whole_screen_move_loop.h create mode 100644 ui/platform_window/x11/window_move_loop_client.cc create mode 100644 ui/platform_window/x11/window_move_loop_client.h diff --git a/chrome/browser/ui/views/tabs/window_finder_ozone.cc b/chrome/browser/ui/views/tabs/window_finder_ozone.cc index bb904744b6bdb..2e56be1564e26 100644 --- a/chrome/browser/ui/views/tabs/window_finder_ozone.cc +++ b/chrome/browser/ui/views/tabs/window_finder_ozone.cc @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/views/tabs/window_finder.h" +#include "ui/views/widget/widget.h" gfx::NativeWindow WindowFinder::GetLocalProcessWindowAtPoint( const gfx::Point& screen_point, diff --git a/ui/ozone/platform/drm/host/drm_window_host.cc b/ui/ozone/platform/drm/host/drm_window_host.cc index 66e8b55529071..4bc36568a6b3f 100644 --- a/ui/ozone/platform/drm/host/drm_window_host.cc +++ b/ui/ozone/platform/drm/host/drm_window_host.cc @@ -146,6 +146,12 @@ gfx::Rect DrmWindowHost::GetRestoredBoundsInPixels() const { return gfx::Rect(); } +bool DrmWindowHost::RunMoveLoop(const gfx::Vector2d& drag_offset) { + return false; +} + +void DrmWindowHost::StopMoveLoop() {} + bool DrmWindowHost::CanDispatchEvent(const PlatformEvent& event) { DCHECK(event); diff --git a/ui/ozone/platform/drm/host/drm_window_host.h b/ui/ozone/platform/drm/host/drm_window_host.h index c935c7b9f2af5..74f3d7c8f3ad9 100644 --- a/ui/ozone/platform/drm/host/drm_window_host.h +++ b/ui/ozone/platform/drm/host/drm_window_host.h @@ -79,6 +79,8 @@ class DrmWindowHost : public PlatformWindow, PlatformImeController* GetPlatformImeController() override; void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override; gfx::Rect GetRestoredBoundsInPixels() const override; + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; + void StopMoveLoop() override; // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc index 2587c6723675b..c6b89e43d0d5b 100644 --- a/ui/ozone/platform/wayland/wayland_window.cc +++ b/ui/ozone/platform/wayland/wayland_window.cc @@ -432,6 +432,12 @@ gfx::Rect WaylandWindow::GetRestoredBoundsInPixels() const { return restored_bounds_; } +bool WaylandWindow::RunMoveLoop(const gfx::Vector2d& drag_offset) { + return true; +} + +void WaylandWindow::StopMoveLoop() {} + bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) { // This window is a nested popup window, all the events must be forwarded // to the main popup window. diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h index 3f63b248db8d0..bd0f4d2635477 100644 --- a/ui/ozone/platform/wayland/wayland_window.h +++ b/ui/ozone/platform/wayland/wayland_window.h @@ -110,6 +110,8 @@ class WaylandWindow : public PlatformWindow, PlatformImeController* GetPlatformImeController() override; void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override; gfx::Rect GetRestoredBoundsInPixels() const override; + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; + void StopMoveLoop() override; // PlatformEventDispatcher bool CanDispatchEvent(const PlatformEvent& event) override; diff --git a/ui/ozone/platform/x11/x11_window_ozone.cc b/ui/ozone/platform/x11/x11_window_ozone.cc index c3f17315009ef..9c312abdad9d1 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.cc +++ b/ui/ozone/platform/x11/x11_window_ozone.cc @@ -24,6 +24,11 @@ X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager, auto* event_source = X11EventSourceLibevent::GetInstance(); if (event_source) event_source->AddXEventDispatcher(this); + +// TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. +#if !defined(OS_CHROMEOS) + move_loop_client_.reset(new WindowMoveLoopClient()); +#endif } X11WindowOzone::~X11WindowOzone() { @@ -49,6 +54,24 @@ void X11WindowOzone::SetCursor(PlatformCursor cursor) { XDefineCursor(xdisplay(), xwindow(), cursor_ozone->xcursor()); } +bool X11WindowOzone::RunMoveLoop(const gfx::Vector2d& drag_offset) { +// TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. +#if !defined(OS_CHROMEOS) + DCHECK(move_loop_client_); + ReleaseCapture(); + return move_loop_client_->RunMoveLoop(this, drag_offset); +#endif + return true; +} + +void X11WindowOzone::StopMoveLoop() { +// TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. +#if !defined(OS_CHROMEOS) + ReleaseCapture(); + move_loop_client_->EndMoveLoop(); +#endif +} + void X11WindowOzone::CheckCanDispatchNextPlatformEvent(XEvent* xev) { handle_next_event_ = xwindow() == x11::None ? false : IsEventForXWindow(*xev); } @@ -69,8 +92,14 @@ bool X11WindowOzone::DispatchXEvent(XEvent* xev) { return true; } -bool X11WindowOzone::CanDispatchEvent(const PlatformEvent& event) { - return handle_next_event_; +bool X11WindowOzone::CanDispatchEvent(const PlatformEvent& platform_event) { + bool in_move_loop = +#if !defined(OS_CHROMEOS) + move_loop_client_->IsInMoveLoop(); +#else + false; +#endif + return handle_next_event_ || in_move_loop; } uint32_t X11WindowOzone::DispatchEvent(const PlatformEvent& event) { diff --git a/ui/ozone/platform/x11/x11_window_ozone.h b/ui/ozone/platform/x11/x11_window_ozone.h index e1586341f19d2..6be3f747549df 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.h +++ b/ui/ozone/platform/x11/x11_window_ozone.h @@ -8,6 +8,7 @@ #include "base/macros.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/x11/x11_event_source_libevent.h" +#include "ui/platform_window/x11/window_move_loop_client.h" #include "ui/platform_window/x11/x11_window_base.h" namespace ui { @@ -30,8 +31,11 @@ class X11WindowOzone : public X11WindowBase, // PlatformWindow: void PrepareForShutdown() override; void SetCapture() override; + void ReleaseCapture() override; void SetCursor(PlatformCursor cursor) override; + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; + void StopMoveLoop() override; // XEventDispatcher: void CheckCanDispatchNextPlatformEvent(XEvent* xev) override; @@ -46,6 +50,11 @@ class X11WindowOzone : public X11WindowBase, X11WindowManagerOzone* window_manager_; +// TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. +#if !defined(OS_CHROMEOS) + std::unique_ptr move_loop_client_; +#endif + // Tells if this dispatcher can process next translated event based on a // previous check in ::CheckCanDispatchNextPlatformEvent based on a XID // target. diff --git a/ui/platform_window/platform_window.h b/ui/platform_window/platform_window.h index c7611eee8afd6..ec00f70cf28f9 100644 --- a/ui/platform_window/platform_window.h +++ b/ui/platform_window/platform_window.h @@ -71,6 +71,11 @@ class PlatformWindow : public PropertyHandler { // Sets and gets the restored bounds of the platform-window. virtual void SetRestoredBoundsInPixels(const gfx::Rect& bounds) = 0; virtual gfx::Rect GetRestoredBoundsInPixels() const = 0; + + // Asks to window move client to start move loop. + virtual bool RunMoveLoop(const gfx::Vector2d& drag_offset) = 0; + + virtual void StopMoveLoop() = 0; }; } // namespace ui diff --git a/ui/platform_window/stub/stub_window.cc b/ui/platform_window/stub/stub_window.cc index 154d3e25248fd..070241e6327e4 100644 --- a/ui/platform_window/stub/stub_window.cc +++ b/ui/platform_window/stub/stub_window.cc @@ -80,4 +80,10 @@ gfx::Rect StubWindow::GetRestoredBoundsInPixels() const { return gfx::Rect(); } +bool StubWindow::RunMoveLoop(const gfx::Vector2d& drag_offset) { + return false; +} + +void StubWindow::StopMoveLoop() {} + } // namespace ui diff --git a/ui/platform_window/stub/stub_window.h b/ui/platform_window/stub/stub_window.h index f46c8aa955d11..73c8e20c6c965 100644 --- a/ui/platform_window/stub/stub_window.h +++ b/ui/platform_window/stub/stub_window.h @@ -50,6 +50,8 @@ class STUB_WINDOW_EXPORT StubWindow : public PlatformWindow { PlatformImeController* GetPlatformImeController() override; void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override; gfx::Rect GetRestoredBoundsInPixels() const override; + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; + void StopMoveLoop() override; PlatformWindowDelegate* delegate_; gfx::Rect bounds_; diff --git a/ui/platform_window/x11/BUILD.gn b/ui/platform_window/x11/BUILD.gn index 3e46e8c94c96b..0f737c7c2c1c9 100644 --- a/ui/platform_window/x11/BUILD.gn +++ b/ui/platform_window/x11/BUILD.gn @@ -34,7 +34,18 @@ jumbo_component("x11") { "x11_window_export.h", ] - if (use_x11) { + if (ozone_platform_x11) { + sources += [ + "whole_screen_move_loop.cc", + "whole_screen_move_loop.h", + "window_move_loop_client.cc", + "window_move_loop_client.h", + ] + deps += [ + "//ui/base", + "//ui/base/x", + ] + } else if (use_x11) { sources += [ "x11_window.cc", "x11_window.h", diff --git a/ui/platform_window/x11/whole_screen_move_loop.cc b/ui/platform_window/x11/whole_screen_move_loop.cc new file mode 100644 index 0000000000000..1c5fb1fd409aa --- /dev/null +++ b/ui/platform_window/x11/whole_screen_move_loop.cc @@ -0,0 +1,228 @@ +// Copyright (c) 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 "ui/platform_window/x11/whole_screen_move_loop.h" + +#include +#include + +#include "base/bind.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "ui/aura/client/capture_client.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/x/x11_pointer_grab.h" +#include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_window_event_manager.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/events/ozone/events_ozone.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/scoped_event_dispatcher.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/events/platform/x11/x11_event_source_libevent.h" +#include "ui/gfx/x/x11.h" +#include "ui/platform_window/platform_window_delegate.h" + +namespace ui { + +// XGrabKey requires the modifier mask to explicitly be specified. +const unsigned int kModifiersMasks[] = {0, // No additional modifier. + Mod2Mask, // Num lock + LockMask, // Caps lock + Mod5Mask, // Scroll lock + Mod2Mask | LockMask, + Mod2Mask | Mod5Mask, + LockMask | Mod5Mask, + Mod2Mask | LockMask | Mod5Mask}; + +WholeScreenMoveLoop::WholeScreenMoveLoop(views::X11MoveLoopDelegate* delegate) + : delegate_(delegate), + in_move_loop_(false), + grab_input_window_(x11::None), + grabbed_pointer_(false), + canceled_(false), + weak_factory_(this) {} + +WholeScreenMoveLoop::~WholeScreenMoveLoop() {} + +void WholeScreenMoveLoop::DispatchMouseMovement() { + if (!last_motion_in_screen_ && !last_motion_in_screen_->IsLocatedEvent()) + return; + delegate_->OnMouseMovement( + last_motion_in_screen_->AsLocatedEvent()->location(), + last_motion_in_screen_->flags(), last_motion_in_screen_->time_stamp()); + last_motion_in_screen_.reset(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation: + +bool WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) { + return in_move_loop_; +} + +uint32_t WholeScreenMoveLoop::DispatchEvent( + const ui::PlatformEvent& platform_event) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + + // This method processes all events while the move loop is active. + if (!in_move_loop_) + return ui::POST_DISPATCH_PERFORM_DEFAULT; + + auto* event = static_cast(platform_event); + switch (event->type()) { + case ui::ET_MOUSE_MOVED: + case ui::ET_MOUSE_DRAGGED: { + bool can_send = !last_motion_in_screen_.get(); + + ui::MouseEvent* mouse_event = event->AsMouseEvent(); + gfx::Point root_location = mouse_event->root_location(); + mouse_event->set_location(root_location); + + last_motion_in_screen_ = ui::Event::Clone(*event); + DCHECK(last_motion_in_screen_->IsMouseEvent()); + + if (can_send) + DispatchMouseMovement(); + return ui::POST_DISPATCH_PERFORM_DEFAULT; + } + case ui::ET_MOUSE_RELEASED: { + ui::MouseEvent* mouse_event = event->AsMouseEvent(); + gfx::Point root_location = mouse_event->root_location(); + mouse_event->set_location(root_location); + + last_motion_in_screen_ = ui::Event::Clone(*event); + DCHECK(last_motion_in_screen_->IsMouseEvent()); + + EndMoveLoop(); + return ui::POST_DISPATCH_PERFORM_DEFAULT; + } + case ui::ET_KEY_PRESSED: + canceled_ = true; + EndMoveLoop(); + return ui::POST_DISPATCH_NONE; + default: + break; + } + return ui::POST_DISPATCH_PERFORM_DEFAULT; +} + +bool WholeScreenMoveLoop::RunMoveLoop() { + DCHECK(!in_move_loop_); // Can only handle one nested loop at a time. + + CreateDragInputWindow(gfx::GetXDisplay()); + + if (!GrabPointer()) { + XDestroyWindow(gfx::GetXDisplay(), grab_input_window_); + CHECK(false) << "failed to grab pointer"; + return false; + } + + GrabEscKey(); + + std::unique_ptr old_dispatcher = + std::move(nested_dispatcher_); + nested_dispatcher_ = + ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this); + + base::WeakPtr alive(weak_factory_.GetWeakPtr()); + + in_move_loop_ = true; + canceled_ = false; + base::MessageLoop::ScopedNestableTaskAllower allow_nested; + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + + if (!alive) + return false; + + nested_dispatcher_ = std::move(old_dispatcher); + return !canceled_; +} + +void WholeScreenMoveLoop::UpdateCursor() {} + +void WholeScreenMoveLoop::EndMoveLoop() { + if (!in_move_loop_) + return; + + // Prevent DispatchMouseMovement from dispatching any posted motion event. + last_motion_in_screen_.reset(); + + // TODO(erg): Is this ungrab the cause of having to click to give input focus + // on drawn out windows? Not ungrabbing here screws the X server until I kill + // the chrome process. + + // Ungrab before we let go of the window. + if (grabbed_pointer_) + ui::UngrabPointer(); + else + UpdateCursor(); + + XDisplay* display = gfx::GetXDisplay(); + unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape); + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { + XUngrabKey(display, esc_keycode, kModifiersMasks[i], grab_input_window_); + } + + // Restore the previous dispatcher. + nested_dispatcher_.reset(); + grab_input_window_events_.reset(); + XDestroyWindow(display, grab_input_window_); + grab_input_window_ = x11::None; + in_move_loop_ = false; + quit_closure_.Run(); +} + +bool WholeScreenMoveLoop::GrabPointer() { + XDisplay* display = gfx::GetXDisplay(); + + // Pass "owner_events" as false so that X sends all mouse events to + // |grab_input_window_|. + int ret = ui::GrabPointer(grab_input_window_, false, x11::None); + if (ret != GrabSuccess) { + DLOG(ERROR) << "Grabbing pointer for dragging failed: " + << ui::GetX11ErrorString(display, ret); + } + XFlush(display); + return ret == GrabSuccess; +} + +void WholeScreenMoveLoop::GrabEscKey() { + XDisplay* display = gfx::GetXDisplay(); + unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape); + for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) { + XGrabKey(display, esc_keycode, kModifiersMasks[i], grab_input_window_, + x11::False, GrabModeAsync, GrabModeAsync); + } +} + +void WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { + unsigned long attribute_mask = CWEventMask | CWOverrideRedirect; + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.override_redirect = x11::True; + grab_input_window_ = XCreateWindow(display, DefaultRootWindow(display), -100, + -100, 10, 10, 0, CopyFromParent, InputOnly, + CopyFromParent, attribute_mask, &swa); + uint32_t event_mask = ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask; + grab_input_window_events_.reset( + new ui::XScopedEventSelector(grab_input_window_, event_mask)); + + XMapRaised(display, grab_input_window_); +} + +} // namespace ui diff --git a/ui/platform_window/x11/whole_screen_move_loop.h b/ui/platform_window/x11/whole_screen_move_loop.h new file mode 100644 index 0000000000000..4a18fe908a1d7 --- /dev/null +++ b/ui/platform_window/x11/whole_screen_move_loop.h @@ -0,0 +1,90 @@ +// Copyright (c) 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. + +#ifndef UI_PLATFORM_WINDOW_X11_WHOLE_SCREEN_MOVE_LOOP_H_ +#define UI_PLATFORM_WINDOW_X11_WHOLE_SCREEN_MOVE_LOOP_H_ + +#include + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "ui/base/cursor/cursor.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/x/x11_types.h" +#include "ui/views/widget/desktop_aura/x11_move_loop.h" +#include "ui/views/widget/desktop_aura/x11_move_loop_delegate.h" + +namespace ui { +class MouseEvent; +class ScopedEventDispatcher; +class XScopedEventSelector; +class PlatformWindowDelegate; + +// Runs a nested run loop and grabs the mouse. This is used to implement +// dragging. +class WholeScreenMoveLoop : public ui::PlatformEventDispatcher { + public: + explicit WholeScreenMoveLoop(views::X11MoveLoopDelegate* delegate); + ~WholeScreenMoveLoop() override; + + // ui:::PlatformEventDispatcher: + bool CanDispatchEvent(const ui::PlatformEvent& event) override; + uint32_t DispatchEvent(const ui::PlatformEvent& platform_event) override; + + // X11MoveLoop: + bool RunMoveLoop(); + void UpdateCursor(); + void EndMoveLoop(); + + bool in_move_loop() { return in_move_loop_; } + + private: + // Grabs the pointer, setting the mouse cursor to |cursor|. Returns true if + // successful. + bool GrabPointer(); + + void GrabEscKey(); + + // Creates an input-only window to be used during the drag. + void CreateDragInputWindow(XDisplay* display); + + // Dispatch mouse movement event to |delegate_| in a posted task. + void DispatchMouseMovement(); + + views::X11MoveLoopDelegate* delegate_; + + // Are we running a nested run loop from RunMoveLoop()? + bool in_move_loop_; + std::unique_ptr nested_dispatcher_; + + // An invisible InputOnly window. Keyboard grab and sometimes mouse grab + // are set on this window. + XID grab_input_window_; + + // Events selected on |grab_input_window_|. + std::unique_ptr grab_input_window_events_; + + // Whether the pointer was grabbed on |grab_input_window_|. + bool grabbed_pointer_; + + base::Closure quit_closure_; + + // Keeps track of whether the move-loop is cancled by the user (e.g. by + // pressing escape). + bool canceled_; + + std::unique_ptr last_motion_in_screen_; + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(WholeScreenMoveLoop); +}; + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_X11_WHOLE_SCREEN_MOVE_LOOP_H_ diff --git a/ui/platform_window/x11/window_move_loop_client.cc b/ui/platform_window/x11/window_move_loop_client.cc new file mode 100644 index 0000000000000..cbfe515ddeef3 --- /dev/null +++ b/ui/platform_window/x11/window_move_loop_client.cc @@ -0,0 +1,62 @@ +// Copyright (c) 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 "ui/platform_window/x11/window_move_loop_client.h" + +#include + +#include "base/debug/stack_trace.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/x/x11_util.h" +#include "ui/events/event.h" + +#include "ui/platform_window/platform_window.h" + +namespace ui { + +WindowMoveLoopClient::WindowMoveLoopClient() + : move_loop_(this), window_(nullptr) {} + +WindowMoveLoopClient::~WindowMoveLoopClient() {} + +void WindowMoveLoopClient::OnMouseMovement(const gfx::Point& screen_point, + int flags, + base::TimeTicks event_time) { + gfx::Point system_loc = screen_point - window_offset_; + window_->SetBounds(gfx::Rect(system_loc, gfx::Size())); +} + +void WindowMoveLoopClient::OnMouseReleased() { + EndMoveLoop(); +} + +void WindowMoveLoopClient::OnMoveLoopEnded() { + window_ = nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// DesktopWindowTreeHostLinux, wm::WindowMoveClient implementation: + +bool WindowMoveLoopClient::RunMoveLoop(PlatformWindow* window, + const gfx::Vector2d& drag_offset) { + window_offset_ = drag_offset; + window_ = window; + window_->SetCapture(); + return move_loop_.RunMoveLoop(); +} + +void WindowMoveLoopClient::EndMoveLoop() { + window_->ReleaseCapture(); + move_loop_.EndMoveLoop(); +} + +bool WindowMoveLoopClient::IsInMoveLoop() { + return move_loop_.in_move_loop(); +} + +} // namespace ui diff --git a/ui/platform_window/x11/window_move_loop_client.h b/ui/platform_window/x11/window_move_loop_client.h new file mode 100644 index 0000000000000..2e191f87dc289 --- /dev/null +++ b/ui/platform_window/x11/window_move_loop_client.h @@ -0,0 +1,56 @@ +// Copyright (c) 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. + +#ifndef UI_PLATFORM_WINDOW_X11_WINDOW_MOVE_LOOP_CLIENT_H_ +#define UI_PLATFORM_WINDOW_X11_WINDOW_MOVE_LOOP_CLIENT_H_ + +#include + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "ui/gfx/geometry/point.h" +#include "ui/platform_window/x11/whole_screen_move_loop.h" +#include "ui/platform_window/x11/x11_window_export.h" +#include "ui/views/views_export.h" +#include "ui/wm/public/window_move_client.h" + +namespace ui { + +class PlatformWindow; + +// When we're dragging tabs, we need to manually position our window. +class X11_WINDOW_EXPORT WindowMoveLoopClient : public views::X11MoveLoopDelegate { + public: + WindowMoveLoopClient(); + ~WindowMoveLoopClient() override; + + // Overridden from X11MoveLoopDelegate: + void OnMouseMovement(const gfx::Point& screen_point, + int flags, + base::TimeTicks event_time) override; + void OnMouseReleased() override; + void OnMoveLoopEnded() override; + + bool RunMoveLoop(PlatformWindow* window, const gfx::Vector2d& drag_offset); + void EndMoveLoop(); + + bool IsInMoveLoop(); + + private: + WholeScreenMoveLoop move_loop_; + + // We need to keep track of this so we can actually move it when reacting to + // mouse events. + PlatformWindow* window_; + + // Our cursor offset from the top left window origin when the drag + // started. Used to calculate the window's new bounds relative to the current + // location of the cursor. + gfx::Vector2d window_offset_; +}; + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_X11_WINDOW_MOVE_LOOP_CLIENT_H_ diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc index 51f5e8e17c380..1887b3b377a8a 100644 --- a/ui/platform_window/x11/x11_window_base.cc +++ b/ui/platform_window/x11/x11_window_base.cc @@ -203,7 +203,7 @@ void X11WindowBase::SetBounds(const gfx::Rect& bounds) { XWindowChanges changes = {0}; unsigned value_mask = 0; - if (bounds_.size() != bounds.size()) { + if (!bounds.size().IsEmpty() && bounds_.size() != bounds.size()) { changes.width = bounds.width(); changes.height = bounds.height(); value_mask |= CWHeight | CWWidth; @@ -223,8 +223,13 @@ void X11WindowBase::SetBounds(const gfx::Rect& bounds) { // case if we're running without a window manager. If there's a window // manager, it can modify or ignore the request, but (per ICCCM) we'll get a // (possibly synthetic) ConfigureNotify about the actual size and correct - // |bounds_| later. + // |bounds_| later. If |bounds| came with zero size, use the previous size + // of |bounds_|. + gfx::Size size = bounds_.size(); + if (!bounds.size().IsEmpty()) + size = bounds_.size(); bounds_ = bounds; + bounds_.set_size(size); // Even if the pixel bounds didn't change this call to the delegate should // still happen. The device scale factor may have changed which effectively @@ -343,6 +348,12 @@ gfx::Rect X11WindowBase::GetRestoredBoundsInPixels() const { return gfx::Rect(); } +bool X11WindowBase::RunMoveLoop(const gfx::Vector2d& drag_offset) { + return false; +} + +void X11WindowBase::StopMoveLoop() {} + void X11WindowBase::UnConfineCursor() { if (!has_pointer_barriers_) return; diff --git a/ui/platform_window/x11/x11_window_base.h b/ui/platform_window/x11/x11_window_base.h index 10f6a818c332d..83a0c668186b8 100644 --- a/ui/platform_window/x11/x11_window_base.h +++ b/ui/platform_window/x11/x11_window_base.h @@ -50,6 +50,8 @@ class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { PlatformImeController* GetPlatformImeController() override; void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override; gfx::Rect GetRestoredBoundsInPixels() const override; + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; + void StopMoveLoop() override; protected: // Creates new underlying XWindow. Does not map XWindow. @@ -58,6 +60,7 @@ class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { void Destroy(); PlatformWindowDelegate* delegate() { return delegate_; } + XDisplay* xdisplay() { return xdisplay_; } XID xwindow() const { return xwindow_; } diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index 50327795ad492..a5f292c997b39 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc @@ -374,14 +374,13 @@ Widget::MoveLoopResult DesktopWindowTreeHostPlatform::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { - // TODO: needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); + if (platform_window()->RunMoveLoop(drag_offset)) + return Widget::MOVE_LOOP_SUCCESSFUL; return Widget::MOVE_LOOP_CANCELED; } void DesktopWindowTreeHostPlatform::EndMoveLoop() { - // TODO: needs PlatformWindow support. - NOTIMPLEMENTED_LOG_ONCE(); + platform_window()->StopMoveLoop(); } void DesktopWindowTreeHostPlatform::SetVisibilityChangedAnimationsEnabled( diff --git a/ui/views/widget/desktop_aura/x11_move_loop_delegate.h b/ui/views/widget/desktop_aura/x11_move_loop_delegate.h index b5eee9b72572e..874468aa43783 100644 --- a/ui/views/widget/desktop_aura/x11_move_loop_delegate.h +++ b/ui/views/widget/desktop_aura/x11_move_loop_delegate.h @@ -14,6 +14,8 @@ namespace views { // Receives mouse events while the X11MoveLoop is tracking a drag. class X11MoveLoopDelegate { public: + virtual ~X11MoveLoopDelegate() {} + // Called when we receive a mouse move event. virtual void OnMouseMovement(const gfx::Point& screen_point, int flags, From 0d4605f3217813e42f045bf7b183c0c88cc8b82c Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Tue, 15 May 2018 13:49:49 +0300 Subject: [PATCH 10/17] [ozone/x11] Add initial display data fetching mechanism. This is a work-in-progress, which allows to fetch display data and unblock start of ozone/x11, which crashes now. Idieally, X11NativeDisplayDelegate should communicate with DesktopScreenX11 (to be copied and modified and renamed to X11DesktopScreenOzone). fixup! [ozone/x11] Add initial display data fetching mechanism. Compilation fixes. --- ui/ozone/platform/x11/BUILD.gn | 2 + ui/ozone/platform/x11/ozone_platform_x11.cc | 4 +- .../x11/x11_native_display_delegate.cc | 109 ++++++++++++++++++ .../x11/x11_native_display_delegate.h | 56 +++++++++ 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 ui/ozone/platform/x11/x11_native_display_delegate.cc create mode 100644 ui/ozone/platform/x11/x11_native_display_delegate.h diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn index 5de8d7166c17a..0319ce89575aa 100644 --- a/ui/ozone/platform/x11/BUILD.gn +++ b/ui/ozone/platform/x11/BUILD.gn @@ -30,6 +30,8 @@ source_set("x11") { "x11_window_manager_ozone.h", "x11_window_ozone.cc", "x11_window_ozone.h", + "x11_native_display_delegate.h", + "x11_native_display_delegate.cc", ] deps = [ diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc index 944206ff68f22..3b65fd4d020bb 100644 --- a/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -10,12 +10,12 @@ #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/x/x11_util.h" -#include "ui/display/manager/fake_display_delegate.h" #include "ui/events/devices/x11/touch_factory_x11.h" #include "ui/events/platform/x11/x11_event_source_libevent.h" #include "ui/events/system_input_injector.h" #include "ui/gfx/x/x11.h" #include "ui/ozone/common/stub_overlay_manager.h" +#include "ui/ozone/platform/x11/x11_native_display_delegate.h" #include "ui/ozone/platform/x11/x11_cursor_factory_ozone.h" #include "ui/ozone/platform/x11/x11_surface_factory.h" #include "ui/ozone/platform/x11/x11_window_manager_ozone.h" @@ -73,7 +73,7 @@ class OzonePlatformX11 : public OzonePlatform { std::unique_ptr CreateNativeDisplayDelegate() override { - return std::make_unique(); + return std::make_unique(); } void InitializeUI(const InitParams& params) override { diff --git a/ui/ozone/platform/x11/x11_native_display_delegate.cc b/ui/ozone/platform/x11/x11_native_display_delegate.cc new file mode 100644 index 0000000000000..58464ae289242 --- /dev/null +++ b/ui/ozone/platform/x11/x11_native_display_delegate.cc @@ -0,0 +1,109 @@ +// 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 "ui/ozone/platform/x11/x11_native_display_delegate.h" + +#include "ui/display/types/display_snapshot.h" +#include "ui/display/types/native_display_observer.h" +#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_types.h" + +namespace ui { + +X11NativeDisplayDelegate::X11NativeDisplayDelegate() = default; + +X11NativeDisplayDelegate::~X11NativeDisplayDelegate() = default; + +void X11NativeDisplayDelegate::Initialize() { + // This shouldn't be called twice. + DCHECK(!current_snapshot_); + + XDisplay* display = gfx::GetXDisplay(); + Screen* screen = DefaultScreenOfDisplay(display); + current_snapshot_.reset(new display::DisplaySnapshot( + XScreenNumberOfScreen(screen), gfx::Point(0, 0), + gfx::Size(WidthOfScreen(screen), HeightOfScreen(screen)), + display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false, + false, false, gfx::ColorSpace(), "", base::FilePath(), + display::DisplaySnapshot::DisplayModeList(), std::vector(), + nullptr, nullptr, 0, 0, gfx::Size())); + const int default_refresh = 60; + current_mode_.reset(new display::DisplayMode( + gfx::Size(WidthOfScreen(screen), HeightOfScreen(screen)), false, + default_refresh)); + current_snapshot_->set_current_mode(current_mode_.get()); + + for (display::NativeDisplayObserver& observer : observers_) + observer.OnConfigurationChanged(); +} + +void X11NativeDisplayDelegate::TakeDisplayControl( + display::DisplayControlCallback callback) { + NOTREACHED(); +} + +void X11NativeDisplayDelegate::RelinquishDisplayControl( + display::DisplayControlCallback callback) { + NOTREACHED(); +} + +void X11NativeDisplayDelegate::GetDisplays( + display::GetDisplaysCallback callback) { + // TODO(msisov): Add support for multiple displays and dynamic display data + // change. + std::vector snapshot; + snapshot.push_back(current_snapshot_.get()); + std::move(callback).Run(snapshot); +} + +void X11NativeDisplayDelegate::Configure(const display::DisplaySnapshot& output, + const display::DisplayMode* mode, + const gfx::Point& origin, + display::ConfigureCallback callback) { + NOTREACHED(); +} + +void X11NativeDisplayDelegate::GetHDCPState( + const display::DisplaySnapshot& output, + display::GetHDCPStateCallback callback) { + NOTREACHED(); +} + +void X11NativeDisplayDelegate::SetHDCPState( + const display::DisplaySnapshot& output, + display::HDCPState state, + display::SetHDCPStateCallback callback) { + NOTREACHED(); +} + +bool X11NativeDisplayDelegate::SetColorMatrix(int64_t display_id, + const std::vector& color_matrix) { + NOTREACHED(); + return false; +} + +bool X11NativeDisplayDelegate::SetGammaCorrection( + int64_t display_id, + const std::vector& degamma_lut, + const std::vector& gamma_lut) { + NOTREACHED(); + return false; +} + +void X11NativeDisplayDelegate::AddObserver( + display::NativeDisplayObserver* observer) { + observers_.AddObserver(observer); +} + +void X11NativeDisplayDelegate::RemoveObserver( + display::NativeDisplayObserver* observer) { + observers_.RemoveObserver(observer); +} + +display::FakeDisplayController* +X11NativeDisplayDelegate::GetFakeDisplayController() { + return nullptr; +} + +} // namespace ui diff --git a/ui/ozone/platform/x11/x11_native_display_delegate.h b/ui/ozone/platform/x11/x11_native_display_delegate.h new file mode 100644 index 0000000000000..b0cdda69ac2fd --- /dev/null +++ b/ui/ozone/platform/x11/x11_native_display_delegate.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef UI_OZONE_PLATFORM_X11_X11_NATIVE_DISPLAY_DELEGATE_H_ +#define UI_OZONE_PLATFORM_X11_X11_NATIVE_DISPLAY_DELEGATE_H_ + +#include "base/macros.h" +#include "base/observer_list.h" +#include "ui/display/types/native_display_delegate.h" +#include "ui/display/types/display_snapshot.h" + +namespace ui { + +class X11NativeDisplayDelegate : public display::NativeDisplayDelegate { + public: + X11NativeDisplayDelegate(); + ~X11NativeDisplayDelegate() override; + + // display::NativeDisplayDelegate overrides: + void Initialize() override; + void TakeDisplayControl(display::DisplayControlCallback callback) override; + void RelinquishDisplayControl( + display::DisplayControlCallback callback) override; + void GetDisplays(display::GetDisplaysCallback callback) override; + void Configure(const display::DisplaySnapshot& output, + const display::DisplayMode* mode, + const gfx::Point& origin, + display::ConfigureCallback callback) override; + void GetHDCPState(const display::DisplaySnapshot& output, + display::GetHDCPStateCallback callback) override; + void SetHDCPState(const display::DisplaySnapshot& output, + display::HDCPState state, + display::SetHDCPStateCallback callback) override; + bool SetColorMatrix(int64_t display_id, + const std::vector& color_matrix) override; + bool SetGammaCorrection( + int64_t display_id, + const std::vector& degamma_lut, + const std::vector& gamma_lut) override; + void AddObserver(display::NativeDisplayObserver* observer) override; + void RemoveObserver(display::NativeDisplayObserver* observer) override; + display::FakeDisplayController* GetFakeDisplayController() override; + + private: + std::unique_ptr current_snapshot_; + std::unique_ptr current_mode_; + + base::ObserverList observers_; + + DISALLOW_COPY_AND_ASSIGN(X11NativeDisplayDelegate); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_NATIVE_DISPLAY_DELEGATE_H_ From a78be027cbbda783ab7dd94f1124289453445e71 Mon Sep 17 00:00:00 2001 From: Julie Jeongeun Kim Date: Tue, 5 Jun 2018 15:44:19 +0900 Subject: [PATCH 11/17] Add X11DisplayManager to get display information X11NativeDisplayDelegate owns X11DisplayManager and it's registered as an X11DisplayManager's observer and it starts to communicate with it. When X11DisplayManager is created, it gathers display information and create DisplaySnapshot. DesktopScreenOzone gets updated display through DesktopScreenOzone::OnConfigurationChanged, X11NativeDisplayDelegate::GetDisplays and DesktopScreenOzone::OnHostDisplaysReady. fixup! Add X11DisplayManager to get display information This patch removed OwnedDisplaySnapshot because it doesn't need to own DisplayMode. DisplaySnapshot is created with DisplaySnapshot::DisplayModeList, which is the list has DisplayModes. Whenever DisplayMode is added to the list, it's cloned and kept in the list and every DisplayMode should be in the list. --- ui/base/x/x11_util.cc | 32 +++ ui/base/x/x11_util.h | 4 + ui/display/util/BUILD.gn | 3 +- ui/ozone/platform/x11/BUILD.gn | 7 +- .../platform/x11/x11_display_manager_ozone.cc | 233 ++++++++++++++++++ .../platform/x11/x11_display_manager_ozone.h | 73 ++++++ .../x11/x11_native_display_delegate.cc | 41 ++- .../x11/x11_native_display_delegate.h | 23 +- .../widget/desktop_aura/desktop_screen_x11.cc | 33 +-- 9 files changed, 384 insertions(+), 65 deletions(-) create mode 100644 ui/ozone/platform/x11/x11_display_manager_ozone.cc create mode 100644 ui/ozone/platform/x11/x11_display_manager_ozone.h diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc index 311b10e3d6737..7ece648fc8c13 100644 --- a/ui/base/x/x11_util.cc +++ b/ui/base/x/x11_util.cc @@ -19,6 +19,7 @@ #include #include "base/bind.h" +#include "base/command_line.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" @@ -55,6 +56,7 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/switches.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_error_tracker.h" @@ -1269,6 +1271,36 @@ bool WmSupportsHint(XAtom atom) { return base::ContainsValue(supported_atoms, atom); } +gfx::ICCProfile GetICCProfileForMonitor(int monitor) { + gfx::ICCProfile icc_profile; + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless)) + return icc_profile; + std::string atom_name; + if (monitor == 0) { + atom_name = "_ICC_PROFILE"; + } else { + atom_name = base::StringPrintf("_ICC_PROFILE_%d", monitor); + } + Atom property = gfx::GetAtom(atom_name.c_str()); + if (property != x11::None) { + Atom prop_type = x11::None; + int prop_format = 0; + unsigned long nitems = 0; + unsigned long nbytes = 0; + char* property_data = NULL; + if (XGetWindowProperty( + gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()), property, + 0, 0x1FFFFFFF /* MAXINT32 / 4 */, x11::False, AnyPropertyType, + &prop_type, &prop_format, &nitems, &nbytes, + reinterpret_cast(&property_data)) == + x11::Success) { + icc_profile = gfx::ICCProfile::FromData(property_data, nitems); + XFree(property_data); + } + } + return icc_profile; +} + XRefcountedMemory::XRefcountedMemory(unsigned char* x11_data, size_t length) : x11_data_(length ? x11_data : nullptr), length_(length) { } diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h index e7942b9809026..2405941e79f37 100644 --- a/ui/base/x/x11_util.h +++ b/ui/base/x/x11_util.h @@ -23,6 +23,7 @@ #include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/platform_event.h" +#include "ui/gfx/icc_profile.h" #include "ui/gfx/x/x11_types.h" typedef unsigned long XSharedMemoryId; // ShmSeg in the X headers. @@ -297,6 +298,9 @@ UI_BASE_X_EXPORT bool IsX11WindowFullScreen(XID window); // Returns true if the window manager supports the given hint. UI_BASE_X_EXPORT bool WmSupportsHint(XAtom atom); +// Return the ICCProfile corresponding to |monitor| using XGetWindowProperty. +UI_BASE_X_EXPORT gfx::ICCProfile GetICCProfileForMonitor(int monitor); + // Manages a piece of X11 allocated memory as a RefCountedMemory segment. This // object takes ownership over the passed in memory and will free it with the // X11 allocator when done. diff --git a/ui/display/util/BUILD.gn b/ui/display/util/BUILD.gn index ebf6b16c1c28c..72203f7fb4695 100644 --- a/ui/display/util/BUILD.gn +++ b/ui/display/util/BUILD.gn @@ -5,6 +5,7 @@ import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/libfuzzer/fuzzer_test.gni") +import("//ui/ozone/ozone.gni") jumbo_component("util") { output_name = "display_util" @@ -27,7 +28,7 @@ jumbo_component("util") { "//ui/gfx/geometry", ] - if (use_x11) { + if (use_x11 || ozone_platform_x11) { sources += [ "x11/edid_parser_x11.cc", "x11/edid_parser_x11.h", diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn index 0319ce89575aa..8748ffc5fc52a 100644 --- a/ui/ozone/platform/x11/BUILD.gn +++ b/ui/ozone/platform/x11/BUILD.gn @@ -32,6 +32,8 @@ source_set("x11") { "x11_window_ozone.h", "x11_native_display_delegate.h", "x11_native_display_delegate.cc", + "x11_display_manager_ozone.h", + "x11_display_manager_ozone.cc", ] deps = [ @@ -61,7 +63,10 @@ source_set("x11") { deps += [ "//gpu/vulkan/x" ] } - configs += [ "//build/config/linux:x11" ] + configs += [ + "//build/config/linux:x11", + "//build/config/linux:xrandr" + ] public_configs = [ "//third_party/khronos:khronos_headers" ] } diff --git a/ui/ozone/platform/x11/x11_display_manager_ozone.cc b/ui/ozone/platform/x11/x11_display_manager_ozone.cc new file mode 100644 index 0000000000000..de23d5bccb4a0 --- /dev/null +++ b/ui/ozone/platform/x11/x11_display_manager_ozone.cc @@ -0,0 +1,233 @@ +// Copyright (c) 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 "ui/ozone/platform/x11/x11_display_manager_ozone.h" + +#include "base/logging.h" +#include "base/memory/protected_memory_cfi.h" +#include "ui/base/x/x11_util.h" +#include "ui/display/display.h" +#include "ui/display/types/display_snapshot.h" +#include "ui/display/util/display_util.h" +#include "ui/display/util/x11/edid_parser_x11.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/gfx/x/x11_types.h" + +#include + +namespace ui { + +namespace { + +constexpr int default_refresh = 60; + +std::unique_ptr CreateSnapshot( + int64_t display_id, + gfx::Rect bounds, + gfx::ColorSpace color_space) { + display::DisplaySnapshot::DisplayModeList modes; + std::unique_ptr display_mode = + std::make_unique( + gfx::Size(bounds.width(), bounds.height()), false, default_refresh); + modes.push_back(std::move(display_mode)); + const display::DisplayMode* mode = modes.back().get(); + + return std::make_unique( + display_id, gfx::Point(bounds.x(), bounds.y()), + gfx::Size(bounds.width(), bounds.height()), + display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false, + false, false, false, color_space, "", base::FilePath(), std::move(modes), + std::vector(), mode, mode, 0, 0, gfx::Size()); +} + +std::vector> +BuildFallbackDisplayList() { + std::vector> snapshots; + ::XDisplay* display = gfx::GetXDisplay(); + ::Screen* screen = DefaultScreenOfDisplay(display); + int width = WidthOfScreen(screen); + int height = HeightOfScreen(screen); + int64_t display_id = 0; + gfx::Rect bounds(0, 0, width, height); + std::unique_ptr snapshot = + CreateSnapshot(display_id, bounds, gfx::ColorSpace()); + snapshots.push_back(std::move(snapshot)); + return snapshots; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// X11DisplayManagerOzone, public: + +X11DisplayManagerOzone::X11DisplayManagerOzone() + : xdisplay_(gfx::GetXDisplay()), + x_root_window_(DefaultRootWindow(xdisplay_)), + xrandr_version_(0), + xrandr_event_base_(0) { + // We only support 1.3+. There were library changes before this and we should + // use the new interface instead of the 1.2 one. + int randr_version_major = 0; + int randr_version_minor = 0; + if (XRRQueryVersion(xdisplay_, &randr_version_major, &randr_version_minor)) { + xrandr_version_ = randr_version_major * 100 + randr_version_minor; + } + // Need at least xrandr version 1.3. + if (xrandr_version_ < 103) { + snapshots_ = BuildFallbackDisplayList(); + return; + } + + int error_base_ignored = 0; + XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored); + + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); + XRRSelectInput(xdisplay_, x_root_window_, + RRScreenChangeNotifyMask | RROutputChangeNotifyMask | + RRCrtcChangeNotifyMask); + BuildDisplaysFromXRandRInfo(); + return; +} + +X11DisplayManagerOzone::~X11DisplayManagerOzone() { + if (xrandr_version_ >= 103 && ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); +} + +void X11DisplayManagerOzone::SetObserver(Observer* observer) { + observer_ = observer; + if (snapshots_.size() > 0) + observer_->OnOutputReadyForUse(); +} + +void X11DisplayManagerOzone::GetDisplaysSnapshot( + display::GetDisplaysCallback callback) { + std::vector snapshots; + for (const auto& snapshot : snapshots_) + snapshots.push_back(snapshot.get()); + std::move(callback).Run(snapshots); +} + +bool X11DisplayManagerOzone::CanDispatchEvent(const ui::PlatformEvent& event) { + // TODO(msisov, jkim): implement this. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +uint32_t X11DisplayManagerOzone::DispatchEvent(const ui::PlatformEvent& event) { + // TODO(msisov, jkim): implement this. + NOTIMPLEMENTED_LOG_ONCE(); + return ui::POST_DISPATCH_NONE; +} + +typedef XRRMonitorInfo* (*XRRGetMonitors)(::Display*, Window, bool, int*); +typedef void (*XRRFreeMonitors)(XRRMonitorInfo*); + +PROTECTED_MEMORY_SECTION base::ProtectedMemory + g_XRRGetMonitors_ptr; +PROTECTED_MEMORY_SECTION base::ProtectedMemory + g_XRRFreeMonitors_ptr; + +void X11DisplayManagerOzone::BuildDisplaysFromXRandRInfo() { + DCHECK(xrandr_version_ >= 103); + gfx::XScopedPtr< + XRRScreenResources, + gfx::XObjectDeleter> + resources(XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_)); + if (!resources) { + LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window."; + snapshots_ = BuildFallbackDisplayList(); + return; + } + + std::map output_to_monitor; + if (xrandr_version_ >= 105) { + void* xrandr_lib = dlopen(NULL, RTLD_NOW); + if (xrandr_lib) { + static base::ProtectedMemory::Initializer get_init( + &g_XRRGetMonitors_ptr, reinterpret_cast( + dlsym(xrandr_lib, "XRRGetMonitors"))); + static base::ProtectedMemory::Initializer free_init( + &g_XRRFreeMonitors_ptr, reinterpret_cast( + dlsym(xrandr_lib, "XRRFreeMonitors"))); + if (*g_XRRGetMonitors_ptr && *g_XRRFreeMonitors_ptr) { + int nmonitors = 0; + XRRMonitorInfo* monitors = base::UnsanitizedCfiCall( + g_XRRGetMonitors_ptr)(xdisplay_, x_root_window_, false, &nmonitors); + for (int monitor = 0; monitor < nmonitors; monitor++) { + for (int j = 0; j < monitors[monitor].noutput; j++) { + output_to_monitor[monitors[monitor].outputs[j]] = monitor; + } + } + base::UnsanitizedCfiCall(g_XRRFreeMonitors_ptr)(monitors); + } + } + } + + primary_display_index_ = 0; + RROutput primary_display_id = XRRGetOutputPrimary(xdisplay_, x_root_window_); + + int explicit_primary_display_index = -1; + int monitor_order_primary_display_index = -1; + + for (int i = 0; i < resources->noutput; ++i) { + RROutput output_id = resources->outputs[i]; + gfx::XScopedPtr> + output_info(XRRGetOutputInfo(xdisplay_, resources.get(), output_id)); + + bool is_connected = (output_info->connection == RR_Connected); + if (!is_connected) + continue; + + bool is_primary_display = output_id == primary_display_id; + + if (output_info->crtc) { + gfx::XScopedPtr> + crtc(XRRGetCrtcInfo(xdisplay_, resources.get(), output_info->crtc)); + + int64_t display_id = -1; + if (!display::EDIDParserX11(output_id).GetDisplayId( + static_cast(i), &display_id)) { + // It isn't ideal, but if we can't parse the EDID data, fallback on the + // display number. + display_id = i; + } + + gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height); + if (is_primary_display) + explicit_primary_display_index = display_id; + + auto monitor_iter = output_to_monitor.find(output_id); + if (monitor_iter != output_to_monitor.end() && monitor_iter->second == 0) + monitor_order_primary_display_index = display_id; + + gfx::ColorSpace color_space; + if (!display::Display::HasForceDisplayColorProfile()) { + gfx::ICCProfile icc_profile = ui::GetICCProfileForMonitor( + monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second); + icc_profile.HistogramDisplay(display_id); + color_space = icc_profile.GetColorSpace(); + } else { + color_space = display::Display::GetForcedDisplayColorProfile(); + } + + std::unique_ptr snapshot = + CreateSnapshot(display_id, crtc_bounds, color_space); + snapshots_.push_back(std::move(snapshot)); + } + } + + if (explicit_primary_display_index != -1) { + primary_display_index_ = explicit_primary_display_index; + } else if (monitor_order_primary_display_index != -1) { + primary_display_index_ = monitor_order_primary_display_index; + } +} + +} // namespace ui diff --git a/ui/ozone/platform/x11/x11_display_manager_ozone.h b/ui/ozone/platform/x11/x11_display_manager_ozone.h new file mode 100644 index 0000000000000..78319da483597 --- /dev/null +++ b/ui/ozone/platform/x11/x11_display_manager_ozone.h @@ -0,0 +1,73 @@ +// Copyright (c) 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. + +#ifndef UI_OZONE_PLATFORM_X11_X11_DISPLAY_MANAGER_OZONE_H_ +#define UI_OZONE_PLATFORM_X11_X11_DISPLAY_MANAGER_OZONE_H_ + +#include + +#include "ui/display/types/native_display_delegate.h" +#include "ui/events/platform/platform_event_dispatcher.h" + +typedef unsigned long XID; +typedef XID Window; +typedef struct _XDisplay Display; + +namespace display { +class DisplayMode; +class DisplaySnapshot; +} // namespace display + +namespace ui { + +// X11DisplayManagerOzone talks to xrandr. +class X11DisplayManagerOzone : public ui::PlatformEventDispatcher { + public: + class Observer { + public: + // Will be called when X11DisplayManagerOzone is available. + virtual void OnOutputReadyForUse() = 0; + }; + + X11DisplayManagerOzone(); + ~X11DisplayManagerOzone() override; + + void SetObserver(Observer* observer); + Observer* observer() { return observer_; } + + void GetDisplaysSnapshot(display::GetDisplaysCallback callback); + + // ui::PlatformEventDispatcher: + bool CanDispatchEvent(const ui::PlatformEvent& event) override; + uint32_t DispatchEvent(const ui::PlatformEvent& event) override; + + private: + // Builds a list of displays from the current screen information offered by + // the X server. + void BuildDisplaysFromXRandRInfo(); + + ::Display* xdisplay_; + ::Window x_root_window_; + + // XRandR version. MAJOR * 100 + MINOR. Zero if no xrandr is present. + int xrandr_version_; + + // The base of the event numbers used to represent XRandr events used in + // decoding events regarding output add/remove. + int xrandr_event_base_; + + // The display objects we present to chrome. + std::vector> snapshots_; + + // The index into displays_ that represents the primary display. + size_t primary_display_index_; + + Observer* observer_; + + DISALLOW_COPY_AND_ASSIGN(X11DisplayManagerOzone); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_DISPLAY_MANAGER_OZONE_H_ diff --git a/ui/ozone/platform/x11/x11_native_display_delegate.cc b/ui/ozone/platform/x11/x11_native_display_delegate.cc index 58464ae289242..2551b6ed3996e 100644 --- a/ui/ozone/platform/x11/x11_native_display_delegate.cc +++ b/ui/ozone/platform/x11/x11_native_display_delegate.cc @@ -16,26 +16,8 @@ X11NativeDisplayDelegate::X11NativeDisplayDelegate() = default; X11NativeDisplayDelegate::~X11NativeDisplayDelegate() = default; void X11NativeDisplayDelegate::Initialize() { - // This shouldn't be called twice. - DCHECK(!current_snapshot_); - - XDisplay* display = gfx::GetXDisplay(); - Screen* screen = DefaultScreenOfDisplay(display); - current_snapshot_.reset(new display::DisplaySnapshot( - XScreenNumberOfScreen(screen), gfx::Point(0, 0), - gfx::Size(WidthOfScreen(screen), HeightOfScreen(screen)), - display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false, - false, false, gfx::ColorSpace(), "", base::FilePath(), - display::DisplaySnapshot::DisplayModeList(), std::vector(), - nullptr, nullptr, 0, 0, gfx::Size())); - const int default_refresh = 60; - current_mode_.reset(new display::DisplayMode( - gfx::Size(WidthOfScreen(screen), HeightOfScreen(screen)), false, - default_refresh)); - current_snapshot_->set_current_mode(current_mode_.get()); - - for (display::NativeDisplayObserver& observer : observers_) - observer.OnConfigurationChanged(); + display_manager_ = std::make_unique(); + display_manager_->SetObserver(this); } void X11NativeDisplayDelegate::TakeDisplayControl( @@ -52,16 +34,20 @@ void X11NativeDisplayDelegate::GetDisplays( display::GetDisplaysCallback callback) { // TODO(msisov): Add support for multiple displays and dynamic display data // change. - std::vector snapshot; - snapshot.push_back(current_snapshot_.get()); - std::move(callback).Run(snapshot); + if (displays_ready_) + display_manager_->GetDisplaysSnapshot(std::move(callback)); } void X11NativeDisplayDelegate::Configure(const display::DisplaySnapshot& output, const display::DisplayMode* mode, const gfx::Point& origin, display::ConfigureCallback callback) { - NOTREACHED(); + NOTIMPLEMENTED(); + + // It should call |callback| after configuration. + // Even if we don't have implementation, it calls |callback| to finish the + // logic. otherwise, several tests from views_mus_unittests don't work. + std::move(callback).Run(true); } void X11NativeDisplayDelegate::GetHDCPState( @@ -106,4 +92,11 @@ X11NativeDisplayDelegate::GetFakeDisplayController() { return nullptr; } +void X11NativeDisplayDelegate::OnOutputReadyForUse() { + if (!displays_ready_) + displays_ready_ = true; + + for (display::NativeDisplayObserver& observer : observers_) + observer.OnConfigurationChanged(); +} } // namespace ui diff --git a/ui/ozone/platform/x11/x11_native_display_delegate.h b/ui/ozone/platform/x11/x11_native_display_delegate.h index b0cdda69ac2fd..8d9542225e477 100644 --- a/ui/ozone/platform/x11/x11_native_display_delegate.h +++ b/ui/ozone/platform/x11/x11_native_display_delegate.h @@ -5,14 +5,19 @@ #ifndef UI_OZONE_PLATFORM_X11_X11_NATIVE_DISPLAY_DELEGATE_H_ #define UI_OZONE_PLATFORM_X11_X11_NATIVE_DISPLAY_DELEGATE_H_ -#include "base/macros.h" #include "base/observer_list.h" #include "ui/display/types/native_display_delegate.h" -#include "ui/display/types/display_snapshot.h" +#include "ui/ozone/platform/x11/x11_display_manager_ozone.h" + +namespace display { +class DisplayMode; +class DisplaySnapshot; +} // namespace display namespace ui { -class X11NativeDisplayDelegate : public display::NativeDisplayDelegate { +class X11NativeDisplayDelegate : public display::NativeDisplayDelegate, + public X11DisplayManagerOzone::Observer { public: X11NativeDisplayDelegate(); ~X11NativeDisplayDelegate() override; @@ -42,11 +47,15 @@ class X11NativeDisplayDelegate : public display::NativeDisplayDelegate { void RemoveObserver(display::NativeDisplayObserver* observer) override; display::FakeDisplayController* GetFakeDisplayController() override; + // X11DisplayManagerOzone::Observer overrides: + void OnOutputReadyForUse() override; + private: - std::unique_ptr current_snapshot_; - std::unique_ptr current_mode_; - - base::ObserverList observers_; + bool displays_ready_ = false; + + std::unique_ptr display_manager_; + + base::ObserverList::Unchecked observers_; DISALLOW_COPY_AND_ASSIGN(X11NativeDisplayDelegate); }; diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc index 0951d2fb5b9ac..f230252d51ae8 100644 --- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -39,37 +39,6 @@ namespace { -// static -gfx::ICCProfile GetICCProfileForMonitor(int monitor) { - gfx::ICCProfile icc_profile; - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless)) - return icc_profile; - std::string atom_name; - if (monitor == 0) { - atom_name = "_ICC_PROFILE"; - } else { - atom_name = base::StringPrintf("_ICC_PROFILE_%d", monitor); - } - Atom property = gfx::GetAtom(atom_name.c_str()); - if (property != x11::None) { - Atom prop_type = x11::None; - int prop_format = 0; - unsigned long nitems = 0; - unsigned long nbytes = 0; - char* property_data = NULL; - if (XGetWindowProperty( - gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()), property, - 0, 0x1FFFFFFF /* MAXINT32 / 4 */, x11::False, AnyPropertyType, - &prop_type, &prop_format, &nitems, &nbytes, - reinterpret_cast(&property_data)) == - x11::Success) { - icc_profile = gfx::ICCProfile::FromData(property_data, nitems); - XFree(property_data); - } - } - return icc_profile; -} - double GetDeviceScaleFactor() { float device_scale_factor = 1.0f; if (views::LinuxUI::instance()) { @@ -449,7 +418,7 @@ std::vector DesktopScreenX11::BuildDisplaysFromXRandRInfo() { monitor_order_primary_display_index = displays.size(); if (!display::Display::HasForceDisplayColorProfile()) { - gfx::ICCProfile icc_profile = GetICCProfileForMonitor( + gfx::ICCProfile icc_profile = ui::GetICCProfileForMonitor( monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second); icc_profile.HistogramDisplay(display.id()); display.set_color_space(icc_profile.GetColorSpace()); From 1103f5500169ddf8a3630b064426418e2613168a Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Fri, 10 Aug 2018 09:26:12 +0300 Subject: [PATCH 12/17] [ozone/x11/headless] Use FakeInputMethodContextFactory to avoid crashing. That is, after we've added ime imeplementation for Wayland, other ozone platforms started to crash. To avoid this, use fake factory. fixup! [ozone/x11/headless] Use FakeInputMethodContextFactory to avoid crashing. Added condition before setting LinuxInputMethodContextFactory. fixup! [ozone/x11/headless] Use FakeInputMethodContextFactory to avoid crashing. It needs to skip DCHEK at InitializeInputMethodForTesting() as LinuxInputMethodContextFactory's already set at OzonePlatform::InitializeUI(). --- ui/base/ime/input_method_initializer.cc | 2 ++ .../ime/linux/fake_input_method_context_factory.cc | 4 +++- ui/base/ime/linux/fake_input_method_context_factory.h | 2 ++ ui/ozone/platform/headless/BUILD.gn | 1 + ui/ozone/platform/headless/ozone_platform_headless.cc | 9 +++++++++ ui/ozone/platform/x11/BUILD.gn | 11 ++++++----- ui/ozone/platform/x11/DEPS | 1 + ui/ozone/platform/x11/ozone_platform_x11.cc | 11 +++++++++-- 8 files changed, 33 insertions(+), 8 deletions(-) diff --git a/ui/base/ime/input_method_initializer.cc b/ui/base/ime/input_method_initializer.cc index b64f362a5d851..6dd0a9347de0d 100644 --- a/ui/base/ime/input_method_initializer.cc +++ b/ui/base/ime/input_method_initializer.cc @@ -50,11 +50,13 @@ void InitializeInputMethodForTesting() { if (!g_linux_input_method_context_factory_for_testing) g_linux_input_method_context_factory_for_testing = new FakeInputMethodContextFactory(); +#if !defined(USE_OZONE) const LinuxInputMethodContextFactory* factory = LinuxInputMethodContextFactory::instance(); CHECK(!factory || factory == g_linux_input_method_context_factory_for_testing) << "LinuxInputMethodContextFactory was already initialized somewhere " << "else."; +#endif LinuxInputMethodContextFactory::SetInstance( g_linux_input_method_context_factory_for_testing); #elif defined(OS_WIN) diff --git a/ui/base/ime/linux/fake_input_method_context_factory.cc b/ui/base/ime/linux/fake_input_method_context_factory.cc index f8d3102ee4863..aba2f76ed3047 100644 --- a/ui/base/ime/linux/fake_input_method_context_factory.cc +++ b/ui/base/ime/linux/fake_input_method_context_factory.cc @@ -8,7 +8,9 @@ namespace ui { -FakeInputMethodContextFactory::FakeInputMethodContextFactory() {} +FakeInputMethodContextFactory::FakeInputMethodContextFactory() = default; + +FakeInputMethodContextFactory::~FakeInputMethodContextFactory() = default; std::unique_ptr FakeInputMethodContextFactory::CreateInputMethodContext( diff --git a/ui/base/ime/linux/fake_input_method_context_factory.h b/ui/base/ime/linux/fake_input_method_context_factory.h index 12bb7661b1ea8..24f75149bc1e9 100644 --- a/ui/base/ime/linux/fake_input_method_context_factory.h +++ b/ui/base/ime/linux/fake_input_method_context_factory.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "ui/base/ime/linux/linux_input_method_context_factory.h" +#include "ui/base/ime/ui_base_ime_export.h" namespace ui { @@ -16,6 +17,7 @@ class UI_BASE_IME_LINUX_EXPORT FakeInputMethodContextFactory : public LinuxInputMethodContextFactory { public: FakeInputMethodContextFactory(); + ~FakeInputMethodContextFactory() override; // LinuxInputMethodContextFactory: std::unique_ptr CreateInputMethodContext( diff --git a/ui/ozone/platform/headless/BUILD.gn b/ui/ozone/platform/headless/BUILD.gn index 046dec9b9b3cf..8fa5fa97bf0db 100644 --- a/ui/ozone/platform/headless/BUILD.gn +++ b/ui/ozone/platform/headless/BUILD.gn @@ -26,6 +26,7 @@ source_set("headless") { "//base", "//skia", "//ui/base", + "//ui/base/ime", "//ui/display/manager", "//ui/events", "//ui/events/ozone:events_ozone_layout", diff --git a/ui/ozone/platform/headless/ozone_platform_headless.cc b/ui/ozone/platform/headless/ozone_platform_headless.cc index 3e22233d1ec60..2bed591e6bc88 100644 --- a/ui/ozone/platform/headless/ozone_platform_headless.cc +++ b/ui/ozone/platform/headless/ozone_platform_headless.cc @@ -8,6 +8,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" +#include "ui/base/ime/linux/fake_input_method_context_factory.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h" #include "ui/events/platform/platform_event_source.h" @@ -90,6 +91,13 @@ class OzonePlatformHeadless : public OzonePlatform { input_controller_ = CreateStubInputController(); cursor_factory_ozone_ = std::make_unique(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); + + if (LinuxInputMethodContextFactory::instance()) + return; + fake_input_method_factory_ = + std::make_unique(); + LinuxInputMethodContextFactory::SetInstance( + fake_input_method_factory_.get()); } void InitializeGPU(const InitParams& params) override { @@ -105,6 +113,7 @@ class OzonePlatformHeadless : public OzonePlatform { std::unique_ptr input_controller_; std::unique_ptr gpu_platform_support_host_; std::unique_ptr overlay_manager_; + std::unique_ptr fake_input_method_factory_; base::FilePath file_path_; DISALLOW_COPY_AND_ASSIGN(OzonePlatformHeadless); diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn index 8748ffc5fc52a..f0655fb8227c8 100644 --- a/ui/ozone/platform/x11/BUILD.gn +++ b/ui/ozone/platform/x11/BUILD.gn @@ -24,16 +24,16 @@ source_set("x11") { "x11_cursor_factory_ozone.h", "x11_cursor_ozone.cc", "x11_cursor_ozone.h", + "x11_display_manager_ozone.cc", + "x11_display_manager_ozone.h", + "x11_native_display_delegate.cc", + "x11_native_display_delegate.h", "x11_surface_factory.cc", "x11_surface_factory.h", "x11_window_manager_ozone.cc", "x11_window_manager_ozone.h", "x11_window_ozone.cc", "x11_window_ozone.h", - "x11_native_display_delegate.h", - "x11_native_display_delegate.cc", - "x11_display_manager_ozone.h", - "x11_display_manager_ozone.cc", ] deps = [ @@ -41,6 +41,7 @@ source_set("x11") { "//gpu/vulkan:buildflags", "//skia", "//ui/base", + "//ui/base/ime", "//ui/base/x", "//ui/display/manager", "//ui/events", @@ -65,7 +66,7 @@ source_set("x11") { configs += [ "//build/config/linux:x11", - "//build/config/linux:xrandr" + "//build/config/linux:xrandr", ] public_configs = [ "//third_party/khronos:khronos_headers" ] diff --git a/ui/ozone/platform/x11/DEPS b/ui/ozone/platform/x11/DEPS index 8d590992a5c1a..db1d9fe859da4 100644 --- a/ui/ozone/platform/x11/DEPS +++ b/ui/ozone/platform/x11/DEPS @@ -1,3 +1,4 @@ include_rules = [ "+ui/base/x", + "+ui/base/ime", ] diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc index 3b65fd4d020bb..1edd3483f864a 100644 --- a/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -9,14 +9,15 @@ #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/ime/linux/fake_input_method_context_factory.h" #include "ui/base/x/x11_util.h" #include "ui/events/devices/x11/touch_factory_x11.h" #include "ui/events/platform/x11/x11_event_source_libevent.h" #include "ui/events/system_input_injector.h" #include "ui/gfx/x/x11.h" #include "ui/ozone/common/stub_overlay_manager.h" -#include "ui/ozone/platform/x11/x11_native_display_delegate.h" #include "ui/ozone/platform/x11/x11_cursor_factory_ozone.h" +#include "ui/ozone/platform/x11/x11_native_display_delegate.h" #include "ui/ozone/platform/x11/x11_surface_factory.h" #include "ui/ozone/platform/x11/x11_window_manager_ozone.h" #include "ui/ozone/platform/x11/x11_window_ozone.h" @@ -84,8 +85,13 @@ class OzonePlatformX11 : public OzonePlatform { input_controller_ = CreateStubInputController(); cursor_factory_ozone_ = std::make_unique(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); - TouchFactory::SetTouchDeviceListFromCommandLine(); + + if (LinuxInputMethodContextFactory::instance()) + return; + fake_input_method_factory_ = + std::make_unique(); + LinuxInputMethodContextFactory::SetInstance(fake_input_method_factory_.get()); } void InitializeGPU(const InitParams& params) override { @@ -142,6 +148,7 @@ class OzonePlatformX11 : public OzonePlatform { std::unique_ptr input_controller_; std::unique_ptr cursor_factory_ozone_; std::unique_ptr gpu_platform_support_host_; + std::unique_ptr fake_input_method_factory_; // Objects in the GPU process. std::unique_ptr surface_factory_ozone_; From 61a388519183bc1794069d7120c004b2f1de8981 Mon Sep 17 00:00:00 2001 From: Julie Jeongeun Kim Date: Wed, 8 Aug 2018 21:51:02 +0900 Subject: [PATCH 13/17] Implements Drag and Drop for Ozone/X11 It introduces X11DragSource and X11DragContext to handle DnD on Ozone/X11. They are from DesktopDragDropClientAuraX11 which includes implementation for platform layer. X11DragSource handles event and deliver the data at a server side and X11DragContext handles events and read the data at a client side. It splits OSExchangeDataProviderAuraX11 to OSExchangeDataProviderAuraX11 and OSExchangeDataProviderAuraX11Base to resue it. OSExchangeDataProviderAuraX11 keeps only platform event specific part. --- ui/base/BUILD.gn | 47 +- .../os_exchange_data_provider_aurax11.cc | 433 +--------------- .../os_exchange_data_provider_aurax11.h | 104 +--- .../os_exchange_data_provider_aurax11_base.cc | 464 ++++++++++++++++++ .../os_exchange_data_provider_aurax11_base.h | 134 +++++ ui/ozone/platform/x11/BUILD.gn | 6 + ui/ozone/platform/x11/x11_drag_context.cc | 266 ++++++++++ ui/ozone/platform/x11/x11_drag_context.h | 115 +++++ ui/ozone/platform/x11/x11_drag_source.cc | 458 +++++++++++++++++ ui/ozone/platform/x11/x11_drag_source.h | 157 ++++++ ui/ozone/platform/x11/x11_drag_util.cc | 108 ++++ ui/ozone/platform/x11/x11_drag_util.h | 107 ++++ ui/ozone/platform/x11/x11_window_ozone.cc | 170 ++++++- ui/ozone/platform/x11/x11_window_ozone.h | 37 ++ ui/platform_window/x11/x11_window_base.cc | 9 + ui/platform_window/x11/x11_window_base.h | 10 +- 16 files changed, 2076 insertions(+), 549 deletions(-) create mode 100644 ui/base/dragdrop/os_exchange_data_provider_aurax11_base.cc create mode 100644 ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h create mode 100644 ui/ozone/platform/x11/x11_drag_context.cc create mode 100644 ui/ozone/platform/x11/x11_drag_context.h create mode 100644 ui/ozone/platform/x11/x11_drag_source.cc create mode 100644 ui/ozone/platform/x11/x11_drag_source.h create mode 100644 ui/ozone/platform/x11/x11_drag_util.cc create mode 100644 ui/ozone/platform/x11/x11_drag_util.h diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn index 1e2170ec64487..acab081a69834 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn @@ -456,9 +456,12 @@ jumbo_component("base") { deps += [ "//ui/events" ] } + if (use_x11 || ozone_platform_x11) { + configs += [ "//build/config/linux:x11" ] + } + if (use_x11) { public_deps += [ "//ui/base/x" ] - configs += [ "//build/config/linux:x11" ] if (!is_chromeos) { sources += [ @@ -472,30 +475,44 @@ jumbo_component("base") { } } - if (use_x11 && use_aura) { + if ((use_x11 || ozone_platform_x11) && use_aura) { sources += [ - "cursor/cursor_loader_x11.cc", - "cursor/cursor_loader_x11.h", - "cursor/cursor_x11.cc", - "x/selection_owner.cc", - "x/selection_owner.h", - "x/selection_requestor.cc", - "x/selection_requestor.h", "x/selection_utils.cc", "x/selection_utils.h", - ] - deps += [ - "//ui/events/platform/x11", - "//ui/gfx/x", + "x/selection_owner.cc", + "x/selection_owner.h", ] if (!is_chromeos) { # These Aura X11 files aren't used on ChromeOS. sources += [ - "dragdrop/os_exchange_data_provider_aurax11.cc", - "dragdrop/os_exchange_data_provider_aurax11.h", + "dragdrop/os_exchange_data_provider_aurax11_base.cc", + "dragdrop/os_exchange_data_provider_aurax11_base.h", ] } + + if (use_x11 && use_aura) { + sources += [ + "cursor/cursor_loader_x11.cc", + "cursor/cursor_loader_x11.h", + "cursor/cursor_x11.cc", + "x/selection_requestor.cc", + "x/selection_requestor.h", + ] + + if (!is_chromeos) { + # These Aura X11 files aren't used on ChromeOS. + sources += [ + "dragdrop/os_exchange_data_provider_aurax11.cc", + "dragdrop/os_exchange_data_provider_aurax11.h", + ] + } + } + + deps += [ + "//ui/events/platform/x11", + "//ui/gfx/x", + ] } if (use_aura) { diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc b/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc index 6666737fd2da0..4543984f1210b 100644 --- a/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc +++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc @@ -7,14 +7,6 @@ #include #include "base/logging.h" -#include "base/memory/ref_counted_memory.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/filename_util.h" -#include "ui/base/clipboard/clipboard.h" -#include "ui/base/clipboard/scoped_clipboard_writer.h" -#include "ui/base/dragdrop/file_info.h" #include "ui/base/x/selection_utils.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/x/x11_atom_cache.h" @@ -26,68 +18,19 @@ namespace ui { -namespace { - -const char kDndSelection[] = "XdndSelection"; -const char kRendererTaint[] = "chromium/x-renderer-taint"; - -const char kNetscapeURL[] = "_NETSCAPE_URL"; - -} // namespace - OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11( ::Window x_window, const SelectionFormatMap& selection) - : x_display_(gfx::GetXDisplay()), - x_root_window_(DefaultRootWindow(x_display_)), - own_window_(false), - x_window_(x_window), - format_map_(selection), - selection_owner_(x_display_, x_window_, gfx::GetAtom(kDndSelection)) {} + : OSExchangeDataProviderAuraX11Base(x_window, selection) {} OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11() - : x_display_(gfx::GetXDisplay()), - x_root_window_(DefaultRootWindow(x_display_)), - own_window_(true), - x_window_(XCreateWindow(x_display_, - x_root_window_, - -100, - -100, - 10, - 10, // x, y, width, height - 0, // border width - CopyFromParent, // depth - InputOnly, - CopyFromParent, // visual - 0, - NULL)), - format_map_(), - selection_owner_(x_display_, x_window_, gfx::GetAtom(kDndSelection)) { - XStoreName(x_display_, x_window_, "Chromium Drag & Drop Window"); - + : OSExchangeDataProviderAuraX11Base() { PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); } OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() { - if (own_window_) { + if (own_window_) PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - XDestroyWindow(x_display_, x_window_); - } -} - -void OSExchangeDataProviderAuraX11::TakeOwnershipOfSelection() const { - selection_owner_.TakeOwnershipOfSelection(format_map_); -} - -void OSExchangeDataProviderAuraX11::RetrieveTargets( - std::vector* targets) const { - selection_owner_.RetrieveTargets(targets); -} - -SelectionFormatMap OSExchangeDataProviderAuraX11::GetFormatMap() const { - // We return the |selection_owner_|'s format map instead of our own in case - // ours has been modified since TakeOwnershipOfSelection() was called. - return selection_owner_.selection_format_map(); } std::unique_ptr @@ -98,305 +41,6 @@ OSExchangeDataProviderAuraX11::Clone() const { return std::move(ret); } -void OSExchangeDataProviderAuraX11::MarkOriginatedFromRenderer() { - std::string empty; - format_map_.Insert(gfx::GetAtom(kRendererTaint), - scoped_refptr( - base::RefCountedString::TakeString(&empty))); -} - -bool OSExchangeDataProviderAuraX11::DidOriginateFromRenderer() const { - return format_map_.find(gfx::GetAtom(kRendererTaint)) != format_map_.end(); -} - -void OSExchangeDataProviderAuraX11::SetString(const base::string16& text_data) { - if (HasString()) - return; - - std::string utf8 = base::UTF16ToUTF8(text_data); - scoped_refptr mem( - base::RefCountedString::TakeString(&utf8)); - - format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeText), mem); - format_map_.Insert(gfx::GetAtom(kText), mem); - format_map_.Insert(gfx::GetAtom(kString), mem); - format_map_.Insert(gfx::GetAtom(kUtf8String), mem); -} - -void OSExchangeDataProviderAuraX11::SetURL(const GURL& url, - const base::string16& title) { - // TODO(dcheng): The original GTK code tries very hard to avoid writing out an - // empty title. Is this necessary? - if (url.is_valid()) { - // Mozilla's URL format: (UTF16: URL, newline, title) - base::string16 spec = base::UTF8ToUTF16(url.spec()); - - std::vector data; - ui::AddString16ToVector(spec, &data); - ui::AddString16ToVector(base::ASCIIToUTF16("\n"), &data); - ui::AddString16ToVector(title, &data); - scoped_refptr mem( - base::RefCountedBytes::TakeVector(&data)); - - format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeMozillaURL), mem); - - // Set a string fallback as well. - SetString(spec); - - // Return early if this drag already contains file contents (this implies - // that file contents must be populated before URLs). Nautilus (and possibly - // other file managers) prefer _NETSCAPE_URL over the X Direct Save - // protocol, but we want to prioritize XDS in this case. - if (!file_contents_name_.empty()) - return; - - // Set _NETSCAPE_URL for file managers like Nautilus that use it as a hint - // to create a link to the URL. Setting text/uri-list doesn't work because - // Nautilus will fetch and copy the contents of the URL to the drop target - // instead of linking... - // Format is UTF8: URL + "\n" + title. - std::string netscape_url = url.spec(); - netscape_url += "\n"; - netscape_url += base::UTF16ToUTF8(title); - format_map_.Insert(gfx::GetAtom(kNetscapeURL), - scoped_refptr( - base::RefCountedString::TakeString(&netscape_url))); - } -} - -void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) { - std::vector data; - data.push_back(FileInfo(path, base::FilePath())); - SetFilenames(data); -} - -void OSExchangeDataProviderAuraX11::SetFilenames( - const std::vector& filenames) { - std::vector paths; - for (auto it = filenames.begin(); it != filenames.end(); ++it) { - std::string url_spec = net::FilePathToFileURL(it->path).spec(); - if (!url_spec.empty()) - paths.push_back(url_spec); - } - - std::string joined_data = base::JoinString(paths, "\n"); - scoped_refptr mem( - base::RefCountedString::TakeString(&joined_data)); - format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeURIList), mem); -} - -void OSExchangeDataProviderAuraX11::SetPickledData( - const Clipboard::FormatType& format, - const base::Pickle& pickle) { - const unsigned char* data = - reinterpret_cast(pickle.data()); - - std::vector bytes; - bytes.insert(bytes.end(), data, data + pickle.size()); - scoped_refptr mem( - base::RefCountedBytes::TakeVector(&bytes)); - - format_map_.Insert(gfx::GetAtom(format.ToString().c_str()), mem); -} - -bool OSExchangeDataProviderAuraX11::GetString(base::string16* result) const { - if (HasFile()) { - // Various Linux file managers both pass a list of file:// URIs and set the - // string representation to the URI. We explicitly don't want to return use - // this representation. - return false; - } - - std::vector<::Atom> text_atoms = ui::GetTextAtomsFrom(); - std::vector< ::Atom> requested_types; - GetAtomIntersection(text_atoms, GetTargets(), &requested_types); - - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - std::string text = data.GetText(); - *result = base::UTF8ToUTF16(text); - return true; - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::GetURLAndTitle( - OSExchangeData::FilenameToURLPolicy policy, - GURL* url, - base::string16* title) const { - std::vector<::Atom> url_atoms = ui::GetURLAtomsFrom(); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - // TODO(erg): Technically, both of these forms can accept multiple URLs, - // but that doesn't match the assumptions of the rest of the system which - // expect single types. - - if (data.GetType() == gfx::GetAtom(Clipboard::kMimeTypeMozillaURL)) { - // Mozilla URLs are (UTF16: URL, newline, title). - base::string16 unparsed; - data.AssignTo(&unparsed); - - std::vector tokens = base::SplitString( - unparsed, base::ASCIIToUTF16("\n"), - base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - if (tokens.size() > 0) { - if (tokens.size() > 1) - *title = tokens[1]; - else - *title = base::string16(); - - *url = GURL(tokens[0]); - return true; - } - } else if (data.GetType() == gfx::GetAtom(Clipboard::kMimeTypeURIList)) { - std::vector tokens = ui::ParseURIList(data); - for (std::vector::const_iterator it = tokens.begin(); - it != tokens.end(); ++it) { - GURL test_url(*it); - if (!test_url.SchemeIsFile() || - policy == OSExchangeData::CONVERT_FILENAMES) { - *url = test_url; - *title = base::string16(); - return true; - } - } - } - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::GetFilename(base::FilePath* path) const { - std::vector filenames; - if (GetFilenames(&filenames)) { - *path = filenames.front().path; - return true; - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::GetFilenames( - std::vector* filenames) const { - std::vector<::Atom> url_atoms = ui::GetURIListAtomsFrom(); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - filenames->clear(); - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - std::vector tokens = ui::ParseURIList(data); - for (std::vector::const_iterator it = tokens.begin(); - it != tokens.end(); ++it) { - GURL url(*it); - base::FilePath file_path; - if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) { - filenames->push_back(FileInfo(file_path, base::FilePath())); - } - } - } - - return !filenames->empty(); -} - -bool OSExchangeDataProviderAuraX11::GetPickledData( - const Clipboard::FormatType& format, - base::Pickle* pickle) const { - std::vector< ::Atom> requested_types; - requested_types.push_back(gfx::GetAtom(format.ToString().c_str())); - - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - // Note that the pickle object on the right hand side of the assignment - // only refers to the bytes in |data|. The assignment copies the data. - *pickle = base::Pickle(reinterpret_cast(data.GetData()), - static_cast(data.GetSize())); - return true; - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::HasString() const { - std::vector<::Atom> text_atoms = ui::GetTextAtomsFrom(); - std::vector< ::Atom> requested_types; - GetAtomIntersection(text_atoms, GetTargets(), &requested_types); - return !requested_types.empty() && !HasFile(); -} - -bool OSExchangeDataProviderAuraX11::HasURL( - OSExchangeData::FilenameToURLPolicy policy) const { - std::vector<::Atom> url_atoms = ui::GetURLAtomsFrom(); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - if (requested_types.empty()) - return false; - - // The Linux desktop doesn't differentiate between files and URLs like - // Windows does and stuffs all the data into one mime type. - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - if (data.GetType() == gfx::GetAtom(Clipboard::kMimeTypeMozillaURL)) { - // File managers shouldn't be using this type, so this is a URL. - return true; - } else if (data.GetType() == - gfx::GetAtom(ui::Clipboard::kMimeTypeURIList)) { - std::vector tokens = ui::ParseURIList(data); - for (std::vector::const_iterator it = tokens.begin(); - it != tokens.end(); ++it) { - if (!GURL(*it).SchemeIsFile() || - policy == OSExchangeData::CONVERT_FILENAMES) - return true; - } - - return false; - } - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::HasFile() const { - std::vector<::Atom> url_atoms = ui::GetURIListAtomsFrom(); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - if (requested_types.empty()) - return false; - - // To actually answer whether we have a file, we need to look through the - // contents of the kMimeTypeURIList type, and see if any of them are file:// - // URIs. - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - std::vector tokens = ui::ParseURIList(data); - for (std::vector::const_iterator it = tokens.begin(); - it != tokens.end(); ++it) { - GURL url(*it); - base::FilePath file_path; - if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) - return true; - } - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::HasCustomFormat( - const Clipboard::FormatType& format) const { - std::vector< ::Atom> url_atoms; - url_atoms.push_back(gfx::GetAtom(format.ToString().c_str())); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - return !requested_types.empty(); -} - void OSExchangeDataProviderAuraX11::SetFileContents( const base::FilePath& filename, const std::string& file_contents) { @@ -432,60 +76,6 @@ void OSExchangeDataProviderAuraX11::SetFileContents( base::RefCountedString::TakeString(&file_contents_copy))); } -void OSExchangeDataProviderAuraX11::SetHtml(const base::string16& html, - const GURL& base_url) { - std::vector bytes; - // Manually jam a UTF16 BOM into bytes because otherwise, other programs will - // assume UTF-8. - bytes.push_back(0xFF); - bytes.push_back(0xFE); - ui::AddString16ToVector(html, &bytes); - scoped_refptr mem( - base::RefCountedBytes::TakeVector(&bytes)); - - format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeHTML), mem); -} - -bool OSExchangeDataProviderAuraX11::GetHtml(base::string16* html, - GURL* base_url) const { - std::vector< ::Atom> url_atoms; - url_atoms.push_back(gfx::GetAtom(Clipboard::kMimeTypeHTML)); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - ui::SelectionData data(format_map_.GetFirstOf(requested_types)); - if (data.IsValid()) { - *html = data.GetHtml(); - *base_url = GURL(); - return true; - } - - return false; -} - -bool OSExchangeDataProviderAuraX11::HasHtml() const { - std::vector< ::Atom> url_atoms; - url_atoms.push_back(gfx::GetAtom(Clipboard::kMimeTypeHTML)); - std::vector< ::Atom> requested_types; - GetAtomIntersection(url_atoms, GetTargets(), &requested_types); - - return !requested_types.empty(); -} - -void OSExchangeDataProviderAuraX11::SetDragImage( - const gfx::ImageSkia& image, - const gfx::Vector2d& cursor_offset) { - drag_image_ = image; - drag_image_offset_ = cursor_offset; -} - -gfx::ImageSkia OSExchangeDataProviderAuraX11::GetDragImage() const { - return drag_image_; -} - -gfx::Vector2d OSExchangeDataProviderAuraX11::GetDragImageOffset() const { - return drag_image_offset_; -} bool OSExchangeDataProviderAuraX11::CanDispatchEvent( const PlatformEvent& event) { @@ -505,21 +95,4 @@ uint32_t OSExchangeDataProviderAuraX11::DispatchEvent( return ui::POST_DISPATCH_NONE; } -bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const { - base::string16 text; - if (GetString(&text)) { - GURL test_url(text); - if (test_url.is_valid()) { - *url = test_url; - return true; - } - } - - return false; -} - -std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const { - return format_map_.GetTypes(); -} - } // namespace ui diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11.h b/ui/base/dragdrop/os_exchange_data_provider_aurax11.h index f8e60b31ac610..472984b08eb1d 100644 --- a/ui/base/dragdrop/os_exchange_data_provider_aurax11.h +++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11.h @@ -5,30 +5,14 @@ #ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_AURAX11_H_ #define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_AURAX11_H_ -#include -#include - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/pickle.h" -#include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/base/x/selection_owner.h" -#include "ui/base/x/selection_requestor.h" -#include "ui/base/x/selection_utils.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h" #include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/gfx/geometry/vector2d.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/gfx/x/x11.h" -#include "url/gurl.h" namespace ui { -class Clipboard; -class OSExchangeDataProviderAuraX11Test; - // OSExchangeData::Provider implementation for aura on linux. class UI_BASE_EXPORT OSExchangeDataProviderAuraX11 - : public OSExchangeData::Provider, + : public OSExchangeDataProviderAuraX11Base, public PlatformEventDispatcher { public: // |x_window| is the window the cursor is over, and |selection| is the set of @@ -42,99 +26,15 @@ class UI_BASE_EXPORT OSExchangeDataProviderAuraX11 ~OSExchangeDataProviderAuraX11() override; - // After all the Set* methods have built up the data we're offering, call - // this to take ownership of the XdndSelection clipboard. - void TakeOwnershipOfSelection() const; - - // Retrieves a list of types we're offering. Noop if we haven't taken the - // selection. - void RetrieveTargets(std::vector* targets) const; - - // Makes a copy of the format map currently being offered. - SelectionFormatMap GetFormatMap() const; - - const base::FilePath& file_contents_name() const { - return file_contents_name_; - } - - // Overridden from OSExchangeData::Provider: std::unique_ptr Clone() const override; - void MarkOriginatedFromRenderer() override; - bool DidOriginateFromRenderer() const override; - void SetString(const base::string16& data) override; - void SetURL(const GURL& url, const base::string16& title) override; - void SetFilename(const base::FilePath& path) override; - void SetFilenames(const std::vector& filenames) override; - void SetPickledData(const Clipboard::FormatType& format, - const base::Pickle& pickle) override; - bool GetString(base::string16* data) const override; - bool GetURLAndTitle(OSExchangeData::FilenameToURLPolicy policy, - GURL* url, - base::string16* title) const override; - bool GetFilename(base::FilePath* path) const override; - bool GetFilenames(std::vector* filenames) const override; - bool GetPickledData(const Clipboard::FormatType& format, - base::Pickle* pickle) const override; - bool HasString() const override; - bool HasURL(OSExchangeData::FilenameToURLPolicy policy) const override; - bool HasFile() const override; - bool HasCustomFormat(const Clipboard::FormatType& format) const override; - void SetFileContents(const base::FilePath& filename, const std::string& file_contents) override; - void SetHtml(const base::string16& html, const GURL& base_url) override; - bool GetHtml(base::string16* html, GURL* base_url) const override; - bool HasHtml() const override; - void SetDragImage(const gfx::ImageSkia& image, - const gfx::Vector2d& cursor_offset) override; - gfx::ImageSkia GetDragImage() const override; - gfx::Vector2d GetDragImageOffset() const override; - // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; private: - friend class OSExchangeDataProviderAuraX11Test; - typedef std::map PickleData; - - // Returns true if |formats_| contains a string format and the string can be - // parsed as a URL. - bool GetPlainTextURL(GURL* url) const; - - // Returns the targets in |format_map_|. - std::vector< ::Atom> GetTargets() const; - - // Drag image and offset data. - gfx::ImageSkia drag_image_; - gfx::Vector2d drag_image_offset_; - - // Our X11 state. - Display* x_display_; - ::Window x_root_window_; - - // In X11, because the IPC parts of drag operations are implemented by - // XSelection, we require an x11 window to receive drag messages on. The - // OSExchangeDataProvider system is modeled on the Windows implementation, - // which does not require a window. We only sometimes have a valid window - // available (in the case of drag receiving). Other times, we need to create - // our own xwindow just to receive events on it. - const bool own_window_; - - ::Window x_window_; - - // A representation of data. This is either passed to us from the other - // process, or built up through a sequence of Set*() calls. It can be passed - // to |selection_owner_| when we take the selection. - SelectionFormatMap format_map_; - - // Auxilary data for the X Direct Save protocol. - base::FilePath file_contents_name_; - - // Takes a snapshot of |format_map_| and offers it to other windows. - mutable SelectionOwner selection_owner_; - DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderAuraX11); }; diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11_base.cc b/ui/base/dragdrop/os_exchange_data_provider_aurax11_base.cc new file mode 100644 index 0000000000000..3e681632492fa --- /dev/null +++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11_base.cc @@ -0,0 +1,464 @@ +// Copyright (c) 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 "ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h" + +#include + +#include "base/logging.h" +#include "base/memory/ref_counted_memory.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "net/base/filename_util.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/base/clipboard/scoped_clipboard_writer.h" +#include "ui/base/dragdrop/file_info.h" +#include "ui/base/x/selection_utils.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/gfx/x/x11_atom_cache.h" + +// Note: the GetBlah() methods are used immediately by the +// web_contents_view_aura.cc:PrepareDropData(), while the omnibox is a +// little more discriminating and calls HasBlah() before trying to get the +// information. + +namespace ui { + +namespace { + +const char kDndSelection[] = "XdndSelection"; +const char kRendererTaint[] = "chromium/x-renderer-taint"; + +const char kNetscapeURL[] = "_NETSCAPE_URL"; + +} // namespace + +OSExchangeDataProviderAuraX11Base::OSExchangeDataProviderAuraX11Base( + ::Window x_window, + const SelectionFormatMap& selection) + : x_display_(gfx::GetXDisplay()), + x_root_window_(DefaultRootWindow(x_display_)), + own_window_(false), + x_window_(x_window), + format_map_(selection), + selection_owner_(x_display_, x_window_, gfx::GetAtom(kDndSelection)) {} + +OSExchangeDataProviderAuraX11Base::OSExchangeDataProviderAuraX11Base() + : x_display_(gfx::GetXDisplay()), + x_root_window_(DefaultRootWindow(x_display_)), + own_window_(true), + x_window_(XCreateWindow(x_display_, + x_root_window_, + -100, + -100, + 10, + 10, // x, y, width, height + 0, // border width + CopyFromParent, // depth + InputOnly, + CopyFromParent, // visual + 0, + NULL)), + format_map_(), + selection_owner_(x_display_, x_window_, gfx::GetAtom(kDndSelection)) { + XStoreName(x_display_, x_window_, "Chromium Drag & Drop Window"); +} + +OSExchangeDataProviderAuraX11Base::~OSExchangeDataProviderAuraX11Base() { + if (own_window_) + XDestroyWindow(x_display_, x_window_); +} + +void OSExchangeDataProviderAuraX11Base::TakeOwnershipOfSelection() const { + selection_owner_.TakeOwnershipOfSelection(format_map_); +} + +void OSExchangeDataProviderAuraX11Base::RetrieveTargets( + std::vector* targets) const { + selection_owner_.RetrieveTargets(targets); +} + +SelectionFormatMap OSExchangeDataProviderAuraX11Base::GetFormatMap() const { + // We return the |selection_owner_|'s format map instead of our own in case + // ours has been modified since TakeOwnershipOfSelection() was called. + return selection_owner_.selection_format_map(); +} + +void OSExchangeDataProviderAuraX11Base::MarkOriginatedFromRenderer() { + std::string empty; + format_map_.Insert(gfx::GetAtom(kRendererTaint), + scoped_refptr( + base::RefCountedString::TakeString(&empty))); +} + +bool OSExchangeDataProviderAuraX11Base::DidOriginateFromRenderer() const { + return format_map_.find(gfx::GetAtom(kRendererTaint)) != format_map_.end(); +} + +void OSExchangeDataProviderAuraX11Base::SetString( + const base::string16& text_data) { + if (HasString()) + return; + + std::string utf8 = base::UTF16ToUTF8(text_data); + scoped_refptr mem( + base::RefCountedString::TakeString(&utf8)); + + format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeText), mem); + format_map_.Insert(gfx::GetAtom(kText), mem); + format_map_.Insert(gfx::GetAtom(kString), mem); + format_map_.Insert(gfx::GetAtom(kUtf8String), mem); +} + +void OSExchangeDataProviderAuraX11Base::SetURL(const GURL& url, + const base::string16& title) { + // TODO(dcheng): The original GTK code tries very hard to avoid writing out an + // empty title. Is this necessary? + if (url.is_valid()) { + // Mozilla's URL format: (UTF16: URL, newline, title) + base::string16 spec = base::UTF8ToUTF16(url.spec()); + + std::vector data; + ui::AddString16ToVector(spec, &data); + ui::AddString16ToVector(base::ASCIIToUTF16("\n"), &data); + ui::AddString16ToVector(title, &data); + scoped_refptr mem( + base::RefCountedBytes::TakeVector(&data)); + + format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeMozillaURL), mem); + + // Set a string fallback as well. + SetString(spec); + + // Return early if this drag already contains file contents (this implies + // that file contents must be populated before URLs). Nautilus (and possibly + // other file managers) prefer _NETSCAPE_URL over the X Direct Save + // protocol, but we want to prioritize XDS in this case. + if (!file_contents_name_.empty()) + return; + + // Set _NETSCAPE_URL for file managers like Nautilus that use it as a hint + // to create a link to the URL. Setting text/uri-list doesn't work because + // Nautilus will fetch and copy the contents of the URL to the drop target + // instead of linking... + // Format is UTF8: URL + "\n" + title. + std::string netscape_url = url.spec(); + netscape_url += "\n"; + netscape_url += base::UTF16ToUTF8(title); + format_map_.Insert(gfx::GetAtom(kNetscapeURL), + scoped_refptr( + base::RefCountedString::TakeString(&netscape_url))); + } +} + +void OSExchangeDataProviderAuraX11Base::SetFilename( + const base::FilePath& path) { + std::vector data; + data.push_back(FileInfo(path, base::FilePath())); + SetFilenames(data); +} + +void OSExchangeDataProviderAuraX11Base::SetFilenames( + const std::vector& filenames) { + std::vector paths; + for (auto it = filenames.begin(); it != filenames.end(); ++it) { + std::string url_spec = net::FilePathToFileURL(it->path).spec(); + if (!url_spec.empty()) + paths.push_back(url_spec); + } + + std::string joined_data = base::JoinString(paths, "\n"); + scoped_refptr mem( + base::RefCountedString::TakeString(&joined_data)); + format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeURIList), mem); +} + +void OSExchangeDataProviderAuraX11Base::SetPickledData( + const Clipboard::FormatType& format, + const base::Pickle& pickle) { + const unsigned char* data = + reinterpret_cast(pickle.data()); + + std::vector bytes; + bytes.insert(bytes.end(), data, data + pickle.size()); + scoped_refptr mem( + base::RefCountedBytes::TakeVector(&bytes)); + + format_map_.Insert(gfx::GetAtom(format.ToString().c_str()), mem); +} + +bool OSExchangeDataProviderAuraX11Base::GetString( + base::string16* result) const { + if (HasFile()) { + // Various Linux file managers both pass a list of file:// URIs and set the + // string representation to the URI. We explicitly don't want to return use + // this representation. + return false; + } + + std::vector<::Atom> text_atoms = ui::GetTextAtomsFrom(); + std::vector<::Atom> requested_types; + GetAtomIntersection(text_atoms, GetTargets(), &requested_types); + + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + std::string text = data.GetText(); + *result = base::UTF8ToUTF16(text); + return true; + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::GetURLAndTitle( + OSExchangeData::FilenameToURLPolicy policy, + GURL* url, + base::string16* title) const { + std::vector<::Atom> url_atoms = ui::GetURLAtomsFrom(); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + // TODO(erg): Technically, both of these forms can accept multiple URLs, + // but that doesn't match the assumptions of the rest of the system which + // expect single types. + + if (data.GetType() == gfx::GetAtom(Clipboard::kMimeTypeMozillaURL)) { + // Mozilla URLs are (UTF16: URL, newline, title). + base::string16 unparsed; + data.AssignTo(&unparsed); + + std::vector tokens = + base::SplitString(unparsed, base::ASCIIToUTF16("\n"), + base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + if (tokens.size() > 0) { + if (tokens.size() > 1) + *title = tokens[1]; + else + *title = base::string16(); + + *url = GURL(tokens[0]); + return true; + } + } else if (data.GetType() == gfx::GetAtom(Clipboard::kMimeTypeURIList)) { + std::vector tokens = ui::ParseURIList(data); + for (std::vector::const_iterator it = tokens.begin(); + it != tokens.end(); ++it) { + GURL test_url(*it); + if (!test_url.SchemeIsFile() || + policy == OSExchangeData::CONVERT_FILENAMES) { + *url = test_url; + *title = base::string16(); + return true; + } + } + } + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::GetFilename( + base::FilePath* path) const { + std::vector filenames; + if (GetFilenames(&filenames)) { + *path = filenames.front().path; + return true; + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::GetFilenames( + std::vector* filenames) const { + std::vector<::Atom> url_atoms = ui::GetURIListAtomsFrom(); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + filenames->clear(); + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + std::vector tokens = ui::ParseURIList(data); + for (std::vector::const_iterator it = tokens.begin(); + it != tokens.end(); ++it) { + GURL url(*it); + base::FilePath file_path; + if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) { + filenames->push_back(FileInfo(file_path, base::FilePath())); + } + } + } + + return !filenames->empty(); +} + +bool OSExchangeDataProviderAuraX11Base::GetPickledData( + const Clipboard::FormatType& format, + base::Pickle* pickle) const { + std::vector<::Atom> requested_types; + requested_types.push_back(gfx::GetAtom(format.ToString().c_str())); + + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + // Note that the pickle object on the right hand side of the assignment + // only refers to the bytes in |data|. The assignment copies the data. + *pickle = base::Pickle(reinterpret_cast(data.GetData()), + static_cast(data.GetSize())); + return true; + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::HasString() const { + std::vector<::Atom> text_atoms = ui::GetTextAtomsFrom(); + std::vector<::Atom> requested_types; + GetAtomIntersection(text_atoms, GetTargets(), &requested_types); + return !requested_types.empty() && !HasFile(); +} + +bool OSExchangeDataProviderAuraX11Base::HasURL( + OSExchangeData::FilenameToURLPolicy policy) const { + std::vector<::Atom> url_atoms = ui::GetURLAtomsFrom(); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + if (requested_types.empty()) + return false; + + // The Linux desktop doesn't differentiate between files and URLs like + // Windows does and stuffs all the data into one mime type. + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + if (data.GetType() == gfx::GetAtom(Clipboard::kMimeTypeMozillaURL)) { + // File managers shouldn't be using this type, so this is a URL. + return true; + } else if (data.GetType() == + gfx::GetAtom(ui::Clipboard::kMimeTypeURIList)) { + std::vector tokens = ui::ParseURIList(data); + for (std::vector::const_iterator it = tokens.begin(); + it != tokens.end(); ++it) { + if (!GURL(*it).SchemeIsFile() || + policy == OSExchangeData::CONVERT_FILENAMES) + return true; + } + + return false; + } + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::HasFile() const { + std::vector<::Atom> url_atoms = ui::GetURIListAtomsFrom(); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + if (requested_types.empty()) + return false; + + // To actually answer whether we have a file, we need to look through the + // contents of the kMimeTypeURIList type, and see if any of them are file:// + // URIs. + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + std::vector tokens = ui::ParseURIList(data); + for (std::vector::const_iterator it = tokens.begin(); + it != tokens.end(); ++it) { + GURL url(*it); + base::FilePath file_path; + if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) + return true; + } + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::HasCustomFormat( + const Clipboard::FormatType& format) const { + std::vector<::Atom> url_atoms; + url_atoms.push_back(gfx::GetAtom(format.ToString().c_str())); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + return !requested_types.empty(); +} + +void OSExchangeDataProviderAuraX11Base::SetHtml(const base::string16& html, + const GURL& base_url) { + std::vector bytes; + // Manually jam a UTF16 BOM into bytes because otherwise, other programs will + // assume UTF-8. + bytes.push_back(0xFF); + bytes.push_back(0xFE); + ui::AddString16ToVector(html, &bytes); + scoped_refptr mem( + base::RefCountedBytes::TakeVector(&bytes)); + + format_map_.Insert(gfx::GetAtom(Clipboard::kMimeTypeHTML), mem); +} + +bool OSExchangeDataProviderAuraX11Base::GetHtml(base::string16* html, + GURL* base_url) const { + std::vector<::Atom> url_atoms; + url_atoms.push_back(gfx::GetAtom(Clipboard::kMimeTypeHTML)); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + ui::SelectionData data(format_map_.GetFirstOf(requested_types)); + if (data.IsValid()) { + *html = data.GetHtml(); + *base_url = GURL(); + return true; + } + + return false; +} + +bool OSExchangeDataProviderAuraX11Base::HasHtml() const { + std::vector<::Atom> url_atoms; + url_atoms.push_back(gfx::GetAtom(Clipboard::kMimeTypeHTML)); + std::vector<::Atom> requested_types; + GetAtomIntersection(url_atoms, GetTargets(), &requested_types); + + return !requested_types.empty(); +} + +void OSExchangeDataProviderAuraX11Base::SetDragImage( + const gfx::ImageSkia& image, + const gfx::Vector2d& cursor_offset) { + drag_image_ = image; + drag_image_offset_ = cursor_offset; +} + +gfx::ImageSkia OSExchangeDataProviderAuraX11Base::GetDragImage() const { + return drag_image_; +} + +gfx::Vector2d OSExchangeDataProviderAuraX11Base::GetDragImageOffset() const { + return drag_image_offset_; +} + +bool OSExchangeDataProviderAuraX11Base::GetPlainTextURL(GURL* url) const { + base::string16 text; + if (GetString(&text)) { + GURL test_url(text); + if (test_url.is_valid()) { + *url = test_url; + return true; + } + } + + return false; +} + +std::vector<::Atom> OSExchangeDataProviderAuraX11Base::GetTargets() const { + return format_map_.GetTypes(); +} + +} // namespace ui diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h b/ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h new file mode 100644 index 0000000000000..16ff12fd84faa --- /dev/null +++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h @@ -0,0 +1,134 @@ +// Copyright (c) 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. + +#ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_AURAX11_BASE_H_ +#define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_AURAX11_BASE_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/pickle.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/x/selection_owner.h" +#include "ui/base/x/selection_requestor.h" +#include "ui/base/x/selection_utils.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/x/x11.h" +#include "url/gurl.h" + +namespace ui { + +class Clipboard; +class OSExchangeDataProviderAuraX11Test; + +// OSExchangeData::Provider implementation for aura on linux. +class UI_BASE_EXPORT OSExchangeDataProviderAuraX11Base + : public OSExchangeData::Provider { + public: + // |x_window| is the window the cursor is over, and |selection| is the set of + // data being offered. + OSExchangeDataProviderAuraX11Base(::Window x_window, + const SelectionFormatMap& selection); + + // Creates a Provider for sending drag information. This creates its own, + // hidden X11 window to own send data. + OSExchangeDataProviderAuraX11Base(); + + ~OSExchangeDataProviderAuraX11Base() override; + + // After all the Set* methods have built up the data we're offering, call + // this to take ownership of the XdndSelection clipboard. + void TakeOwnershipOfSelection() const; + + // Retrieves a list of types we're offering. Noop if we haven't taken the + // selection. + void RetrieveTargets(std::vector* targets) const; + + // Makes a copy of the format map currently being offered. + SelectionFormatMap GetFormatMap() const; + + const base::FilePath& file_contents_name() const { + return file_contents_name_; + } + + // Overridden from OSExchangeData::Provider: + void MarkOriginatedFromRenderer() override; + bool DidOriginateFromRenderer() const override; + void SetString(const base::string16& data) override; + void SetURL(const GURL& url, const base::string16& title) override; + void SetFilename(const base::FilePath& path) override; + void SetFilenames(const std::vector& filenames) override; + void SetPickledData(const Clipboard::FormatType& format, + const base::Pickle& pickle) override; + bool GetString(base::string16* data) const override; + bool GetURLAndTitle(OSExchangeData::FilenameToURLPolicy policy, + GURL* url, + base::string16* title) const override; + bool GetFilename(base::FilePath* path) const override; + bool GetFilenames(std::vector* filenames) const override; + bool GetPickledData(const Clipboard::FormatType& format, + base::Pickle* pickle) const override; + bool HasString() const override; + bool HasURL(OSExchangeData::FilenameToURLPolicy policy) const override; + bool HasFile() const override; + bool HasCustomFormat(const Clipboard::FormatType& format) const override; + + void SetHtml(const base::string16& html, const GURL& base_url) override; + bool GetHtml(base::string16* html, GURL* base_url) const override; + bool HasHtml() const override; + void SetDragImage(const gfx::ImageSkia& image, + const gfx::Vector2d& cursor_offset) override; + gfx::ImageSkia GetDragImage() const override; + gfx::Vector2d GetDragImageOffset() const override; + + protected: + friend class OSExchangeDataProviderAuraX11Test; + typedef std::map PickleData; + + // Returns true if |formats_| contains a string format and the string can be + // parsed as a URL. + bool GetPlainTextURL(GURL* url) const; + + // Returns the targets in |format_map_|. + std::vector<::Atom> GetTargets() const; + + // Drag image and offset data. + gfx::ImageSkia drag_image_; + gfx::Vector2d drag_image_offset_; + + // Our X11 state. + Display* x_display_; + ::Window x_root_window_; + + // In X11, because the IPC parts of drag operations are implemented by + // XSelection, we require an x11 window to receive drag messages on. The + // OSExchangeDataProvider system is modeled on the Windows implementation, + // which does not require a window. We only sometimes have a valid window + // available (in the case of drag receiving). Other times, we need to create + // our own xwindow just to receive events on it. + const bool own_window_; + + ::Window x_window_; + + // A representation of data. This is either passed to us from the other + // process, or built up through a sequence of Set*() calls. It can be passed + // to |selection_owner_| when we take the selection. + SelectionFormatMap format_map_; + + // Auxilary data for the X Direct Save protocol. + base::FilePath file_contents_name_; + + // Takes a snapshot of |format_map_| and offers it to other windows. + mutable SelectionOwner selection_owner_; + + DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderAuraX11Base); +}; + +} // namespace ui + +#endif // UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_AURAX11_BASE_H_ diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn index f0655fb8227c8..16cb495ed805c 100644 --- a/ui/ozone/platform/x11/BUILD.gn +++ b/ui/ozone/platform/x11/BUILD.gn @@ -28,6 +28,12 @@ source_set("x11") { "x11_display_manager_ozone.h", "x11_native_display_delegate.cc", "x11_native_display_delegate.h", + "x11_drag_context.h", + "x11_drag_context.cc", + "x11_drag_source.h", + "x11_drag_source.cc", + "x11_drag_util.h", + "x11_drag_util.cc", "x11_surface_factory.cc", "x11_surface_factory.h", "x11_window_manager_ozone.cc", diff --git a/ui/ozone/platform/x11/x11_drag_context.cc b/ui/ozone/platform/x11/x11_drag_context.cc new file mode 100644 index 0000000000000..f94cd861fa47c --- /dev/null +++ b/ui/ozone/platform/x11/x11_drag_context.cc @@ -0,0 +1,266 @@ +// Copyright (c) 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 "ui/ozone/platform/x11/x11_drag_context.h" + +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aurax11_base.h" +#include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_window_event_manager.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/ozone/platform/x11/x11_drag_util.h" +#include "ui/ozone/platform/x11/x11_window_ozone.h" + +namespace ui { + +namespace { + +constexpr int kWillAcceptDrop = 1; +constexpr int kWantFurtherPosEvents = 2; + +// Window property that will receive the drag and drop selection data. +const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER"; + +} // namespace + +class X11OSExchangeDataProvider : public OSExchangeDataProviderAuraX11Base { + public: + X11OSExchangeDataProvider(::Window x_window, + const SelectionFormatMap& selection) + : OSExchangeDataProviderAuraX11Base(x_window, selection) {} + + X11OSExchangeDataProvider() {} + + std::unique_ptr Clone() const override { + std::unique_ptr ret( + new X11OSExchangeDataProvider()); + ret->format_map_ = format_map_; + return std::move(ret); + } +}; + +X11DragContext::X11DragContext(X11WindowOzone* window, + XID local_window, + const XClientMessageEvent& event, + const SelectionFormatMap* map) + : window_(window), + local_window_(local_window), + source_window_(event.data.l[0]), + waiting_to_handle_position_(false), + suggested_action_(x11::None) { + if (!map) { + bool get_types_from_property = ((event.data.l[1] & 1) != 0); + + if (get_types_from_property) { + if (!ui::GetAtomArrayProperty(source_window_, kXdndTypeList, + &unfetched_targets_)) { + return; + } + } else { + // data.l[2,3,4] contain the first three types. Unused slots can be None. + for (int i = 0; i < 3; ++i) { + if (event.data.l[2 + i] != x11::None) { + unfetched_targets_.push_back(event.data.l[2 + i]); + } + } + } + +#if DCHECK_IS_ON() + DVLOG(1) << "XdndEnter has " << unfetched_targets_.size() << " data types"; + for (::Atom target : unfetched_targets_) { + DVLOG(1) << "XdndEnter data type: " << target; + } +#endif // DCHECK_IS_ON() + + // The window doesn't have a DesktopDragDropClientAura, that means it's + // created by some other process. Listen for messages on it. + source_window_events_.reset( + new ui::XScopedEventSelector(source_window_, PropertyChangeMask)); + + // We must perform a full sync here because we could be racing + // |source_window_|. + XSync(gfx::GetXDisplay(), x11::False); + } else { + // This drag originates from an aura window within our process. This means + // that we can shortcut the X11 server and ask the owning SelectionOwner + // for the data it's offering. + fetched_targets_ = *map; + } + + ReadActions(); +} + +X11DragContext::~X11DragContext() = default; + +void X11DragContext::OnXdndPosition(const XClientMessageEvent& event) { + unsigned long source_window = event.data.l[0]; + int x_root_window = event.data.l[2] >> 16; + int y_root_window = event.data.l[2] & 0xffff; + ::Time time_stamp = event.data.l[3]; + ::Atom suggested_action = event.data.l[4]; + + OnXdndPositionMessage(suggested_action, source_window, time_stamp, + gfx::PointF(x_root_window, y_root_window)); +} + +void X11DragContext::OnXdndPositionMessage(::Atom suggested_action, + XID source_window, + ::Time time_stamp, + const gfx::PointF& screen_point) { + DCHECK_EQ(source_window_, source_window); + suggested_action_ = suggested_action; + + if (!unfetched_targets_.empty()) { + // We have unfetched targets. That means we need to pause the handling of + // the position message and ask the other window for its data. + screen_point_ = screen_point; + position_time_stamp_ = time_stamp; + waiting_to_handle_position_ = true; + + fetched_targets_ = ui::SelectionFormatMap(); + RequestNextTarget(); + } else { + CompleteXdndPosition(source_window, screen_point); + } +} + +void X11DragContext::RequestNextTarget() { + DCHECK(!unfetched_targets_.empty()); + DCHECK(waiting_to_handle_position_); + + ::Atom target = unfetched_targets_.back(); + unfetched_targets_.pop_back(); + + XConvertSelection(gfx::GetXDisplay(), gfx::GetAtom(kXdndSelection), target, + gfx::GetAtom(kChromiumDragReciever), local_window_, + position_time_stamp_); +} + +void X11DragContext::OnSelectionNotify(const XSelectionEvent& event) { + if (!waiting_to_handle_position_) { + // A misbehaved window may send SelectionNotify without us requesting data + // via XConvertSelection(). + return; + } + + DVLOG(1) << "SelectionNotify, format " << event.target; + + if (event.property != x11::None) { + DCHECK_EQ(event.property, gfx::GetAtom(kChromiumDragReciever)); + + scoped_refptr data; + ::Atom type = x11::None; + if (ui::GetRawBytesOfProperty(local_window_, event.property, &data, NULL, + &type)) { + fetched_targets_.Insert(event.target, data); + } + } else { + // The source failed to convert the drop data to the format (target in X11 + // parlance) that we asked for. This happens, even though we only ask for + // the formats advertised by the source. http://crbug.com/628099 + DVLOG(1) << "XConvertSelection failed for source-advertised target " + << event.target; + } + + if (!unfetched_targets_.empty()) { + RequestNextTarget(); + } else { + waiting_to_handle_position_ = false; + CompleteXdndPosition(source_window_, screen_point_); + } +} + +void X11DragContext::ReadActions() { + std::vector<::Atom> atom_array; + if (!ui::GetAtomArrayProperty(source_window_, kXdndActionList, &atom_array)) { + actions_.clear(); + } else { + actions_.swap(atom_array); + } +} + +int X11DragContext::GetDragOperation() const { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + for (std::vector<::Atom>::const_iterator it = actions_.begin(); + it != actions_.end(); ++it) { + MaskOperation(*it, &drag_operation); + } + + MaskOperation(suggested_action_, &drag_operation); + + return drag_operation; +} + +void X11DragContext::MaskOperation(::Atom xdnd_operation, + int* drag_operation) const { + if (xdnd_operation == gfx::GetAtom(kXdndActionCopy)) + *drag_operation |= ui::DragDropTypes::DRAG_COPY; + else if (xdnd_operation == gfx::GetAtom(kXdndActionMove)) + *drag_operation |= ui::DragDropTypes::DRAG_MOVE; + else if (xdnd_operation == gfx::GetAtom(kXdndActionLink)) + *drag_operation |= ui::DragDropTypes::DRAG_LINK; +} + +void X11DragContext::CompleteXdndPosition(XID source_window, + const gfx::PointF& screen_point) { + // int drag_operation = ui::DragDropTypes::DRAG_COPY; + std::unique_ptr data = std::make_unique( + std::make_unique(local_window_, + fetched_targets())); + int drag_operation = GetDragOperation(); + // KDE-based file browsers such as Dolphin change the drag operation depending + // on whether alt/ctrl/shift was pressed. However once Chromium gets control + // over the X11 events, the source application does no longer receive X11 + // events for key modifier changes, so the dnd operation gets stuck in an + // incorrect state. Blink can only dnd-open files of type DRAG_COPY, so the + // DRAG_COPY mask is added if the dnd object is a file. + if (drag_operation & + (ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_LINK) && + data->HasFile()) { + drag_operation |= ui::DragDropTypes::DRAG_COPY; + } + + if (!sent_entered_) { + window_->OnDragDataCollected(screen_point, std::move(data), drag_operation); + sent_entered_ = true; + } + window_->OnDragMotion(screen_point, 0, position_time_stamp_, drag_operation); + + // Sends an XdndStatus message back to the source_window. l[2,3] + // theoretically represent an area in the window where the current action is + // the same as what we're returning, but I can't find any implementation that + // actually making use of this. A client can return (0, 0) and/or set the + // first bit of l[1] to disable the feature, and it appears that gtk neither + // sets this nor respects it if set. + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gfx::GetAtom(kXdndStatus); + xev.xclient.format = 32; + xev.xclient.window = source_window; + xev.xclient.data.l[0] = local_window_; + xev.xclient.data.l[1] = + (drag_operation != 0) ? (kWantFurtherPosEvents | kWillAcceptDrop) : 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = DragOperationToAtom(drag_operation); + + ui::SendXClientEvent(source_window, &xev); +} + +void X11DragContext::OnXdndDrop(int drag_operation) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gfx::GetAtom(kXdndFinished); + xev.xclient.format = 32; + xev.xclient.window = source_window(); + xev.xclient.data.l[0] = local_window_; + xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0; + xev.xclient.data.l[2] = DragOperationToAtom(drag_operation); + + ui::SendXClientEvent(source_window(), &xev); +} + +} // namespace ui diff --git a/ui/ozone/platform/x11/x11_drag_context.h b/ui/ozone/platform/x11/x11_drag_context.h new file mode 100644 index 0000000000000..191c1beae0ae4 --- /dev/null +++ b/ui/ozone/platform/x11/x11_drag_context.h @@ -0,0 +1,115 @@ +// Copyright (c) 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. + +#ifndef UI_OZONE_PLATFORM_X11_X11_DRAG_CONTEXT_H_ +#define UI_OZONE_PLATFORM_X11_X11_DRAG_CONTEXT_H_ + +#include "ui/base/x/selection_utils.h" +#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/gfx/geometry/point_f.h" + +namespace ui { + +class XScopedEventSelector; +class X11WindowOzone; + +class X11DragContext { + public: + X11DragContext(X11WindowOzone* window, + XID local_window, + const XClientMessageEvent& event, + const SelectionFormatMap* map); + ~X11DragContext(); + + void OnXdndPosition(const XClientMessageEvent& event); + // When we receive an XdndPosition message, we need to have all the data + // copied from the other window before we process the XdndPosition + // message. If we have that data already, dispatch immediately. Otherwise, + // delay dispatching until we do. + void OnXdndPositionMessage(::Atom suggested_action, + XID source_window, + ::Time time_stamp, + const gfx::PointF& screen_point); + + // Called when XSelection data has been copied to our process. + void OnSelectionNotify(const XSelectionEvent& xselection); + + void OnXdndDrop(int drag_operation); + + // Clones the fetched targets. + const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; } + + // Reads the kXdndActionList property from |source_window| and copies it + // into |actions|. + void ReadActions(); + + // Creates a ui::DragDropTypes::DragOperation representation of the current + // action list. + int GetDragOperation() const; + + // views::DesktopDragDropClientOzone* source_client() { return source_client_; } + + XID source_window() { return source_window_; } + + private: + // Called to request the next target from the source window. This is only + // done on the first XdndPosition; after that, we cache the data offered by + // the source window. + void RequestNextTarget(); + + // Masks the X11 atom |xdnd_operation|'s views representation onto + // |drag_operation|. + void MaskOperation(::Atom xdnd_operation, int* drag_operation) const; + + void CompleteXdndPosition(::Window source_window, + const gfx::PointF& screen_point); + + void SendXClientEvent(XID xid, XEvent* xev); + + X11WindowOzone* window_; + + // The XID of our chrome local aura window handling our events. + XID local_window_; + + // The XID of the window that's initiated the drag. + XID source_window_; + + // Events that we have selected on |source_window_|. + std::unique_ptr source_window_events_; + + // Whether we're blocking the handling of an XdndPosition message by waiting + // for |unfetched_targets_| to be fetched. + bool waiting_to_handle_position_; + + // Where the cursor is on screen. + gfx::PointF screen_point_; + + // The time stamp of the last XdndPosition event we received. The XDND + // specification mandates that we use this time stamp when querying the source + // about the drag and drop data. + ::Time position_time_stamp_; + + // A SelectionFormatMap of data that we have in our process. + ui::SelectionFormatMap fetched_targets_; + + // The names of various data types offered by the other window that we + // haven't fetched and put in |fetched_targets_| yet. + std::vector<::Atom> unfetched_targets_; + + // XdndPosition messages have a suggested action. Qt applications exclusively + // use this, instead of the XdndActionList which is backed by |actions_|. + ::Atom suggested_action_; + + // Possible actions. + std::vector<::Atom> actions_; + + bool sent_entered_ = false; + + DISALLOW_COPY_AND_ASSIGN(X11DragContext); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_DRAG_CONTEXT_H_ diff --git a/ui/ozone/platform/x11/x11_drag_source.cc b/ui/ozone/platform/x11/x11_drag_source.cc new file mode 100644 index 0000000000000..c2c3d7eca545f --- /dev/null +++ b/ui/ozone/platform/x11/x11_drag_source.cc @@ -0,0 +1,458 @@ +// Copyright (c) 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 "ui/ozone/platform/x11/x11_drag_source.h" + +#include "base/threading/thread_task_runner_handle.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/x/x11_window_event_manager.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/platform/scoped_event_dispatcher.h" +#include "ui/ozone/platform/x11/x11_drag_util.h" +#include "ui/ozone/platform/x11/x11_window_ozone.h" + +namespace ui { + +namespace { + +// The time to wait since sending the last XdndPosition message before +// reprocessing the most recent mouse move event in case that the window +// stacking order has changed and |source_current_window_| needs to be updated. +const int kRepeatMouseMoveTimeoutMs = 350; + +// The time to wait for the target to respond after the user has released the +// mouse button before ending the move loop. +const int kEndMoveLoopTimeoutMs = 1000; + +XID ValidateXdndWindow(XID window) { + if (window == x11::None) + return x11::None; + + // TODO(crbug/651775): The proxy window should be reported separately from the + // target window. XDND messages should be sent to the proxy, and their + // window field should point to the target. + + // Figure out which window we should test as XdndAware. If |target| has + // XdndProxy, it will set that proxy on target, and if not, |target|'s + // original value will remain. + ui::GetXIDProperty(window, kXdndProxy, &window); + + int version; + if (ui::GetIntProperty(window, kXdndAware, &version) && + version >= kMaxXdndVersion) { + return window; + } + return x11::None; +} + +} // namespace + +X11DragSource::X11DragSource(X11WindowOzone* window, + XID xwindow, + int operation, + const ui::OSExchangeData& data) + : window_(window), + xwindow_(xwindow), + drag_operation_(operation), + negotiated_operation_(ui::DragDropTypes::DRAG_NONE), + source_state_(SOURCE_STATE_OTHER), + format_map_(), + selection_owner_(gfx::GetXDisplay(), + xwindow, + gfx::GetAtom(kXdndSelection)), + screen_point_(gfx::Point()), + weak_factory_(this) { + XStoreName(gfx::GetXDisplay(), xwindow, "Chromium Drag & Drop Window"); + CreateDragInputWindow(gfx::GetXDisplay()); + + base::string16 str; + const OSExchangeData::FilenameToURLPolicy policy = + OSExchangeData::FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES; + if (data.HasURL(policy)) { + GURL url; + base::string16 title; + data.GetURLAndTitle(policy, &url, &title); + InsertURLToSelectionFormatMap(url, title, &format_map_); + } + if (data.GetString(&str)) { + InsertStringToSelectionFormatMap(str, &format_map_); + } + selection_owner_.TakeOwnershipOfSelection(format_map_); + + old_dispatcher_ = std::move(nested_dispatcher_); + nested_dispatcher_ = + ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this); +} + +X11DragSource::~X11DragSource() { + window_ = nullptr; + last_motion_in_screen_.reset(); + + if (source_current_window_ != gfx::kNullAcceleratedWidget) { + SendXdndLeave(source_current_window_); + source_current_window_ = gfx::kNullAcceleratedWidget; + } + repeat_mouse_move_timer_.Stop(); + end_move_loop_timer_.Stop(); + + grab_input_window_events_.reset(); + XDestroyWindow(gfx::GetXDisplay(), grab_input_window_); + grab_input_window_ = gfx::kNullAcceleratedWidget; + + nested_dispatcher_ = std::move(old_dispatcher_); +} + +void X11DragSource::OnXdndStatus(const XClientMessageEvent& event) { + DVLOG(1) << "OnXdndStatus"; + + gfx::AcceleratedWidget source_window = event.data.l[0]; + + if (source_window != source_current_window_) + return; + + if (source_state_ != SOURCE_STATE_PENDING_DROP && + source_state_ != SOURCE_STATE_OTHER) { + return; + } + + waiting_on_status_ = false; + status_received_since_enter_ = true; + + if (event.data.l[1] & 1) { + ::Atom atom_operation = event.data.l[4]; + negotiated_operation_ = AtomToDragOperation(atom_operation); + } else { + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; + } + + if (source_state_ == SOURCE_STATE_PENDING_DROP) { + // We were waiting on the status message so we could send the XdndDrop. + if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) { + FinishDragDrop(); + return; + } + source_state_ = SOURCE_STATE_DROPPED; + SendXdndDrop(source_window); + return; + } + + ui::CursorType cursor_type = ui::CursorType::kNull; + switch (negotiated_operation_) { + case ui::DragDropTypes::DRAG_NONE: + cursor_type = ui::CursorType::kDndNone; + break; + case ui::DragDropTypes::DRAG_MOVE: + cursor_type = ui::CursorType::kDndMove; + break; + case ui::DragDropTypes::DRAG_COPY: + cursor_type = ui::CursorType::kDndCopy; + break; + case ui::DragDropTypes::DRAG_LINK: + cursor_type = ui::CursorType::kDndLink; + break; + } + // Note: event.data.[2,3] specify a rectangle. It is a request by the other + // window to not send further XdndPosition messages while the cursor is + // within it. However, it is considered advisory and (at least according to + // the spec) the other side must handle further position messages within + // it. GTK+ doesn't bother with this, so neither should we. + + if (next_position_message_.get()) { + // We were waiting on the status message so we could send off the next + // position message we queued up. + gfx::Point p = next_position_message_->first; + unsigned long event_time = next_position_message_->second; + next_position_message_.reset(); + + SendXdndPosition(source_window, p, event_time); + } +} + +void X11DragSource::OnXdndFinished(const XClientMessageEvent& event) { + gfx::AcceleratedWidget source_window = event.data.l[0]; + if (source_current_window_ != source_window) + return; + + // Clear |negotiated_operation_| if the drag was rejected. + if ((event.data.l[1] & 1) == 0) + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; + + // Clear |source_current_window_| to avoid sending XdndLeave upon ending the + // move loop. + source_current_window_ = gfx::kNullAcceleratedWidget; + FinishDragDrop(); +} + +void X11DragSource::OnSelectionRequest(const XEvent& event) { + selection_owner_.OnSelectionRequest(event); +} + +DragDropTypes::DragOperation X11DragSource::negotiated_operation() { + return negotiated_operation_; +} + +bool X11DragSource::CanDispatchEvent(const ui::PlatformEvent& event) { + return true; +} + +uint32_t X11DragSource::DispatchEvent(const ui::PlatformEvent& event) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + + // This method processes all events while the move loop is active. + ui::EventType type = event->type(); + switch (type) { + case ui::ET_MOUSE_MOVED: + case ui::ET_MOUSE_DRAGGED: { + bool dispatch_mouse_event = !last_motion_in_screen_.get(); + last_motion_in_screen_.reset( + ui::EventFromNative(event).release()->AsMouseEvent()); + + last_motion_in_screen_->set_location( + ui::EventSystemLocationFromNative(event)); + if (dispatch_mouse_event) { + // Post a task to dispatch mouse movement event when control returns to + // the message loop. This allows smoother dragging since the events are + // dispatched without waiting for the drag widget updates. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&X11DragSource::DispatchMouseMovement, + weak_factory_.GetWeakPtr())); + } + return ui::POST_DISPATCH_NONE; + } break; + case ui::ET_MOUSE_RELEASED: { + // TODO: left button checking. + // Assume that drags are being done with the left mouse button. Only + // break the drag if the left mouse button was released. + DispatchMouseMovement(); + HandleMouseRelease(); + + if (!grabbed_pointer_) { + // If the source widget had capture prior to the move loop starting, + // it may be relying on views::Widget getting the mouse release and + // releasing capture in Widget::OnMouseEvent(). + return ui::POST_DISPATCH_PERFORM_DEFAULT; + } + } break; + default: + break; + } + return ui::POST_DISPATCH_PERFORM_DEFAULT; +} + +void X11DragSource::CreateDragInputWindow(XDisplay* display) { + unsigned long attribute_mask = CWEventMask | CWOverrideRedirect; + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.override_redirect = x11::True; + grab_input_window_ = XCreateWindow(display, DefaultRootWindow(display), -100, + -100, 10, 10, 0, CopyFromParent, InputOnly, + CopyFromParent, attribute_mask, &swa); + uint32_t event_mask = ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask; + grab_input_window_events_.reset( + new ui::XScopedEventSelector(grab_input_window_, event_mask)); + + XMapRaised(display, grab_input_window_); +} + +void X11DragSource::SendXdndEnter(XID dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gfx::GetAtom(kXdndEnter); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = (kMaxXdndVersion << 24); // The version number. + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + std::vector targets; + selection_owner_.RetrieveTargets(&targets); + + if (targets.size() > 3) { + xev.xclient.data.l[1] |= 1; + ui::SetAtomArrayProperty(xwindow_, kXdndTypeList, "ATOM", targets); + } else { + // Pack the targets into the enter message. + for (size_t i = 0; i < targets.size(); ++i) + xev.xclient.data.l[2 + i] = targets[i]; + } + + ui::SendXClientEvent(dest_window, &xev); +} + +void X11DragSource::SendXdndLeave(XID dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gfx::GetAtom(kXdndLeave); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + ui::SendXClientEvent(dest_window, &xev); +} + +void X11DragSource::SendXdndPosition(XID dest_window, + const gfx::Point& screen_point, + unsigned long event_time) { + waiting_on_status_ = true; + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gfx::GetAtom(kXdndPosition); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y(); + xev.xclient.data.l[3] = event_time; + xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_); + ui::SendXClientEvent(dest_window, &xev); + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and + // the Xdnd protocol both recommend that drag events should be sent + // periodically. + repeat_mouse_move_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs), + base::Bind(&X11DragSource::ProcessMouseMove, base::Unretained(this), + screen_point, event_time)); +} + +void X11DragSource::SendXdndDrop(XID dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gfx::GetAtom(kXdndDrop); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = x11::CurrentTime; + xev.xclient.data.l[3] = x11::None; + xev.xclient.data.l[4] = x11::None; + ui::SendXClientEvent(dest_window, &xev); +} + +void X11DragSource::DispatchMouseMovement() { + if (!last_motion_in_screen_) + return; + + repeat_mouse_move_timer_.Stop(); + base::TimeTicks time_stamp = last_motion_in_screen_->time_stamp(); + ProcessMouseMove(last_motion_in_screen_->location(), + (time_stamp - base::TimeTicks()).InMilliseconds()); + + last_motion_in_screen_.reset(); +} + +void X11DragSource::ProcessMouseMove(const gfx::Point& screen_point, + unsigned long event_time) { + if (source_state_ != SOURCE_STATE_OTHER) + return; + + // Find the current window the cursor is over. + gfx::AcceleratedWidget dest_window = gfx::kNullAcceleratedWidget; + window_->OnMouseMoved(screen_point, &dest_window); + if (dest_window == gfx::kNullAcceleratedWidget) { + screen_point_ = screen_point; + ui::EnumerateTopLevelWindows(this); + dest_window = ValidateXdndWindow(toplevel_); + } + + if (source_current_window_ != dest_window) { + if (source_current_window_ != gfx::kNullAcceleratedWidget) + SendXdndLeave(source_current_window_); + + source_current_window_ = dest_window; + waiting_on_status_ = false; + next_position_message_.reset(); + status_received_since_enter_ = false; + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; + + if (source_current_window_ != gfx::kNullAcceleratedWidget) + SendXdndEnter(source_current_window_); + } + + if (source_current_window_ != gfx::kNullAcceleratedWidget) { + if (waiting_on_status_) { + next_position_message_.reset( + new std::pair(screen_point, event_time)); + } else { + SendXdndPosition(dest_window, screen_point, event_time); + } + } +} + +void X11DragSource::StartEndMoveLoopTimer() { + end_move_loop_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(kEndMoveLoopTimeoutMs), this, + &X11DragSource::FinishDragDrop); +} + +void X11DragSource::FinishDragDrop() { + window_->OnDragSessionClose(negotiated_operation_); +} + +void X11DragSource::HandleMouseRelease() { + repeat_mouse_move_timer_.Stop(); + + if (source_state_ != SOURCE_STATE_OTHER) { + // The user has previously released the mouse and is clicking in + // frustration. + FinishDragDrop(); + return; + } + + if (source_current_window_ != gfx::kNullAcceleratedWidget) { + if (waiting_on_status_) { + if (status_received_since_enter_) { + // If we are waiting for an XdndStatus message, we need to wait for it + // to complete. + source_state_ = SOURCE_STATE_PENDING_DROP; + + // Start timer to end the move loop if the target takes too long to send + // the XdndStatus and XdndFinished messages. + StartEndMoveLoopTimer(); + return; + } + + FinishDragDrop(); + return; + } + + if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) { + // Start timer to end the move loop if the target takes too long to send + // an XdndFinished message. It is important that StartEndMoveLoopTimer() + // is called before SendXdndDrop() because SendXdndDrop() + // sends XdndFinished synchronously if the drop target is a Chrome + // window. + StartEndMoveLoopTimer(); + + // We have negotiated an action with the other end. + source_state_ = SOURCE_STATE_DROPPED; + SendXdndDrop(source_current_window_); + return; + } + } + + FinishDragDrop(); +} + +bool X11DragSource::ShouldStopIterating(XID xid) { + if (!ui::IsWindowVisible(xid)) + return false; + + if (ui::WindowContainsPoint(xid, screen_point_)) { + toplevel_ = xid; + return true; + } + return false; +} + +} // namespace ui diff --git a/ui/ozone/platform/x11/x11_drag_source.h b/ui/ozone/platform/x11/x11_drag_source.h new file mode 100644 index 0000000000000..e1445e04361ec --- /dev/null +++ b/ui/ozone/platform/x11/x11_drag_source.h @@ -0,0 +1,157 @@ +// Copyright (c) 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. + +#ifndef UI_OZONE_PLATFORM_X11_X11_DRAG_SOURCE_H_ +#define UI_OZONE_PLATFORM_X11_X11_DRAG_SOURCE_H_ + +#include "base/timer/timer.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/x/selection_owner.h" +#include "ui/base/x/x11_util.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/native_widget_types.h" + +namespace ui { +class MouseEvent; +class ScopedEventDispatcher; +class XScopedEventSelector; +class X11WindowOzone; +class OSExchangeData; + +class X11DragSource : public ui::PlatformEventDispatcher, + public ui::EnumerateWindowsDelegate { + public: + explicit X11DragSource(X11WindowOzone* window, + XID xwindow, + int operation, + const OSExchangeData& data); + ~X11DragSource() override; + + void OnXdndStatus(const XClientMessageEvent& event); + void OnXdndFinished(const XClientMessageEvent& event); + void OnSelectionRequest(const XEvent& event); + + DragDropTypes::DragOperation negotiated_operation(); + SelectionFormatMap* format_map() { return &format_map_; } + + // ui:::PlatformEventDispatcher: + bool CanDispatchEvent(const ui::PlatformEvent& event) override; + uint32_t DispatchEvent(const ui::PlatformEvent& event) override; + + private: + enum SourceState { + // |source_current_window_| will receive a drop once we receive an + // XdndStatus from it. + SOURCE_STATE_PENDING_DROP, + + // The move looped will be ended once we receive XdndFinished from + // |source_current_window_|. We should not send XdndPosition to + // |source_current_window_| while in this state. + SOURCE_STATE_DROPPED, + + // There is no drag in progress or there is a drag in progress and the + // user has not yet released the mouse. + SOURCE_STATE_OTHER, + }; + + // Creates an input-only window to be used during the drag. + void CreateDragInputWindow(XDisplay* display); + + void HandleMouseRelease(); + + void FinishDragDrop(); + + void SendXdndEnter(XID dest_window); + void SendXdndLeave(XID dest_window); + void SendXdndPosition(XID dest_window, + const gfx::Point& screen_point, + unsigned long event_time); + void SendXdndDrop(XID dest_window); + + void ProcessMouseMove(const gfx::Point& screen_point, + unsigned long event_time); + + // Dispatch mouse movement event to |delegate_| in a posted task. + void DispatchMouseMovement(); + + void StartEndMoveLoopTimer(); + + // ui::EnumerateWindowsDelegate: + bool ShouldStopIterating(XID xid) override; + + // An invisible InputOnly window. Keyboard grab and sometimes mouse grab + // are set on this window. + XID grab_input_window_; + + // Events selected on |grab_input_window_|. + std::unique_ptr grab_input_window_events_; + + // Whether the pointer was grabbed on |grab_input_window_|. + bool grabbed_pointer_; + + X11WindowOzone* window_; + + XID xwindow_; + + std::unique_ptr last_motion_in_screen_; + gfx::AcceleratedWidget source_current_window_; + + // When the mouse is released, we need to wait for the last XdndStatus message + // only if we have previously received a status message from + // |source_current_window_|. + bool status_received_since_enter_; + + // In the Xdnd protocol, we aren't supposed to send another XdndPosition + // message until we have received a confirming XdndStatus message. + bool waiting_on_status_; + + // If we would send an XdndPosition message while we're waiting for an + // XdndStatus response, we need to cache the latest details we'd send. + std::unique_ptr> next_position_message_; + + // The operation bitfield as requested by StartDragAndDrop. + int drag_operation_; + + // We offer the other window a list of possible operations, + // XdndActionsList. This is the requested action from the other window. This + // is DRAG_NONE if we haven't sent out an XdndPosition message yet, haven't + // yet received an XdndStatus or if the other window has told us that there's + // no action that we can agree on. + DragDropTypes::DragOperation negotiated_operation_; + + // Reprocesses the most recent mouse move event if the mouse has not moved + // in a while in case the window stacking order has changed and + // |source_current_window_| needs to be updated. + base::OneShotTimer repeat_mouse_move_timer_; + + SourceState source_state_; + + // Ends the move loop if the target is too slow to respond after the mouse is + // released. + base::OneShotTimer end_move_loop_timer_; + + std::unique_ptr nested_dispatcher_; + + std::unique_ptr old_dispatcher_; + + // A representation of data. This is either passed to us from the other + // process, or built up through a sequence of Set*() calls. It can be passed + // to |selection_owner_| when we take the selection. + SelectionFormatMap format_map_; + + // Takes a snapshot of |format_map_| and offers it to other windows. + mutable SelectionOwner selection_owner_; + + gfx::Point screen_point_; + XID toplevel_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(X11DragSource); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_DRAG_SOURCE_H_ diff --git a/ui/ozone/platform/x11/x11_drag_util.cc b/ui/ozone/platform/x11/x11_drag_util.cc new file mode 100644 index 0000000000000..62c15ab59d52c --- /dev/null +++ b/ui/ozone/platform/x11/x11_drag_util.cc @@ -0,0 +1,108 @@ +// Copyright (c) 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 "ui/ozone/platform/x11/x11_drag_util.h" + +#include "base/memory/ref_counted_memory.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/x/selection_utils.h" + +namespace ui { + +namespace { +const char kDragNetscapeURL[] = "_NETSCAPE_URL"; +const char kDragString[] = "STRING"; +const char kDragText[] = "TEXT"; +const char kDragTextPlain[] = "text/plain"; +const char kDragTextPlainUtf8[] = "text/plain;charset=utf-8"; +const char kDragUtf8String[] = "UTF8_STRING"; +} // namespace + +::Atom DragOperationToAtom(int drag_operation) { + if (drag_operation & DragDropTypes::DRAG_COPY) + return gfx::GetAtom(kXdndActionCopy); + if (drag_operation & DragDropTypes::DRAG_MOVE) + return gfx::GetAtom(kXdndActionMove); + if (drag_operation & DragDropTypes::DRAG_LINK) + return gfx::GetAtom(kXdndActionLink); + + return x11::None; +} + +DragDropTypes::DragOperation AtomToDragOperation(::Atom atom) { + if (atom == gfx::GetAtom(kXdndActionCopy)) + return DragDropTypes::DRAG_COPY; + if (atom == gfx::GetAtom(kXdndActionMove)) + return DragDropTypes::DRAG_MOVE; + if (atom == gfx::GetAtom(kXdndActionLink)) + return DragDropTypes::DRAG_LINK; + + return DragDropTypes::DRAG_NONE; +} + +std::vector<::Atom> GetOfferedDragOperations(int drag_operations) { + std::vector<::Atom> operations; + if (drag_operations & DragDropTypes::DRAG_COPY) + operations.push_back(gfx::GetAtom(kXdndActionCopy)); + if (drag_operations & DragDropTypes::DRAG_MOVE) + operations.push_back(gfx::GetAtom(kXdndActionMove)); + if (drag_operations & DragDropTypes::DRAG_LINK) + operations.push_back(gfx::GetAtom(kXdndActionLink)); + return operations; +} + +void InsertStringToSelectionFormatMap(const base::string16& text_data, + SelectionFormatMap* map) { + std::string utf8 = base::UTF16ToUTF8(text_data); + scoped_refptr mem( + base::RefCountedString::TakeString(&utf8)); + + map->Insert(gfx::GetAtom(Clipboard::kMimeTypeText), mem); + map->Insert(gfx::GetAtom(kDragText), mem); + map->Insert(gfx::GetAtom(kDragString), mem); + map->Insert(gfx::GetAtom(kDragUtf8String), mem); + map->Insert(gfx::GetAtom(kDragTextPlain), mem); + map->Insert(gfx::GetAtom(kDragTextPlainUtf8), mem); +} + +void InsertURLToSelectionFormatMap(const GURL& url, + const base::string16& title, + SelectionFormatMap* map) { + if (url.is_valid()) { + // Mozilla's URL format: (UTF16: URL, newline, title) + base::string16 spec = base::UTF8ToUTF16(url.spec()); + + std::vector data; + ui::AddString16ToVector(spec, &data); + ui::AddString16ToVector(base::ASCIIToUTF16("\n"), &data); + ui::AddString16ToVector(title, &data); + scoped_refptr mem( + base::RefCountedBytes::TakeVector(&data)); + + map->Insert(gfx::GetAtom(Clipboard::kMimeTypeMozillaURL), mem); + + // Set a string fallback as well. + InsertStringToSelectionFormatMap(spec, map); + + // Set _NETSCAPE_URL for file managers like Nautilus that use it as a hint + // to create a link to the URL. Setting text/uri-list doesn't work because + // Nautilus will fetch and copy the contents of the URL to the drop target + // instead of linking... + // Format is UTF8: URL + "\n" + title. + std::string netscape_url = url.spec(); + netscape_url += "\n"; + netscape_url += base::UTF16ToUTF8(title); + map->Insert(gfx::GetAtom(kDragNetscapeURL), + scoped_refptr( + base::RefCountedString::TakeString(&netscape_url))); + } +} + +void SendXClientEvent(XID window, XEvent* xev) { + DCHECK_EQ(ClientMessage, xev->type); + XSendEvent(gfx::GetXDisplay(), window, x11::False, 0, xev); +} + +} // namespace ui diff --git a/ui/ozone/platform/x11/x11_drag_util.h b/ui/ozone/platform/x11/x11_drag_util.h new file mode 100644 index 0000000000000..a7121c2a5a7fd --- /dev/null +++ b/ui/ozone/platform/x11/x11_drag_util.h @@ -0,0 +1,107 @@ +// Copyright (c) 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. + +#ifndef UI_OZONE_PLATFORM_X11_X11_DRAG_UTIL_H_ +#define UI_OZONE_PLATFORM_X11_X11_DRAG_UTIL_H_ + +#include + +#include "base/strings/string_util.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "url/gurl.h" + +namespace ui { + +class SelectionFormatMap; + +// The lowest XDND protocol version that we understand. +// +// The XDND protocol specification says that we must support all versions +// between 3 and the version we advertise in the XDndAware property. +constexpr int kMinXdndVersion = 3; + +// The value used in the XdndAware property. +// +// The XDND protocol version used between two windows will be the minimum +// between the two versions advertised in the XDndAware property. +constexpr int kMaxXdndVersion = 5; + +// Window property that contains the possible actions that will be presented to +// the user when the drag and drop action is kXdndActionAsk. +const char kXdndActionList[] = "XdndActionList"; + +// Window property that tells other applications the window understands XDND. +const char kXdndAware[] = "XdndAware"; + +// Window property pointing to a proxy window to receive XDND target messages. +// The XDND source must check the proxy window must for the XdndAware property, +// and must send all XDND messages to the proxy instead of the target. However, +// the target field in the messages must still represent the original target +// window (the window pointed to by the cursor). +const char kXdndProxy[] = "XdndProxy"; + +// These actions have the same meaning as in the W3C Drag and Drop spec. +const char kXdndActionCopy[] = "XdndActionCopy"; +const char kXdndActionMove[] = "XdndActionMove"; +const char kXdndActionLink[] = "XdndActionLink"; + +// Message sent from an XDND source to the target to start the XDND protocol. +// The target must wait for an XDndPosition event before querying the data. +const char kXdndEnter[] = "XdndEnter"; + +// Window property that holds the supported drag and drop data types. +// This property is set on the XDND source window when the drag and drop data +// can be converted to more than 3 types. +const char kXdndTypeList[] = "XdndTypeList"; + +// Message sent from an XDND source to the target when the user cancels the drag +// and drop operation. +const char kXdndLeave[] = "XdndLeave"; + +// Message sent by the XDND source when the cursor position changes. +// The source will also send an XdndPosition event right after the XdndEnter +// event, to tell the target about the initial cursor position and the desired +// drop action. +// The time stamp in the XdndPosition must be used when requesting selection +// information. +// After the target optionally acquires selection information, it must tell the +// source if it can accept the drop via an XdndStatus message. +const char kXdndPosition[] = "XdndPosition"; + +// Message sent from an XDND source to the target when the user confirms the +// drag and drop operation. +const char kXdndDrop[] = "XdndDrop"; + +// Selection used by the XDND protocol to transfer data between applications. +const char kXdndSelection[] = "XdndSelection"; + +// Message sent by the XDND target in response to an XdndPosition message. +// The message informs the source if the target will accept the drop, and what +// action will be taken if the drop is accepted. +const char kXdndStatus[] = "XdndStatus"; + +// Message sent from an XDND target to the source in respose to an XdndDrop. +// The message must be sent whether the target acceepts the drop or not. +const char kXdndFinished[] = "XdndFinished"; + +::Atom DragOperationToAtom(int drag_operation); + +DragDropTypes::DragOperation AtomToDragOperation(::Atom atom); + +std::vector<::Atom> GetOfferedDragOperations(int drag_operations); + +void InsertStringToSelectionFormatMap(const base::string16& text_data, + SelectionFormatMap* map); + +void InsertURLToSelectionFormatMap(const GURL& url, + const base::string16& title, + SelectionFormatMap* map); + +void SendXClientEvent(XID xwindow, XEvent* xev); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_DRAG_UTIL_H_ diff --git a/ui/ozone/platform/x11/x11_window_ozone.cc b/ui/ozone/platform/x11/x11_window_ozone.cc index 9c312abdad9d1..e9ba39982b453 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.cc +++ b/ui/ozone/platform/x11/x11_window_ozone.cc @@ -5,6 +5,8 @@ #include "ui/ozone/platform/x11/x11_window_ozone.h" #include "base/bind.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/x/x11_util.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/ozone/events_ozone.h" @@ -12,6 +14,9 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/x/x11.h" #include "ui/ozone/platform/x11/x11_cursor_ozone.h" +#include "ui/ozone/platform/x11/x11_drag_context.h" +#include "ui/ozone/platform/x11/x11_drag_source.h" +#include "ui/ozone/platform/x11/x11_drag_util.h" #include "ui/ozone/platform/x11/x11_window_manager_ozone.h" namespace ui { @@ -19,7 +24,9 @@ namespace ui { X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager, PlatformWindowDelegate* delegate, const gfx::Rect& bounds) - : X11WindowBase(delegate, bounds), window_manager_(window_manager) { + : X11WindowBase(delegate, bounds), + window_manager_(window_manager), + target_current_context_(nullptr) { DCHECK(window_manager); auto* event_source = X11EventSourceLibevent::GetInstance(); if (event_source) @@ -29,6 +36,11 @@ X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager, #if !defined(OS_CHROMEOS) move_loop_client_.reset(new WindowMoveLoopClient()); #endif + + unsigned long xdnd_version = kMaxXdndVersion; + XChangeProperty(xdisplay(), xwindow(), gfx::GetAtom(kXdndAware), XA_ATOM, 32, + PropModeReplace, + reinterpret_cast(&xdnd_version), 1); } X11WindowOzone::~X11WindowOzone() { @@ -54,6 +66,16 @@ void X11WindowOzone::SetCursor(PlatformCursor cursor) { XDefineCursor(xdisplay(), xwindow(), cursor_ozone->xcursor()); } +void X11WindowOzone::StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) { + std::vector<::Atom> actions = GetOfferedDragOperations(operation); + ui::SetAtomArrayProperty(xwindow(), kXdndActionList, "ATOM", actions); + + drag_source_ = + std::make_unique(this, xwindow(), operation, data); +} + bool X11WindowOzone::RunMoveLoop(const gfx::Vector2d& drag_offset) { // TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. #if !defined(OS_CHROMEOS) @@ -88,6 +110,9 @@ bool X11WindowOzone::DispatchXEvent(XEvent* xev) { if (!IsEventForXWindow(*xev)) return false; + if (ProcessDragDropEvent(xev)) + return true; + ProcessXWindowEvent(xev); return true; } @@ -127,4 +152,147 @@ void X11WindowOzone::OnLostCapture() { delegate()->OnLostCapture(); } +void X11WindowOzone::OnDragDataCollected(const gfx::PointF& screen_point, + std::unique_ptr data, + int operation) { + // TODO(jkim). + // delegate()->OnDragEnter(this, screen_point, std::move(data), operation); + NOTIMPLEMENTED_LOG_ONCE(); +} + +void X11WindowOzone::OnMouseMoved(const gfx::Point& point, + gfx::AcceleratedWidget* widget) { + // TODO(jkim). + // delegate()->OnMouseMoved(point, widget); + NOTIMPLEMENTED_LOG_ONCE(); +} + +void X11WindowOzone::OnDragSessionClose(int dnd_action) { + // TODO(jkim). + // drag_source_.reset(); + // delegate()->OnDragSessionClosed(dnd_action); + NOTIMPLEMENTED_LOG_ONCE(); +} + +void X11WindowOzone::OnDragMotion(const gfx::PointF& screen_point, + int flags, + ::Time event_time, + int operation) { + // TODO(jkim). + // gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + // drag_operation_ = + // delegate()->OnDragMotion(screen_point, event_time, operation, &widget); + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool X11WindowOzone::ProcessDragDropEvent(XEvent* xev) { + switch (xev->type) { + case SelectionNotify: { + if (!target_current_context_.get()) { + NOTREACHED(); + return false; + } + target_current_context_->OnSelectionNotify(xev->xselection); + return true; + } + case PropertyNotify: { + if (xev->xproperty.atom != gfx::GetAtom(kXdndActionList)) + return false; + if (!target_current_context_.get() || + target_current_context_->source_window() != xev->xany.window) { + return false; + } + target_current_context_->ReadActions(); + return true; + } + case SelectionRequest: { + if (!drag_source_) + return false; + drag_source_->OnSelectionRequest(*xev); + return true; + } + case ClientMessage: { + XClientMessageEvent& event = xev->xclient; + Atom message_type = event.message_type; + if (message_type == gfx::GetAtom("WM_PROTOCOLS")) + return false; + + if (message_type == gfx::GetAtom(kXdndEnter)) { + int version = (event.data.l[1] & 0xff000000) >> 24; + if (version < kMinXdndVersion) { + // This protocol version is not documented in the XDND standard (last + // revised in 1999), so we don't support it. Since don't understand + // the protocol spoken by the source, we can't tell it that we can't + // talk to it. + LOG(ERROR) + << "XdndEnter message discarded because its version is too old."; + return false; + } + if (version > kMaxXdndVersion) { + // The XDND version used should be the minimum between the versions + // advertised by the source and the target. We advertise + // kMaxXdndVersion, so this should never happen when talking to an + // XDND-compliant application. + LOG(ERROR) + << "XdndEnter message discarded because its version is too new."; + return false; + } + // Make sure that we've run ~X11DragContext() before creating another + // one. + target_current_context_.reset(); + SelectionFormatMap* map = nullptr; + if (drag_source_) + map = drag_source_->format_map(); + target_current_context_.reset( + new X11DragContext(this, xwindow(), event, map)); + return true; + } + if (message_type == gfx::GetAtom(kXdndLeave)) { + // TODO(jkim). + // delegate()->OnDragLeave(); + NOTIMPLEMENTED_LOG_ONCE(); + return true; + } + if (message_type == gfx::GetAtom(kXdndPosition)) { + if (!target_current_context_.get()) { + NOTREACHED(); + return false; + } + + target_current_context_->OnXdndPosition(event); + return true; + } + if (message_type == gfx::GetAtom(kXdndStatus)) { + drag_source_->OnXdndStatus(event); + return true; + } + if (message_type == gfx::GetAtom(kXdndFinished)) { + // TODO(jkim). + // int negotiated_operation = drag_source_->negotiated_operation(); + // drag_source_->OnXdndFinished(event); + // delegate()->OnDragSessionClosed(negotiated_operation); + NOTIMPLEMENTED_LOG_ONCE(); + return true; + } + if (message_type == gfx::GetAtom(kXdndDrop)) { + // TODO(jkim). + // delegate()->OnDragDrop(nullptr); + return false; + + if (!target_current_context_.get()) { + NOTREACHED(); + return false; + } + target_current_context_->OnXdndDrop(drag_operation_); + target_current_context_.reset(); + return true; + } + break; + } + default: + break; + } + return false; +} + } // namespace ui diff --git a/ui/ozone/platform/x11/x11_window_ozone.h b/ui/ozone/platform/x11/x11_window_ozone.h index 6be3f747549df..b03890b4cf813 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.h +++ b/ui/ozone/platform/x11/x11_window_ozone.h @@ -13,6 +13,9 @@ namespace ui { +class OSExchangeData; +class X11DragContext; +class X11DragSource; class X11WindowManagerOzone; // PlatformWindow implementation for X11 Ozone. PlatformEvents are ui::Events. @@ -34,6 +37,10 @@ class X11WindowOzone : public X11WindowBase, void ReleaseCapture() override; void SetCursor(PlatformCursor cursor) override; + void StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) override; + bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; void StopMoveLoop() override; @@ -43,10 +50,35 @@ class X11WindowOzone : public X11WindowBase, PlatformEventDispatcher* GetPlatformEventDispatcher() override; bool DispatchXEvent(XEvent* event) override; + void OnDragDataCollected(const gfx::PointF& screen_point, + std::unique_ptr data, + int operation); + void OnDragMotion(const gfx::PointF& screen_point, + int flags, + ::Time event_time, + int operation); + void OnMouseMoved(const gfx::Point& point, gfx::AcceleratedWidget* widget); + void OnDragSessionClose(int dnd_action); + private: + enum SourceState { + // |source_current_window_| will receive a drop once we receive an + // XdndStatus from it. + SOURCE_STATE_PENDING_DROP, + + // The move looped will be ended once we receive XdndFinished from + // |source_current_window_|. We should not send XdndPosition to + // |source_current_window_| while in this state. + SOURCE_STATE_DROPPED, + + // There is no drag in progress or there is a drag in progress and the + // user has not yet released the mouse. + SOURCE_STATE_OTHER, + }; // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; + bool ProcessDragDropEvent(XEvent* xev); X11WindowManagerOzone* window_manager_; @@ -59,6 +91,11 @@ class X11WindowOzone : public X11WindowBase, // previous check in ::CheckCanDispatchNextPlatformEvent based on a XID // target. bool handle_next_event_ = false; + std::unique_ptr target_current_context_; + // DesktopDragDropClientOzone* drag_drop_client_; + int drag_operation_; + // unsigned long source_window_; + std::unique_ptr drag_source_; DISALLOW_COPY_AND_ASSIGN(X11WindowOzone); }; diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc index 1887b3b377a8a..e39017bd4711e 100644 --- a/ui/platform_window/x11/x11_window_base.cc +++ b/ui/platform_window/x11/x11_window_base.cc @@ -354,6 +354,15 @@ bool X11WindowBase::RunMoveLoop(const gfx::Vector2d& drag_offset) { void X11WindowBase::StopMoveLoop() {} +void X11WindowBase::StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) { + // If this is for ozone/X11, the child class of this, X11WindowOzone, will + // handle it. Otherwise, DragDrop has been implemented directly, for instance + // DesktopDragDropClientAuraX11 or DragDropControllerMus. + NOTREACHED(); +} + void X11WindowBase::UnConfineCursor() { if (!has_pointer_barriers_) return; diff --git a/ui/platform_window/x11/x11_window_base.h b/ui/platform_window/x11/x11_window_base.h index 83a0c668186b8..b05b1dd43e090 100644 --- a/ui/platform_window/x11/x11_window_base.h +++ b/ui/platform_window/x11/x11_window_base.h @@ -11,12 +11,14 @@ #include "base/callback.h" #include "base/containers/flat_set.h" +#include "ui/base/dragdrop/os_exchange_data.h" #include "base/macros.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_types.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/platform_window_handler/wm_drag_handler.h" #include "ui/platform_window/x11/x11_window_export.h" namespace ui { @@ -25,11 +27,17 @@ class XScopedEventSelector; // Abstract base implementation for a X11 based PlatformWindow. Methods that // are platform specific are left unimplemented. -class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { +class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow, + public WmDragHandler { public: X11WindowBase(PlatformWindowDelegate* delegate, const gfx::Rect& bounds); ~X11WindowBase() override; + // Initiates Drag Action. + void StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) override; + // PlatformWindow: void Show() override; void Hide() override; From e8dc77d2d182b693fe1994c7fbea817369090ad0 Mon Sep 17 00:00:00 2001 From: Jose Dapena Paz Date: Mon, 1 Oct 2018 14:53:13 +0200 Subject: [PATCH 14/17] [ozone/common] Fix gbm_bo_get_plane_count type mismatch Though minigbm gbm_bo_get_plane_count returns a size_t, libgbm is returning an int. And a return value of -1 is possible (it reports the operation failed). So modify DCHECK that would assume both are size_t for the case build is using system GBM. --- ui/ozone/common/linux/gbm_wrapper.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/ozone/common/linux/gbm_wrapper.cc b/ui/ozone/common/linux/gbm_wrapper.cc index c74545e4871d4..24518cea94330 100644 --- a/ui/ozone/common/linux/gbm_wrapper.cc +++ b/ui/ozone/common/linux/gbm_wrapper.cc @@ -67,7 +67,12 @@ void InitializeImportData(uint32_t format, } int GetPlaneFdForBo(gbm_bo* bo, size_t plane) { +#if defined(USING_MINIGBM) DCHECK(plane < gbm_bo_get_plane_count(bo)); +#else + const int plane_count = gbm_bo_get_plane_count(bo); + DCHECK(plane_count > 0 && plane < static_cast(plane_count)); +#endif // System linux gbm (or Mesa gbm) does not provide fds per plane basis. Thus, // get plane handle and use drm ioctl to get a prime fd out of it avoid having From 1785f1fcc74d4194ef5f3f0ad1ee6ea9cb13e7f1 Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Wed, 3 Oct 2018 13:30:12 +0300 Subject: [PATCH 15/17] [ozone/x11] Add window move/resize support. This patch adds a move/resize support to the Ozone/X11 platform. Change-Id: Ie5b2a059f0024f12b3235272f43876379be041b6 --- ui/ozone/platform/x11/x11_window_ozone.cc | 93 +++++++++++++++++++++++ ui/ozone/platform/x11/x11_window_ozone.h | 11 ++- ui/platform_window/x11/x11_window_base.h | 1 + 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/ui/ozone/platform/x11/x11_window_ozone.cc b/ui/ozone/platform/x11/x11_window_ozone.cc index e9ba39982b453..5dcc3e05309ea 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.cc +++ b/ui/ozone/platform/x11/x11_window_ozone.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/hit_test.h" #include "ui/base/x/x11_util.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" @@ -21,6 +22,60 @@ namespace ui { +namespace { + +// These constants are defined in the Extended Window Manager Hints +// standard...and aren't in any header that I can find. +const int k_NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; +const int k_NET_WM_MOVERESIZE_SIZE_TOP = 1; +const int k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2; +const int k_NET_WM_MOVERESIZE_SIZE_RIGHT = 3; +const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4; +const int k_NET_WM_MOVERESIZE_SIZE_BOTTOM = 5; +const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6; +const int k_NET_WM_MOVERESIZE_SIZE_LEFT = 7; +const int k_NET_WM_MOVERESIZE_MOVE = 8; + +// Identifies the direction of the "hittest" for X11. +bool IdentifyDirection(int hittest, int* direction) { + DCHECK(direction); + *direction = -1; + switch (hittest) { + case HTBOTTOM: + *direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + case HTBOTTOMLEFT: + *direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + case HTBOTTOMRIGHT: + *direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + case HTCAPTION: + *direction = k_NET_WM_MOVERESIZE_MOVE; + break; + case HTLEFT: + *direction = k_NET_WM_MOVERESIZE_SIZE_LEFT; + break; + case HTRIGHT: + *direction = k_NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + case HTTOP: + *direction = k_NET_WM_MOVERESIZE_SIZE_TOP; + break; + case HTTOPLEFT: + *direction = k_NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + case HTTOPRIGHT: + *direction = k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + default: + return false; + } + return true; +} + +} // namespace + X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager, PlatformWindowDelegate* delegate, const gfx::Rect& bounds) @@ -41,6 +96,10 @@ X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager, XChangeProperty(xdisplay(), xwindow(), gfx::GetAtom(kXdndAware), XA_ATOM, 32, PropModeReplace, reinterpret_cast(&xdnd_version), 1); + + // Set a class property key, which allows |this| to be used for interactive + // events, e.g. move or resize. + SetWmMoveResizeHandler(this, AsWmMoveResizeHandler()); } X11WindowOzone::~X11WindowOzone() { @@ -174,6 +233,36 @@ void X11WindowOzone::OnDragSessionClose(int dnd_action) { NOTIMPLEMENTED_LOG_ONCE(); } +void X11WindowOzone::DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location) { + int direction; + if (!IdentifyDirection(hittest, &direction)) + return; + + // We most likely have an implicit grab right here. We need to dump it + // because what we're about to do is tell the window manager + // that it's now responsible for moving the window around; it immediately + // grabs when it receives the event below. + XUngrabPointer(xdisplay(), x11::CurrentTime); + + XEvent event; + memset(&event, 0, sizeof(event)); + event.xclient.type = ClientMessage; + event.xclient.display = xdisplay(); + event.xclient.window = xwindow(); + event.xclient.message_type = gfx::GetAtom("_NET_WM_MOVERESIZE"); + event.xclient.format = 32; + event.xclient.data.l[0] = pointer_location.x(); + event.xclient.data.l[1] = pointer_location.y(); + event.xclient.data.l[2] = direction; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + XSendEvent(xdisplay(), xroot_window(), x11::False, + SubstructureRedirectMask | SubstructureNotifyMask, &event); +} + void X11WindowOzone::OnDragMotion(const gfx::PointF& screen_point, int flags, ::Time event_time, @@ -295,4 +384,8 @@ bool X11WindowOzone::ProcessDragDropEvent(XEvent* xev) { return false; } +WmMoveResizeHandler* X11WindowOzone::AsWmMoveResizeHandler() { + return static_cast(this); +} + } // namespace ui diff --git a/ui/ozone/platform/x11/x11_window_ozone.h b/ui/ozone/platform/x11/x11_window_ozone.h index b03890b4cf813..4dd2a6877a4bf 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.h +++ b/ui/ozone/platform/x11/x11_window_ozone.h @@ -8,6 +8,7 @@ #include "base/macros.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/x11/x11_event_source_libevent.h" +#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" #include "ui/platform_window/x11/window_move_loop_client.h" #include "ui/platform_window/x11/x11_window_base.h" @@ -21,7 +22,8 @@ class X11WindowManagerOzone; // PlatformWindow implementation for X11 Ozone. PlatformEvents are ui::Events. class X11WindowOzone : public X11WindowBase, public PlatformEventDispatcher, - public XEventDispatcher { + public XEventDispatcher, + public WmMoveResizeHandler { public: X11WindowOzone(X11WindowManagerOzone* window_manager, PlatformWindowDelegate* delegate, @@ -60,6 +62,11 @@ class X11WindowOzone : public X11WindowBase, void OnMouseMoved(const gfx::Point& point, gfx::AcceleratedWidget* widget); void OnDragSessionClose(int dnd_action); + // WmMoveResizeHandler + void DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location) override; + private: enum SourceState { // |source_current_window_| will receive a drop once we receive an @@ -80,6 +87,8 @@ class X11WindowOzone : public X11WindowBase, uint32_t DispatchEvent(const PlatformEvent& event) override; bool ProcessDragDropEvent(XEvent* xev); + WmMoveResizeHandler* AsWmMoveResizeHandler(); + X11WindowManagerOzone* window_manager_; // TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. diff --git a/ui/platform_window/x11/x11_window_base.h b/ui/platform_window/x11/x11_window_base.h index b05b1dd43e090..8051a5e378b2d 100644 --- a/ui/platform_window/x11/x11_window_base.h +++ b/ui/platform_window/x11/x11_window_base.h @@ -71,6 +71,7 @@ class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow, XDisplay* xdisplay() { return xdisplay_; } XID xwindow() const { return xwindow_; } + XID xroot_window() const { return xroot_window_; } void UnConfineCursor(); From 005f53290881da8e326b1c7946134c63e207fe44 Mon Sep 17 00:00:00 2001 From: Maksim Sisov Date: Wed, 10 Oct 2018 11:54:19 +0300 Subject: [PATCH 16/17] [DoNotCarryForward] [ozone/wayland] Implement not implemented methods in WaylandScreen. - GetDisplayForAcceleratedWidget: * This CL implements wl_surface_listener, which says what wl_output a wl_surface enters. That information is then used to identify on what display an accelerated widget is shown. To give more details, a WaylandWindow sets a wl_surface_listener now. As soon as that specific window enters a certain display, wl_surface_listener::enter event is called, and the WaylandWindow receives a pointer to that specific wl_output where it is shown. Then, the window uses WaylandOutputManager to identify the id of that display/output and stores it. Once the WaylandScreen::GetDisplayForAcceleratedWidget call comes, a WaylandWindow, which corresponds to that specific widget, is found and the id of the output it is shown on is taken. If there are two outputs used to show the window, the very first one is always used. - GetAcceleratedWidgetAtScreenPoint * This is a tricky one. To ensure right functionality, a widget under a cursor must be returned. But, Wayland clients cannot know where the windows are located in the global space coordinate system. Instead, it's possible to know widgets located on a surface local coordinate system (remember that clients cannot also know the position of the pointer in the global space coordinate system, but rather on a local surface coordinate system). That is, we will have to pretend that a single surface is a "display", where other widgets (child widgets are located in the surface local coordinate system, where the main surface has 0,0 origin) are shown. Whenever that surface is focused (the cursor is located under that widget), we will use it to determine if the point is on that main surface, a menu surface and etc. Note that bounds where widgets are shown will be fixed in the follow-up cl. Now, the widgets are not shown properly if the returned display has origin different from 0,0. That is, remember that wayland requires placing them relatively to the parent surface, which Chromium does not honor, but rather uses global space coordinate system. - GetDisplayNearestPoint and GetDisplayMatching: * Nothing special. Both of them use the existing helper methods. Bug: 875161, 890271, 890272 Change-Id: I7d6d8a7d6827cf639df2c3f0992deece156b2962 --- ui/ozone/platform/wayland/fake_server.cc | 10 + ui/ozone/platform/wayland/fake_server.h | 6 +- .../wayland/ozone_platform_wayland.cc | 3 +- ui/ozone/platform/wayland/wayland_output.cc | 5 +- ui/ozone/platform/wayland/wayland_output.h | 3 + .../wayland/wayland_output_manager.cc | 15 +- .../platform/wayland/wayland_output_manager.h | 6 +- ui/ozone/platform/wayland/wayland_screen.cc | 86 ++++- ui/ozone/platform/wayland/wayland_screen.h | 7 +- .../wayland/wayland_screen_unittest.cc | 310 ++++++++++++++++-- ui/ozone/platform/wayland/wayland_window.cc | 60 +++- ui/ozone/platform/wayland/wayland_window.h | 25 ++ 12 files changed, 473 insertions(+), 63 deletions(-) diff --git a/ui/ozone/platform/wayland/fake_server.cc b/ui/ozone/platform/wayland/fake_server.cc index 62e7f8c16c118..405716c3cdba8 100644 --- a/ui/ozone/platform/wayland/fake_server.cc +++ b/ui/ozone/platform/wayland/fake_server.cc @@ -1196,6 +1196,16 @@ void FakeServer::Resume() { resume_event_.Signal(); } +MockOutput* FakeServer::CreateAndInitializeOutput() { + auto output = std::make_unique(); + output->Initialize(display()); + + MockOutput* output_ptr = output.get(); + globals_.push_back(std::move(output)); + + return output_ptr; +} + void FakeServer::DoPause() { base::RunLoop().RunUntilIdle(); pause_event_.Signal(); diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h index ddda5748a1966..4531958f6ff26 100644 --- a/ui/ozone/platform/wayland/fake_server.h +++ b/ui/ozone/platform/wayland/fake_server.h @@ -453,11 +453,7 @@ class FakeServer : public base::Thread, base::MessagePumpLibevent::FdWatcher { return resource ? T::FromResource(resource) : nullptr; } - void CreateAndInitializeOutput() { - auto output = std::make_unique(); - output->Initialize(display()); - globals_.push_back(std::move(output)); - } + MockOutput* CreateAndInitializeOutput(); MockDataDeviceManager* data_device_manager() { return &data_device_manager_; } MockSeat* seat() { return &seat_; } diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc index ddddd8615275c..e2c6c135ff5fb 100644 --- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -111,7 +111,8 @@ class OzonePlatformWayland : public OzonePlatform { // The WaylandConnection and the WaylandOutputManager must be created before // PlatformScreen. DCHECK(connection_ && connection_->wayland_output_manager()); - return connection_->wayland_output_manager()->CreateWaylandScreen(); + return connection_->wayland_output_manager()->CreateWaylandScreen( + connection_.get()); } void InitializeUI(const InitParams& args) override { diff --git a/ui/ozone/platform/wayland/wayland_output.cc b/ui/ozone/platform/wayland/wayland_output.cc index fd14f45d63324..0ab6ebaccfbd1 100644 --- a/ui/ozone/platform/wayland/wayland_output.cc +++ b/ui/ozone/platform/wayland/wayland_output.cc @@ -34,7 +34,6 @@ void WaylandOutput::Initialize(Delegate* delegate) { } void WaylandOutput::TriggerDelegateNotification() const { - DCHECK(!rect_in_physical_pixels_.IsEmpty()); delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_, device_scale_factor_); } @@ -51,8 +50,10 @@ void WaylandOutput::OutputHandleGeometry(void* data, const char* model, int32_t output_transform) { WaylandOutput* wayland_output = static_cast(data); - if (wayland_output) + if (wayland_output) { wayland_output->rect_in_physical_pixels_.set_origin(gfx::Point(x, y)); + wayland_output->TriggerDelegateNotification(); + } } // static diff --git a/ui/ozone/platform/wayland/wayland_output.h b/ui/ozone/platform/wayland/wayland_output.h index ddf7946b194f1..ed3968b1e0562 100644 --- a/ui/ozone/platform/wayland/wayland_output.h +++ b/ui/ozone/platform/wayland/wayland_output.h @@ -34,6 +34,9 @@ class WaylandOutput { void TriggerDelegateNotification() const; + // Says whether this WaylandOutput is a wrapper of the passed |output|. + bool has_output(wl_output* output) const { return output_.get() == output; } + uint32_t output_id() const { return output_id_; } // Tells if the output has already received physical screen dimensions in the diff --git a/ui/ozone/platform/wayland/wayland_output_manager.cc b/ui/ozone/platform/wayland/wayland_output_manager.cc index c9db25c35b40e..3a9c337be8837 100644 --- a/ui/ozone/platform/wayland/wayland_output_manager.cc +++ b/ui/ozone/platform/wayland/wayland_output_manager.cc @@ -65,8 +65,19 @@ void WaylandOutputManager::RemoveWaylandOutput(const uint32_t output_id) { OnWaylandOutputRemoved(output_id); } -std::unique_ptr WaylandOutputManager::CreateWaylandScreen() { - auto wayland_screen = std::make_unique(); +uint32_t WaylandOutputManager::GetIdForOutput(wl_output* output) const { + auto output_it = std::find_if(output_list_.begin(), output_list_.end(), + [output](const auto& wayland_output) { + return wayland_output->has_output(output); + }); + // This is unlikely to happen, but let's be explicit here. + DCHECK(output_it != output_list_.end()); + return output_it->get()->output_id(); +} + +std::unique_ptr WaylandOutputManager::CreateWaylandScreen( + WaylandConnection* connection) { + auto wayland_screen = std::make_unique(connection); wayland_screen_ = wayland_screen->GetWeakPtr(); // As long as |wl_output| sends geometry and other events asynchronously (that diff --git a/ui/ozone/platform/wayland/wayland_output_manager.h b/ui/ozone/platform/wayland/wayland_output_manager.h index 5fcdced8a0428..50215f612955b 100644 --- a/ui/ozone/platform/wayland/wayland_output_manager.h +++ b/ui/ozone/platform/wayland/wayland_output_manager.h @@ -19,6 +19,7 @@ struct wl_output; namespace ui { +class WaylandConnection; class WaylandOutput; class WaylandOutputManager : public WaylandOutput::Delegate { @@ -32,8 +33,11 @@ class WaylandOutputManager : public WaylandOutput::Delegate { void AddWaylandOutput(const uint32_t output_id, wl_output* output); void RemoveWaylandOutput(const uint32_t output_id); + uint32_t GetIdForOutput(wl_output* output) const; + // Creates a platform screen and feeds it with existing outputs. - std::unique_ptr CreateWaylandScreen(); + std::unique_ptr CreateWaylandScreen( + WaylandConnection* connection); private: void OnWaylandOutputAdded(uint32_t output_id); diff --git a/ui/ozone/platform/wayland/wayland_screen.cc b/ui/ozone/platform/wayland/wayland_screen.cc index 98ef35e3f6141..4763ed3568867 100644 --- a/ui/ozone/platform/wayland/wayland_screen.cc +++ b/ui/ozone/platform/wayland/wayland_screen.cc @@ -9,10 +9,13 @@ #include "ui/display/display_observer.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { -WaylandScreen::WaylandScreen() : weak_factory_(this) {} +WaylandScreen::WaylandScreen(WaylandConnection* connection) + : connection_(connection), weak_factory_(this) {} WaylandScreen::~WaylandScreen() = default; @@ -59,12 +62,23 @@ display::Display WaylandScreen::GetPrimaryDisplay() const { display::Display WaylandScreen::GetDisplayForAcceleratedWidget( gfx::AcceleratedWidget widget) const { - // TODO(msisov): implement wl_surface_listener::enter and - // wl_surface_listener::leave for a wl_surface to know what surface the window - // is located on. - // - // https://crbug.com/890271 - NOTIMPLEMENTED_LOG_ONCE(); + auto* wayland_window = connection_->GetWindow(widget); + if (!wayland_window) + return GetPrimaryDisplay(); + + const std::vector entered_outputs_ids = + wayland_window->entered_outputs_ids(); + if (entered_outputs_ids.empty()) + return GetPrimaryDisplay(); + + // A widget can be located on two displays. Return the one, which was the very + // first used. + for (const auto& display : display_list_.displays()) { + if (display.id() == entered_outputs_ids.front()) + return display; + } + + NOTREACHED(); return GetPrimaryDisplay(); } @@ -75,25 +89,61 @@ gfx::Point WaylandScreen::GetCursorScreenPoint() const { gfx::AcceleratedWidget WaylandScreen::GetAcceleratedWidgetAtScreenPoint( const gfx::Point& point) const { - // TODO(msisov): implement this once wl_surface_listener::enter and ::leave - // are used. - // - // https://crbug.com/890271 - NOTIMPLEMENTED_LOG_ONCE(); - return gfx::kNullAcceleratedWidget; + // This is a tricky one. To ensure right functionality, a widget under a + // cursor must be returned. But, Wayland clients cannot know where the windows + // are located in the global space coordinate system. Instead, it's possible + // to know widgets located on a surface local coordinate system (remember that + // clients cannot also know the position of the pointer in the global space + // coordinate system, but rather on a local surface coordinate system). That + // is, we will have to pretend that a single surface is a "display", where + // other widgets (child widgets are located in the surface local coordinate + // system, where the main surface has 0,0 origin) are shown. Whenever that + // surface is focused (the cursor is located under that widget), we will use + // it to determine if the point is on that main surface, a menu surface and + // etc. + + // This call comes only when a cursor is under a certain window (see how + // Wayland sends pointer events for better understanding + comment above). + auto* window = connection_->GetCurrentFocusedWindow(); + if (!window) + return gfx::kNullAcceleratedWidget; + + // If |point| is at origin and the focused window does not contain that point, + // it must be the root parent, which contains that |point|. + if (point.IsOrigin() && !window->GetBounds().Contains(point)) { + WaylandWindow* parent_window = nullptr; + do { + parent_window = window->parent_window(); + if (parent_window) + window = parent_window; + } while (parent_window); + DCHECK(!window->parent_window()); + } + + DCHECK(window->GetBounds().Contains(point)); + return window->GetWidget(); } display::Display WaylandScreen::GetDisplayNearestPoint( const gfx::Point& point) const { - NOTIMPLEMENTED_LOG_ONCE(); - return GetPrimaryDisplay(); + if (display_list_.displays().size() <= 1) + return GetPrimaryDisplay(); + for (const auto& display : display_list_.displays()) { + if (display.bounds().Contains(point)) + return display; + } + + return *FindDisplayNearestPoint(display_list_.displays(), point); } display::Display WaylandScreen::GetDisplayMatching( const gfx::Rect& match_rect) const { - // TODO(msisov): https://crbug.com/890272 - NOTIMPLEMENTED_LOG_ONCE(); - return GetPrimaryDisplay(); + if (match_rect.IsEmpty()) + return GetDisplayNearestPoint(match_rect.origin()); + + const display::Display* match = + FindDisplayWithBiggestIntersection(display_list_.displays(), match_rect); + return match ? *match : GetPrimaryDisplay(); } void WaylandScreen::AddObserver(display::DisplayObserver* observer) { diff --git a/ui/ozone/platform/wayland/wayland_screen.h b/ui/ozone/platform/wayland/wayland_screen.h index f2de8d4dbb959..ec32b5f6ca7b9 100644 --- a/ui/ozone/platform/wayland/wayland_screen.h +++ b/ui/ozone/platform/wayland/wayland_screen.h @@ -17,10 +17,12 @@ namespace ui { +class WaylandConnection; + // A PlatformScreen implementation for Wayland. class WaylandScreen : public PlatformScreen { public: - WaylandScreen(); + explicit WaylandScreen(WaylandConnection* connection); ~WaylandScreen() override; void OnOutputAdded(uint32_t output_id, bool is_primary); @@ -52,6 +54,9 @@ class WaylandScreen : public PlatformScreen { base::ObserverList observers_; + // Non-owned pointer. + WaylandConnection* connection_ = nullptr; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(WaylandScreen); diff --git a/ui/ozone/platform/wayland/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/wayland_screen_unittest.cc index 7eb9350f2987c..aa44a1bb3bdb0 100644 --- a/ui/ozone/platform/wayland/wayland_screen_unittest.cc +++ b/ui/ozone/platform/wayland/wayland_screen_unittest.cc @@ -5,12 +5,18 @@ #include #include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/display.h" #include "ui/display/display_observer.h" #include "ui/ozone/platform/wayland/fake_server.h" #include "ui/ozone/platform/wayland/wayland_connection.h" #include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_screen.h" #include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/test/mock_platform_window_delegate.h" +#include "ui/platform_window/platform_window_init_properties.h" + +using ::testing::SaveArg; +using ::testing::_; namespace ui { @@ -69,12 +75,74 @@ class WaylandScreenTest : public WaylandTest { output_manager_ = connection_->wayland_output_manager(); ASSERT_TRUE(output_manager_); + + EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); + platform_screen_ = output_manager_->CreateWaylandScreen(connection_.get()); } protected: + void ValidateTheDisplayForWidget(gfx::AcceleratedWidget widget, + int64_t expected_display_id, + const gfx::Point& expected_origin) { + display::Display display_for_widget = + platform_screen_->GetDisplayForAcceleratedWidget(widget); + EXPECT_EQ(display_for_widget.id(), expected_display_id); + EXPECT_EQ(display_for_widget.bounds().origin(), expected_origin); + } + + wl::MockOutput* CreateOutputWithRectAndSync(const gfx::Rect& rect) { + wl::MockOutput* output = server_.CreateAndInitializeOutput(); + if (!rect.IsEmpty()) + output->SetRect(rect); + + Sync(); + + // The geometry changes are automatically sent only after the client is + // bound to the output. Sync one more time to enqueue the geometry events. + Sync(); + + return output; + } + + void SendGeometryAndModeChangesAndSync(const gfx::Rect& new_rect, + wl_resource* resource) { + wl_output_send_geometry(output_->resource(), new_rect.x(), new_rect.y(), + 0 /* physical_width */, 0 /* physical_height */, + 0 /* subpixel */, "unkown_make", "unknown_model", + 0 /* transform */); + wl_output_send_mode(output_->resource(), WL_OUTPUT_MODE_CURRENT, + new_rect.width(), new_rect.height(), 0 /* refresh */); + + Sync(); + } + + std::unique_ptr CreateWaylandWindowAndSync( + const gfx::Rect& bounds, + PlatformWindowType window_type, + gfx::AcceleratedWidget parent_widget, + MockPlatformWindowDelegate* delegate) { + auto window = std::make_unique(delegate, connection_.get()); + gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_)) + .WillOnce(SaveArg<0>(&widget)); + + PlatformWindowInitProperties properties; + properties.bounds = bounds; + properties.type = window_type; + properties.parent_widget = parent_widget; + EXPECT_TRUE(window->Initialize(std::move(properties))); + EXPECT_NE(widget, gfx::kNullAcceleratedWidget); + + Sync(); + + return window; + } + wl::MockOutput* output_ = nullptr; WaylandOutputManager* output_manager_ = nullptr; + std::unique_ptr platform_screen_; + private: DISALLOW_COPY_AND_ASSIGN(WaylandScreenTest); }; @@ -82,36 +150,32 @@ class WaylandScreenTest : public WaylandTest { // Tests whether a primary output has been initialized before PlatformScreen is // created. TEST_P(WaylandScreenTest, OutputBaseTest) { - EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); - - std::unique_ptr platform_screen = - output_manager_->CreateWaylandScreen(); + // IsPrimaryOutputReady and PlatformScreen creation is done in the + // initialization part of the tests. // Ensure there is only one display, which is the primary one. - auto& all_displays = platform_screen->GetAllDisplays(); + auto& all_displays = platform_screen_->GetAllDisplays(); EXPECT_EQ(all_displays.size(), kNumberOfDisplays); // Ensure the size property of the primary display. - EXPECT_EQ(platform_screen->GetPrimaryDisplay().bounds(), + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds(), gfx::Rect(0, 0, kOutputWidth, kOutputHeight)); } TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { - EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); - std::unique_ptr platform_screen = - output_manager_->CreateWaylandScreen(); - TestDisplayObserver observer; - platform_screen->AddObserver(&observer); + platform_screen_->AddObserver(&observer); // Add a second display. - server_.CreateAndInitializeOutput(); + const gfx::Rect rect(gfx::Point(output_->GetRect().width(), 0), + output_->GetRect().size()); + CreateOutputWithRectAndSync(rect); Sync(); // Ensure that second display is not a primary one and have a different id. int64_t added_display_id = observer.GetDisplay().id(); - EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id); + EXPECT_NE(platform_screen_->GetPrimaryDisplay().id(), added_display_id); // Remove the second output. output_manager_->RemoveWaylandOutput(added_display_id); @@ -123,41 +187,30 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { EXPECT_EQ(added_display_id, removed_display_id); // Create another display again. - server_.CreateAndInitializeOutput(); - - Sync(); + CreateOutputWithRectAndSync(rect); // The newly added display is not a primary yet. added_display_id = observer.GetDisplay().id(); - EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id); + EXPECT_NE(platform_screen_->GetPrimaryDisplay().id(), added_display_id); // Make sure the geometry changes are sent by syncing one more time again. Sync(); - int64_t old_primary_display_id = platform_screen->GetPrimaryDisplay().id(); + int64_t old_primary_display_id = platform_screen_->GetPrimaryDisplay().id(); output_manager_->RemoveWaylandOutput(old_primary_display_id); // Ensure that previously added display is now a primary one. - EXPECT_EQ(platform_screen->GetPrimaryDisplay().id(), added_display_id); + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().id(), added_display_id); // Ensure that the removed display was the one, which was a primary display. EXPECT_EQ(observer.GetDisplay().id(), old_primary_display_id); } TEST_P(WaylandScreenTest, OutputPropertyChanges) { - std::unique_ptr platform_screen = - output_manager_->CreateWaylandScreen(); TestDisplayObserver observer; - platform_screen->AddObserver(&observer); + platform_screen_->AddObserver(&observer); const gfx::Rect new_rect(0, 0, 800, 600); - wl_output_send_geometry(output_->resource(), new_rect.x(), new_rect.y(), - 0 /* physical_width */, 0 /* physical_height */, - 0 /* subpixel */, "unkown_make", "unknown_model", - 0 /* transform */); - wl_output_send_mode(output_->resource(), WL_OUTPUT_MODE_CURRENT, - new_rect.width(), new_rect.height(), 0 /* refresh */); - - Sync(); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); uint32_t changed_values = 0; changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS; @@ -177,6 +230,203 @@ TEST_P(WaylandScreenTest, OutputPropertyChanges) { EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value); } +TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + // Add a test window and get an accelerated widget for it. + MockPlatformWindowDelegate delegate; + std::unique_ptr window = CreateWaylandWindowAndSync( + gfx::Rect(0, 0, 640, 480), PlatformWindowType::kWindow, + gfx::kNullAcceleratedWidget, &delegate); + + gfx::AcceleratedWidget widget = window->GetWidget(); + + // There must be a primary display used if the window has not entered a + // surface yet (very unlikely in the production). + int64_t expected_display_id = platform_screen_->GetPrimaryDisplay().id(); + gfx::Point expected_origin = + platform_screen_->GetPrimaryDisplay().bounds().origin(); + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); + + Sync(); + + // Get the surface of that window. + wl::MockSurface* surface = server_.GetObject(widget); + ASSERT_TRUE(surface); + + // Now, send enter event for the surface, which was created before. + wl_surface_send_enter(surface->resource(), output_->resource()); + + Sync(); + + // The id of the entered display must still correspond to the primary output. + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); + + // Create one more output. Make sure two output are placed next to each other. + const gfx::Rect new_rect(gfx::Point(output_->GetRect().width(), 0), + output_->GetRect().size()); + wl::MockOutput* output2 = CreateOutputWithRectAndSync(new_rect); + + Sync(); + + const display::Display display2 = observer.GetDisplay(); + + // Enter the second output now. + wl_surface_send_enter(surface->resource(), output2->resource()); + + Sync(); + + // The id of the entered display must still correspond to the primary output. + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); + + // Leave the first output. + wl_surface_send_leave(surface->resource(), output_->resource()); + + Sync(); + + // The id and the origin of the display for widget must corresponds to the + // second display now. + expected_display_id = display2.id(); + expected_origin = display2.bounds().origin(); + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); +} + +TEST_P(WaylandScreenTest, GetAcceleratedWidgetAtScreenPoint) { + MockPlatformWindowDelegate delegate; + std::unique_ptr menu_window = CreateWaylandWindowAndSync( + gfx::Rect(window_->GetBounds().width() - 10, + window_->GetBounds().height() - 10, 100, 100), + PlatformWindowType::kPopup, window_->GetWidget(), &delegate); + + // If there is no focused window (focus is set whenever a pointer enters any + // of the windows), there must be kNullAcceleratedWidget returned. There is no + // real way to determine what window is located on a certain screen point in + // Wayland. + gfx::AcceleratedWidget widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point(10, 10)); + EXPECT_EQ(widget_at_screen_point, gfx::kNullAcceleratedWidget); + + // Set a focus to the main window. + window_->set_pointer_focus(true); + widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point(10, 10)); + EXPECT_EQ(widget_at_screen_point, window_->GetWidget()); + + // Imagine the mouse enters a menu window, which is located on top of the main + // window, and gathers focus. + window_->set_pointer_focus(false); + menu_window->set_pointer_focus(true); + widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point( + menu_window->GetBounds().x() + 1, menu_window->GetBounds().y() + 1)); + EXPECT_EQ(widget_at_screen_point, menu_window->GetWidget()); + + // Despite the menu window being focused, the accelerated widget at origin + // must be the parent widget. + widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point(0, 0)); + EXPECT_EQ(widget_at_screen_point, window_->GetWidget()); + + menu_window->set_pointer_focus(false); +} + +TEST_P(WaylandScreenTest, DefaultDisplayForNonExistingWidget) { + display::Display default_display = + platform_screen_->GetDisplayForAcceleratedWidget( + gfx::kNullAcceleratedWidget); + EXPECT_EQ(default_display.id(), platform_screen_->GetPrimaryDisplay().id()); +} + +TEST_P(WaylandScreenTest, GetDisplayNearestPoint) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + const int64_t first_display_id = platform_screen_->GetPrimaryDisplay().id(); + + // Prepare the first output. + const gfx::Rect new_rect(gfx::Rect(0, 0, 640, 480)); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); + + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds().ToString(), + new_rect.ToString()); + + const gfx::Rect output2_new_rect(640, 0, 1024, 768); + CreateOutputWithRectAndSync(output2_new_rect); + + const display::Display second_display = observer.GetDisplay(); + const int64_t second_display_id = second_display.id(); + EXPECT_EQ(second_display.bounds().ToString(), output2_new_rect.ToString()); + + EXPECT_EQ(first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(630, 10)).id()); + EXPECT_EQ(second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(650, 10)).id()); + EXPECT_EQ(first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(10, 10)).id()); + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(10000, 10000)).id()); + EXPECT_EQ( + first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(639, -10)).id()); + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(641, -20)).id()); + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(600, 760)).id()); + EXPECT_EQ( + first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(-1000, 760)).id()); +} + +TEST_P(WaylandScreenTest, GetDisplayMatchingBasic) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + // Prepare the first output. + const gfx::Rect new_rect(gfx::Rect(0, 0, 640, 480)); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); + + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds().ToString(), + new_rect.ToString()); + + const gfx::Rect output2_new_rect(640, 0, 1024, 768); + CreateOutputWithRectAndSync(output2_new_rect); + + const display::Display second_display = observer.GetDisplay(); + const int64_t second_display_id = second_display.id(); + EXPECT_EQ(second_display.bounds().ToString(), output2_new_rect.ToString()); + + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayMatching(gfx::Rect(700, 20, 100, 100)).id()); +} + +TEST_P(WaylandScreenTest, GetDisplayMatchingOverlap) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + // Prepare the first output. + const gfx::Rect new_rect(gfx::Rect(0, 0, 640, 480)); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); + + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds().ToString(), + new_rect.ToString()); + + const gfx::Rect output2_new_rect(640, 0, 1024, 768); + CreateOutputWithRectAndSync(output2_new_rect); + + const display::Display second_display = observer.GetDisplay(); + const int64_t second_display_id = second_display.id(); + EXPECT_EQ(second_display.bounds().ToString(), output2_new_rect.ToString()); + + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayMatching(gfx::Rect(630, 20, 100, 100)).id()); +} + INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, WaylandScreenTest, ::testing::Values(kXdgShellV5)); diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc index c6b89e43d0d5b..3127fe2293e80 100644 --- a/ui/ozone/platform/wayland/wayland_window.cc +++ b/ui/ozone/platform/wayland/wayland_window.cc @@ -15,6 +15,7 @@ #include "ui/events/ozone/events_ozone.h" #include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_pointer.h" #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v5.h" #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v6.h" @@ -87,7 +88,7 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, WaylandWindow::~WaylandWindow() { PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - connection_->RemoveWindow(surface_.id()); + connection_->RemoveWindow(GetWidget()); if (parent_window_) parent_window_->set_child_window(nullptr); @@ -115,6 +116,8 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { } wl_surface_set_user_data(surface_.get(), this); + AddSurfaceListener(); + ui::PlatformWindowType ui_window_type = properties.type; switch (ui_window_type) { case ui::PlatformWindowType::kMenu: @@ -135,13 +138,19 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { connection_->ScheduleFlush(); - connection_->AddWindow(surface_.id(), this); + connection_->AddWindow(GetWidget(), this); PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - delegate_->OnAcceleratedWidgetAvailable(surface_.id()); + delegate_->OnAcceleratedWidgetAvailable(GetWidget()); return true; } +gfx::AcceleratedWidget WaylandWindow::GetWidget() { + if (!surface_) + return gfx::kNullAcceleratedWidget; + return surface_.id(); +} + void WaylandWindow::CreateXdgPopup() { if (bounds_.IsEmpty()) return; @@ -645,4 +654,49 @@ WmDragHandler* WaylandWindow::AsWmDragHandler() { return static_cast(this); } +void WaylandWindow::AddSurfaceListener() { + static struct wl_surface_listener surface_listener = { + &WaylandWindow::Enter, &WaylandWindow::Leave, + }; + wl_surface_add_listener(surface_.get(), &surface_listener, this); +} + +void WaylandWindow::SetEnteredOutputId(struct wl_output* output) { + const uint32_t entered_output_id = + connection_->wayland_output_manager()->GetIdForOutput(output); + DCHECK_NE(entered_output_id, 0u); + entered_outputs_ids_.push_back(entered_output_id); +} + +void WaylandWindow::RemoveEnteredOutputId(struct wl_output* output) { + const uint32_t left_output_id = + connection_->wayland_output_manager()->GetIdForOutput(output); + auto entered_output_id_it = std::find( + entered_outputs_ids_.begin(), entered_outputs_ids_.end(), left_output_id); + DCHECK(entered_output_id_it != entered_outputs_ids_.end()); + entered_outputs_ids_.erase(entered_output_id_it); +} + +// static +void WaylandWindow::Enter(void* data, + struct wl_surface* wl_surface, + struct wl_output* output) { + WaylandWindow* window = static_cast(data); + if (window) { + DCHECK(window->surface() == wl_surface); + window->SetEnteredOutputId(output); + } +} + +// static +void WaylandWindow::Leave(void* data, + struct wl_surface* wl_surface, + struct wl_output* output) { + WaylandWindow* window = static_cast(data); + if (window) { + DCHECK(window->surface() == wl_surface); + window->RemoveEnteredOutputId(output); + } +} + } // namespace ui diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h index bd0f4d2635477..537ffa8ad4b17 100644 --- a/ui/ozone/platform/wayland/wayland_window.h +++ b/ui/ozone/platform/wayland/wayland_window.h @@ -51,6 +51,8 @@ class WaylandWindow : public PlatformWindow, XDGSurfaceWrapper* xdg_surface() const { return xdg_surface_.get(); } XDGPopupWrapper* xdg_popup() const { return xdg_popup_.get(); } + gfx::AcceleratedWidget GetWidget(); + // 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(); @@ -71,6 +73,8 @@ class WaylandWindow : public PlatformWindow, // xdg_popups as long as they must be destroyed in the back order. void set_child_window(WaylandWindow* window) { child_window_ = window; } + WaylandWindow* parent_window() const { return parent_window_; } + // Set whether this window has an implicit grab (often referred to as capture // in Chrome code). Implicit grabs happen while a pointer is down. void set_has_implicit_grab(bool value) { has_implicit_grab_ = value; } @@ -82,6 +86,10 @@ class WaylandWindow : public PlatformWindow, void DispatchHostWindowDragMovement( int hittest, const gfx::Point& pointer_location) override; + + const std::vector& entered_outputs_ids() const { + return entered_outputs_ids_; + } // WmDragHandler void StartDrag(const ui::OSExchangeData& data, @@ -153,6 +161,20 @@ class WaylandWindow : public PlatformWindow, WmMoveResizeHandler* AsWmMoveResizeHandler(); WmDragHandler* AsWmDragHandler(); + + // Starts getting update when the current surface enters or leaves wl_outputs. + void AddSurfaceListener(); + + void SetEnteredOutputId(struct wl_output* output); + void RemoveEnteredOutputId(struct wl_output* output); + + // wl_surface_listener + static void Enter(void* data, + struct wl_surface* wl_surface, + struct wl_output* output); + static void Leave(void* data, + struct wl_surface* wl_surface, + struct wl_output* output); PlatformWindowDelegate* delegate_; WaylandConnection* connection_; @@ -193,6 +215,9 @@ class WaylandWindow : public PlatformWindow, bool is_tooltip_ = false; + // The ids of the outputs this surface is shown on. + std::vector entered_outputs_ids_; + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); }; From e832a2c8d8115f077a19ba6d77e43669a0aac044 Mon Sep 17 00:00:00 2001 From: Julie Jeongeun Kim Date: Wed, 10 Oct 2018 16:44:29 +0900 Subject: [PATCH 17/17] fixup! Implements Drag and Drop for Ozone/X11 It adds APIs to handle drop action at WmDropHandler, implements them at DesktopDragDropClientOzone and connects them to Ozone/X11. --- ui/aura/window_tree_host.cc | 10 +- ui/aura/window_tree_host.h | 4 + ui/ozone/platform/x11/x11_window_ozone.cc | 85 +++++++--- ui/ozone/platform/x11/x11_window_ozone.h | 14 +- .../platform_window_handler/wm_drop_handler.h | 29 ++++ ui/platform_window/x11/x11_window_base.cc | 9 -- ui/platform_window/x11/x11_window_base.h | 8 +- .../desktop_drag_drop_client_ozone.cc | 152 +++++++++++++++++- .../desktop_drag_drop_client_ozone.h | 37 +++++ 9 files changed, 298 insertions(+), 50 deletions(-) diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc index 639b64d7f8438..9b5fdae74c97a 100644 --- a/ui/aura/window_tree_host.cc +++ b/ui/aura/window_tree_host.cc @@ -179,9 +179,15 @@ void WindowTreeHost::ConvertScreenInPixelsToDIP(gfx::Point* point) const { } void WindowTreeHost::ConvertDIPToPixels(gfx::Point* point) const { - auto point_3f = gfx::Point3F(gfx::PointF(*point)); + gfx::PointF pointf = gfx::PointF(*point); + ConvertDIPToPixels(&pointf); + *point = gfx::ToFlooredPoint(pointf); +} + +void WindowTreeHost::ConvertDIPToPixels(gfx::PointF* point) const { + auto point_3f = gfx::Point3F(*point); GetRootTransform().TransformPoint(&point_3f); - *point = gfx::ToFlooredPoint(point_3f.AsPointF()); + *point = point_3f.AsPointF(); } void WindowTreeHost::ConvertPixelsToDIP(gfx::Point* point) const { diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h index 62954bf8c0297..d2ccb608961e3 100644 --- a/ui/aura/window_tree_host.h +++ b/ui/aura/window_tree_host.h @@ -123,6 +123,10 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, // host window's. virtual void ConvertDIPToPixels(gfx::Point* point) const; + // Converts |pointf| from the root window's coordinate system to the + // host window's. + virtual void ConvertDIPToPixels(gfx::PointF* pointf) const; + // Converts |point| from the host window's coordinate system to the // root window's. virtual void ConvertPixelsToDIP(gfx::Point* point) const; diff --git a/ui/ozone/platform/x11/x11_window_ozone.cc b/ui/ozone/platform/x11/x11_window_ozone.cc index 5dcc3e05309ea..9e000e6a0f793 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.cc +++ b/ui/ozone/platform/x11/x11_window_ozone.cc @@ -19,6 +19,7 @@ #include "ui/ozone/platform/x11/x11_drag_source.h" #include "ui/ozone/platform/x11/x11_drag_util.h" #include "ui/ozone/platform/x11/x11_window_manager_ozone.h" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" namespace ui { @@ -100,6 +101,9 @@ X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager, // 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, AsWmDragHandler()); } X11WindowOzone::~X11WindowOzone() { @@ -214,23 +218,35 @@ void X11WindowOzone::OnLostCapture() { void X11WindowOzone::OnDragDataCollected(const gfx::PointF& screen_point, std::unique_ptr data, int operation) { - // TODO(jkim). - // delegate()->OnDragEnter(this, screen_point, std::move(data), operation); - NOTIMPLEMENTED_LOG_ONCE(); + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return; + } + drop_handler->OnDragEnter(this, screen_point, std::move(data), operation); } void X11WindowOzone::OnMouseMoved(const gfx::Point& point, gfx::AcceleratedWidget* widget) { - // TODO(jkim). - // delegate()->OnMouseMoved(point, widget); - NOTIMPLEMENTED_LOG_ONCE(); + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return; + } + + drop_handler->OnMouseMoved(point, widget); } void X11WindowOzone::OnDragSessionClose(int dnd_action) { - // TODO(jkim). - // drag_source_.reset(); - // delegate()->OnDragSessionClosed(dnd_action); - NOTIMPLEMENTED_LOG_ONCE(); + drag_source_.reset(); + + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return; + } + + drop_handler->OnDragSessionClosed(dnd_action); } void X11WindowOzone::DispatchHostWindowDragMovement( @@ -267,11 +283,14 @@ void X11WindowOzone::OnDragMotion(const gfx::PointF& screen_point, int flags, ::Time event_time, int operation) { - // TODO(jkim). - // gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; - // drag_operation_ = - // delegate()->OnDragMotion(screen_point, event_time, operation, &widget); - NOTIMPLEMENTED_LOG_ONCE(); + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return; + } + gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + drag_operation_ = + drop_handler->OnDragMotion(screen_point, event_time, operation, &widget); } bool X11WindowOzone::ProcessDragDropEvent(XEvent* xev) { @@ -337,9 +356,12 @@ bool X11WindowOzone::ProcessDragDropEvent(XEvent* xev) { return true; } if (message_type == gfx::GetAtom(kXdndLeave)) { - // TODO(jkim). - // delegate()->OnDragLeave(); - NOTIMPLEMENTED_LOG_ONCE(); + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return false; + } + drop_handler->OnDragLeave(); return true; } if (message_type == gfx::GetAtom(kXdndPosition)) { @@ -356,16 +378,25 @@ bool X11WindowOzone::ProcessDragDropEvent(XEvent* xev) { return true; } if (message_type == gfx::GetAtom(kXdndFinished)) { - // TODO(jkim). - // int negotiated_operation = drag_source_->negotiated_operation(); - // drag_source_->OnXdndFinished(event); - // delegate()->OnDragSessionClosed(negotiated_operation); - NOTIMPLEMENTED_LOG_ONCE(); + int negotiated_operation = drag_source_->negotiated_operation(); + drag_source_->OnXdndFinished(event); + + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return false; + } + + drop_handler->OnDragSessionClosed(negotiated_operation); return true; } if (message_type == gfx::GetAtom(kXdndDrop)) { - // TODO(jkim). - // delegate()->OnDragDrop(nullptr); + WmDropHandler* drop_handler = GetWmDropHandler(*delegate()); + if (!drop_handler) { + LOG(ERROR) << "Failed to get drag handler."; + return false; + } + drop_handler->OnDragDrop(nullptr); return false; if (!target_current_context_.get()) { @@ -388,4 +419,8 @@ WmMoveResizeHandler* X11WindowOzone::AsWmMoveResizeHandler() { return static_cast(this); } +WmDragHandler* X11WindowOzone::AsWmDragHandler() { + return static_cast(this); +} + } // namespace ui diff --git a/ui/ozone/platform/x11/x11_window_ozone.h b/ui/ozone/platform/x11/x11_window_ozone.h index 4dd2a6877a4bf..24d255e2715e3 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.h +++ b/ui/ozone/platform/x11/x11_window_ozone.h @@ -8,6 +8,7 @@ #include "base/macros.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/x11/x11_event_source_libevent.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/platform_window/x11/window_move_loop_client.h" #include "ui/platform_window/x11/x11_window_base.h" @@ -23,7 +24,8 @@ class X11WindowManagerOzone; class X11WindowOzone : public X11WindowBase, public PlatformEventDispatcher, public XEventDispatcher, - public WmMoveResizeHandler { + public WmMoveResizeHandler, + public WmDragHandler { public: X11WindowOzone(X11WindowManagerOzone* window_manager, PlatformWindowDelegate* delegate, @@ -39,9 +41,6 @@ class X11WindowOzone : public X11WindowBase, void ReleaseCapture() override; void SetCursor(PlatformCursor cursor) override; - void StartDrag(const ui::OSExchangeData& data, - const int operation, - gfx::NativeCursor cursor) override; bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; void StopMoveLoop() override; @@ -67,6 +66,11 @@ class X11WindowOzone : public X11WindowBase, int hittest, const gfx::Point& pointer_location) override; + // WmDragHandler + void StartDrag(const ui::OSExchangeData& data, + const int operation, + gfx::NativeCursor cursor) override; + private: enum SourceState { // |source_current_window_| will receive a drop once we receive an @@ -89,6 +93,8 @@ class X11WindowOzone : public X11WindowBase, WmMoveResizeHandler* AsWmMoveResizeHandler(); + WmDragHandler* AsWmDragHandler(); + X11WindowManagerOzone* window_manager_; // TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. diff --git a/ui/platform_window/platform_window_handler/wm_drop_handler.h b/ui/platform_window/platform_window_handler/wm_drop_handler.h index 54cfa49b7b92d..b49aa940249c5 100644 --- a/ui/platform_window/platform_window_handler/wm_drop_handler.h +++ b/ui/platform_window/platform_window_handler/wm_drop_handler.h @@ -7,6 +7,10 @@ #include "ui/platform_window/platform_window_handler/wm_platform_export.h" +namespace gfx { +class PointF; +} + namespace ui { class PlatformWindowDelegate; @@ -17,6 +21,31 @@ class WmDropHandler { // operation. virtual void OnDragSessionClosed(int operation) = 0; + // Notifies that dragging is entered to |window|. + virtual void OnDragEnter(ui::PlatformWindow* window, + const gfx::PointF& point, + std::unique_ptr data, + int operation) = 0; + + // Notifies that dragging is moved. |widget| will be set with the + // widget located at |point|. It returns the operation selected by client. + virtual int OnDragMotion(const gfx::PointF& point, + uint32_t time, + int operation, + gfx::AcceleratedWidget* widget) = 0; + + // Notifies that dragged data is dropped. When it doesn't deliver + // the dragged data on OnDragEnter, it should put it to |data|. + virtual void OnDragDrop(std::unique_ptr data) = 0; + + // Notifies that dragging is left. + virtual void OnDragLeave() = 0; + + // Notifies that mouse is moved generally and expects that + // |widget| is filled with the widget located at |point|. + virtual void OnMouseMoved(const gfx::Point& point, + gfx::AcceleratedWidget* widget) = 0; + protected: virtual ~WmDropHandler() {} }; diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc index e39017bd4711e..1887b3b377a8a 100644 --- a/ui/platform_window/x11/x11_window_base.cc +++ b/ui/platform_window/x11/x11_window_base.cc @@ -354,15 +354,6 @@ bool X11WindowBase::RunMoveLoop(const gfx::Vector2d& drag_offset) { void X11WindowBase::StopMoveLoop() {} -void X11WindowBase::StartDrag(const ui::OSExchangeData& data, - const int operation, - gfx::NativeCursor cursor) { - // If this is for ozone/X11, the child class of this, X11WindowOzone, will - // handle it. Otherwise, DragDrop has been implemented directly, for instance - // DesktopDragDropClientAuraX11 or DragDropControllerMus. - NOTREACHED(); -} - void X11WindowBase::UnConfineCursor() { if (!has_pointer_barriers_) return; diff --git a/ui/platform_window/x11/x11_window_base.h b/ui/platform_window/x11/x11_window_base.h index 8051a5e378b2d..a670a86409ed4 100644 --- a/ui/platform_window/x11/x11_window_base.h +++ b/ui/platform_window/x11/x11_window_base.h @@ -27,17 +27,11 @@ class XScopedEventSelector; // Abstract base implementation for a X11 based PlatformWindow. Methods that // are platform specific are left unimplemented. -class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow, - public WmDragHandler { +class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { public: X11WindowBase(PlatformWindowDelegate* delegate, const gfx::Rect& bounds); ~X11WindowBase() override; - // Initiates Drag Action. - void StartDrag(const ui::OSExchangeData& data, - const int operation, - gfx::NativeCursor cursor) override; - // PlatformWindow: void Show() override; void Hide() override; diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc index 13c5ce44d05d7..fe05c1e0196ba 100644 --- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc @@ -5,10 +5,16 @@ #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h" #include "base/run_loop.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data_provider_aura.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_handler/wm_drag_handler.h" @@ -16,6 +22,16 @@ namespace views { +namespace { + +aura::Window* GetTargetWindow(aura::Window* root_window, const gfx::Point& point) { + gfx::Point root_location(point); + root_window->GetHost()->ConvertScreenInPixelsToDIP(&root_location); + return root_window->GetEventHandlerForPoint(root_location); +} + +} // namespace + DesktopDragDropClientOzone::DesktopDragDropClientOzone( aura::Window* root_window, views::DesktopNativeCursorManager* cursor_manager, @@ -23,12 +39,20 @@ DesktopDragDropClientOzone::DesktopDragDropClientOzone( ui::PlatformWindowDelegate* delegate) : root_window_(root_window), cursor_manager_(cursor_manager), - drag_handler_(drag_handler) { + drag_handler_(drag_handler), + target_window_(nullptr), + delegate_(nullptr), + os_exchange_data_(nullptr) { // Set a class property key, which allows |this| to be used for drop action. SetWmDropHandler(delegate, AsWmDropHandler()); } -DesktopDragDropClientOzone::~DesktopDragDropClientOzone() = default; +DesktopDragDropClientOzone::~DesktopDragDropClientOzone() { + if (target_window_) { + target_window_->RemoveObserver(this); + target_window_ = nullptr; + } +} int DesktopDragDropClientOzone::StartDragAndDrop( const ui::OSExchangeData& data, @@ -85,7 +109,10 @@ void DesktopDragDropClientOzone::RemoveObserver( } void DesktopDragDropClientOzone::OnWindowDestroyed(aura::Window* window) { - NOTIMPLEMENTED(); + if (target_window_ == window) { + target_window_->RemoveObserver(this); + target_window_ = nullptr; + } } void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) { @@ -94,6 +121,118 @@ void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) { DragDropSessionCompleted(); } +void DesktopDragDropClientOzone::OnDragEnter( + ui::PlatformWindow* window, + const gfx::PointF& point, + std::unique_ptr data, + int operation) { + DCHECK(window); + + point_ = point; + drag_operation_ = operation; + UpdateTargetWindowAndDelegate(point_); + + if (!data) + return; + + os_exchange_data_ = std::move(data); + std::unique_ptr event = CreateDropTargetEvent(); + if (delegate_ && event) + delegate_->OnDragEntered(*event); +} + +void DesktopDragDropClientOzone::OnDragLeave() { + if (delegate_) + delegate_->OnDragExited(); +} + +int DesktopDragDropClientOzone::OnDragMotion(const gfx::PointF& point, + uint32_t time, + int operation, + gfx::AcceleratedWidget* widget) { + point_ = point; + *widget = UpdateTargetWindowAndDelegate(point_); + drag_operation_ = operation; + int client_operation = + ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; + // If it has valid data in |os_exchange_data_|, it gets operation. + if (os_exchange_data_) { + std::unique_ptr event = CreateDropTargetEvent(); + if (event) + client_operation = delegate_->OnDragUpdated(*event); + } + return client_operation; +} + +void DesktopDragDropClientOzone::OnDragDrop( + std::unique_ptr data) { + // If it doesn't has |os_exchange_data_|, it needs to update it with |data|. + if (!os_exchange_data_) { + DCHECK(data); + os_exchange_data_ = std::move(data); + std::unique_ptr event = CreateDropTargetEvent(); + if (event) { + delegate_->OnDragEntered(*event); + delegate_->OnDragUpdated(*event); + } + } + PerformDrop(); +} + +void DesktopDragDropClientOzone::OnMouseMoved(const gfx::Point& point, + gfx::AcceleratedWidget* widget) { + DCHECK(widget); + aura::Window* target_window = GetTargetWindow(root_window_, point); + if (!target_window) { + LOG(ERROR) << "Failed to find target_window at " << point.ToString(); + return; + } + *widget = target_window->GetHost()->GetAcceleratedWidget(); +} + +std::unique_ptr +DesktopDragDropClientOzone::CreateDropTargetEvent( + const gfx::PointF& root_location) const { + if (!target_window_) { + LOG(ERROR) << "Invalid target window"; + return nullptr; + } + gfx::PointF target_location = root_location; + target_window_->GetHost()->ConvertDIPToPixels(&target_location); + aura::Window::ConvertPointToTarget(root_window_, target_window_, + &target_location); + return std::make_unique( + *os_exchange_data_, target_location, root_location, drag_operation_); +} + +std::unique_ptr +DesktopDragDropClientOzone::CreateDropTargetEvent() const { + gfx::Point root_location(point_.x(), point_.y()); + root_window_->GetHost()->ConvertScreenInPixelsToDIP(&root_location); + return CreateDropTargetEvent(gfx::PointF(root_location)); +} + +gfx::AcceleratedWidget +DesktopDragDropClientOzone::UpdateTargetWindowAndDelegate(gfx::PointF& pointf) { + const gfx::Point point(pointf.x(), pointf.y()); + aura::Window* target_window = GetTargetWindow(root_window_, point); + if (target_window_ != target_window) { + aura::client::DragDropDelegate* delegate = nullptr; + if (target_window_) { + target_window_->RemoveObserver(this); + delegate_->OnDragExited(); + } + if (target_window) { + target_window->AddObserver(this); + delegate = aura::client::GetDragDropDelegate(target_window); + } + target_window_ = target_window; + delegate_ = delegate; + } + return target_window ? target_window->GetHost()->GetAcceleratedWidget() + : gfx::kNullAcceleratedWidget; +} + void DesktopDragDropClientOzone::DragDropSessionCompleted() { aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(root_window_); @@ -103,6 +242,13 @@ void DesktopDragDropClientOzone::DragDropSessionCompleted() { cursor_client->SetCursor(initial_cursor_); } +void DesktopDragDropClientOzone::PerformDrop() { + DCHECK(delegate_); + std::unique_ptr event = CreateDropTargetEvent(); + delegate_->OnPerformDrop(*event); + DragDropSessionCompleted(); +} + void DesktopDragDropClientOzone::QuitClosure() { in_move_loop_ = false; if (quit_closure_.is_null()) diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h index 3d90f5bc832ab..ac4721f84c36f 100644 --- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h @@ -11,6 +11,9 @@ #include "ui/base/cursor/cursor.h" #include "ui/platform_window/platform_window_handler/wm_drag_handler.h" #include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" namespace views { @@ -45,18 +48,52 @@ class VIEWS_EXPORT DesktopDragDropClientOzone // Overridden from void ui::WmDropHandler: void OnDragSessionClosed(int operation) override; + void OnDragEnter(ui::PlatformWindow* window, + const gfx::PointF& point, + std::unique_ptr data, + int operation) override; + int OnDragMotion(const gfx::PointF& point, + uint32_t time, + int operation, + gfx::AcceleratedWidget* widget) override; + void OnDragDrop(std::unique_ptr data) override; + void OnDragLeave() override; + void OnMouseMoved(const gfx::Point& point, gfx::AcceleratedWidget* widget) override; private: void DragDropSessionCompleted(); void QuitClosure(); ui::WmDropHandler* AsWmDropHandler(); + // Returns a DropTargetEvent to be passed to the DragDropDelegate, or null to + // abort the drag, using the location of point_ if no root location is passed. + std::unique_ptr CreateDropTargetEvent( + const gfx::PointF& point) const; + std::unique_ptr CreateDropTargetEvent() const; + + // Update |target_window_| and |delegate_| along with |point|. + gfx::AcceleratedWidget UpdateTargetWindowAndDelegate(gfx::PointF& point); + + void PerformDrop(); + aura::Window* root_window_; DesktopNativeCursorManager* cursor_manager_; ui::WmDragHandler* drag_handler_; + // Null unless all drag data has been received and the drag is unfinished. + aura::Window* target_window_; + + // This is the interface that allows us to send drag events from Ozone-Wayland + // to the cross-platform code. + aura::client::DragDropDelegate* delegate_; + + std::unique_ptr os_exchange_data_; + + // The most recent native coordinates of a drag. + gfx::PointF point_; + // Cursor in use prior to the move loop starting. Restored when the move loop // quits. gfx::NativeCursor initial_cursor_;