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. 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/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/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..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); } @@ -475,9 +485,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.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); 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 { 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/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/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/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/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/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/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/linux/gbm_wrapper.cc b/ui/ozone/common/linux/gbm_wrapper.cc index ba55b0e568565..24518cea94330 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,96 @@ 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) { +#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 + // 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 +168,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 +198,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 +244,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 +268,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 +317,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, 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/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/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/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/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/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/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_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); } 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 0d02721728298..3127fe2293e80 100644 --- a/ui/ozone/platform/wayland/wayland_window.cc +++ b/ui/ozone/platform/wayland/wayland_window.cc @@ -15,11 +15,13 @@ #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" #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,11 +81,14 @@ 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() { PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - connection_->RemoveWindow(surface_.id()); + connection_->RemoveWindow(GetWidget()); if (parent_window_) parent_window_->set_child_window(nullptr); @@ -111,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: @@ -131,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; @@ -216,6 +229,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); @@ -422,6 +441,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. @@ -570,7 +595,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 +650,53 @@ WmMoveResizeHandler* WaylandWindow::AsWmMoveResizeHandler() { return static_cast(this); } +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 ad9eb0c983e5e..537ffa8ad4b17 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); @@ -49,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(); @@ -69,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; } @@ -80,6 +86,15 @@ 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, + const int operation, + gfx::NativeCursor cursor) override; // PlatformWindow void Show() override; @@ -103,6 +118,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; @@ -143,6 +160,22 @@ 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_; WaylandWindow* parent_window_ = nullptr; @@ -182,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); }; diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn index 5de8d7166c17a..16cb495ed805c 100644 --- a/ui/ozone/platform/x11/BUILD.gn +++ b/ui/ozone/platform/x11/BUILD.gn @@ -24,6 +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_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", @@ -37,6 +47,7 @@ source_set("x11") { "//gpu/vulkan:buildflags", "//skia", "//ui/base", + "//ui/base/ime", "//ui/base/x", "//ui/display/manager", "//ui/events", @@ -59,7 +70,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/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 944206ff68f22..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/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_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" @@ -73,7 +74,7 @@ class OzonePlatformX11 : public OzonePlatform { std::unique_ptr CreateNativeDisplayDelegate() override { - return std::make_unique(); + return std::make_unique(); } void InitializeUI(const InitParams& params) override { @@ -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_; 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_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_native_display_delegate.cc b/ui/ozone/platform/x11/x11_native_display_delegate.cc new file mode 100644 index 0000000000000..2551b6ed3996e --- /dev/null +++ b/ui/ozone/platform/x11/x11_native_display_delegate.cc @@ -0,0 +1,102 @@ +// 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() { + display_manager_ = std::make_unique(); + display_manager_->SetObserver(this); +} + +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. + 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) { + 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( + 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; +} + +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 new file mode 100644 index 0000000000000..8d9542225e477 --- /dev/null +++ b/ui/ozone/platform/x11/x11_native_display_delegate.h @@ -0,0 +1,65 @@ +// 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/observer_list.h" +#include "ui/display/types/native_display_delegate.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, + public X11DisplayManagerOzone::Observer { + 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; + + // X11DisplayManagerOzone::Observer overrides: + void OnOutputReadyForUse() override; + + private: + bool displays_ready_ = false; + + std::unique_ptr display_manager_; + + base::ObserverList::Unchecked observers_; + + DISALLOW_COPY_AND_ASSIGN(X11NativeDisplayDelegate); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_X11_X11_NATIVE_DISPLAY_DELEGATE_H_ diff --git a/ui/ozone/platform/x11/x11_window_ozone.cc b/ui/ozone/platform/x11/x11_window_ozone.cc index c3f17315009ef..9e000e6a0f793 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.cc +++ b/ui/ozone/platform/x11/x11_window_ozone.cc @@ -5,6 +5,9 @@ #include "ui/ozone/platform/x11/x11_window_ozone.h" #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" #include "ui/events/ozone/events_ozone.h" @@ -12,18 +15,95 @@ #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" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" 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) - : 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) event_source->AddXEventDispatcher(this); + +// TODO(msisov, tonikitoo): Add a dummy implementation for chromeos. +#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); + + // 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() { @@ -49,6 +129,34 @@ 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) + 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); } @@ -65,12 +173,21 @@ bool X11WindowOzone::DispatchXEvent(XEvent* xev) { if (!IsEventForXWindow(*xev)) return false; + if (ProcessDragDropEvent(xev)) + return true; + ProcessXWindowEvent(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) { @@ -98,4 +215,212 @@ void X11WindowOzone::OnLostCapture() { delegate()->OnLostCapture(); } +void X11WindowOzone::OnDragDataCollected(const gfx::PointF& screen_point, + std::unique_ptr data, + int operation) { + 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) { + 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) { + 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( + 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, + int operation) { + 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) { + 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)) { + 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)) { + 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)) { + 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)) { + 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()) { + NOTREACHED(); + return false; + } + target_current_context_->OnXdndDrop(drag_operation_); + target_current_context_.reset(); + return true; + } + break; + } + default: + break; + } + return false; +} + +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 e1586341f19d2..24d255e2715e3 100644 --- a/ui/ozone/platform/x11/x11_window_ozone.h +++ b/ui/ozone/platform/x11/x11_window_ozone.h @@ -8,16 +8,24 @@ #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" namespace ui { +class OSExchangeData; +class X11DragContext; +class X11DragSource; class X11WindowManagerOzone; // PlatformWindow implementation for X11 Ozone. PlatformEvents are ui::Events. class X11WindowOzone : public X11WindowBase, public PlatformEventDispatcher, - public XEventDispatcher { + public XEventDispatcher, + public WmMoveResizeHandler, + public WmDragHandler { public: X11WindowOzone(X11WindowManagerOzone* window_manager, PlatformWindowDelegate* delegate, @@ -30,26 +38,79 @@ 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; void PlatformEventDispatchFinished() override; 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); + + // WmMoveResizeHandler + void DispatchHostWindowDragMovement( + 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 + // 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); + + WmMoveResizeHandler* AsWmMoveResizeHandler(); + + WmDragHandler* AsWmDragHandler(); 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. 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/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/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..b49aa940249c5 --- /dev/null +++ b/ui/platform_window/platform_window_handler/wm_drop_handler.h @@ -0,0 +1,60 @@ +// 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 gfx { +class PointF; +} + +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; + + // 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() {} +}; + +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/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 bcbd46cf382e8..1887b3b377a8a 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,27 @@ 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. + 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_ = XCreateWindow(xdisplay_, xroot_window_, bounds_.x(), bounds_.y(), bounds_.width(), bounds_.height(), @@ -80,6 +102,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 +164,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_); } @@ -170,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; @@ -190,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 @@ -310,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..a670a86409ed4 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 { @@ -50,6 +52,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,8 +62,10 @@ class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { void Destroy(); PlatformWindowDelegate* delegate() { return delegate_; } + XDisplay* xdisplay() { return xdisplay_; } XID xwindow() const { return xwindow_; } + XID xroot_window() const { return xroot_window_; } void UnConfineCursor(); 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..fe05c1e0196ba --- /dev/null +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc @@ -0,0 +1,263 @@ +// 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 "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" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" + +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, + ui::WmDragHandler* drag_handler, + ui::PlatformWindowDelegate* delegate) + : root_window_(root_window), + cursor_manager_(cursor_manager), + 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() { + if (target_window_) { + target_window_->RemoveObserver(this); + target_window_ = nullptr; + } +} + +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) { + if (target_window_ == window) { + target_window_->RemoveObserver(this); + target_window_ = nullptr; + } +} + +void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) { + drag_operation_ = dnd_action; + QuitClosure(); + 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_); + if (!cursor_client) + return; + + 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()) + 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..ac4721f84c36f --- /dev/null +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h @@ -0,0 +1,114 @@ +// 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/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 { +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; + 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_; + + 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_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()); 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..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 @@ -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() { @@ -373,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,