diff options
Diffstat (limited to 'chromium/ui/ozone')
218 files changed, 7249 insertions, 3180 deletions
diff --git a/chromium/ui/ozone/BUILD.gn b/chromium/ui/ozone/BUILD.gn index 8416fb1f493..e788273148e 100644 --- a/chromium/ui/ozone/BUILD.gn +++ b/chromium/ui/ozone/BUILD.gn @@ -90,6 +90,7 @@ component("ozone_base") { "public/ozone_switches.cc", "public/ozone_switches.h", "public/platform_clipboard.h", + "public/platform_gl_egl_utility.cc", "public/platform_gl_egl_utility.h", "public/platform_menu_utils.cc", "public/platform_menu_utils.h", @@ -372,7 +373,7 @@ if (ozone_platform_x11) { buildflag_header("buildflags") { header = "buildflags.h" flags = [ "OZONE_PLATFORM_X11=$ozone_platform_x11" ] - visibility += [ "//ui/gl:gl" ] + visibility += [ "*" ] } group("unittests") { diff --git a/chromium/ui/ozone/DIR_METADATA b/chromium/ui/ozone/DIR_METADATA new file mode 100644 index 00000000000..826e2a2ae84 --- /dev/null +++ b/chromium/ui/ozone/DIR_METADATA @@ -0,0 +1,4 @@ +monorail: { + component: "Internals>Ozone" +} +team_email: "ozone-dev@chromium.org" diff --git a/chromium/ui/ozone/OWNERS b/chromium/ui/ozone/OWNERS index 7288821d96e..ec43a6d2a5d 100644 --- a/chromium/ui/ozone/OWNERS +++ b/chromium/ui/ozone/OWNERS @@ -1,6 +1,3 @@ rjkroege@chromium.org spang@chromium.org alexst@chromium.org - -# TEAM: ozone-dev@chromium.org -# COMPONENT: Internals>Ozone diff --git a/chromium/ui/ozone/common/egl_util.cc b/chromium/ui/ozone/common/egl_util.cc index 56e5ebb152a..d23a6014601 100644 --- a/chromium/ui/ozone/common/egl_util.cc +++ b/chromium/ui/ozone/common/egl_util.cc @@ -12,6 +12,10 @@ #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_implementation.h" +#if BUILDFLAG(USE_OPENGL_APITRACE) +#include <stdlib.h> +#endif + namespace ui { namespace { @@ -93,6 +97,48 @@ bool LoadEGLGLES2Bindings(const base::FilePath& egl_library_path, } gl::SetGLGetProcAddressProc(get_proc_address); + +#if BUILDFLAG(USE_OPENGL_APITRACE) + constexpr char kTraceLibegl[] = "TRACE_LIBEGL"; + constexpr char kTraceLibglesv2[] = "TRACE_LIBGLESV2"; + constexpr char kTraceFile[] = "TRACE_FILE"; + + if (egl_library_path.BaseName().value() != kDefaultEglSoname) { + LOG(ERROR) << "Unsupported EGL library '" + << egl_library_path.BaseName().value() + << "'. egltrace may not work."; + } + if (gles_library_path.BaseName().value() != kDefaultGlesSoname) { + LOG(ERROR) << "Unsupported GLESv2 library '" + << gles_library_path.BaseName().value() + << "'. egltrace may not work."; + } + + // Send correct library names to egttrace. + setenv(kTraceLibegl, egl_library_path.BaseName().value().c_str(), + /*overwrite=*/0); + setenv(kTraceLibglesv2, gles_library_path.BaseName().value().c_str(), + /*overwrite=*/0); +#if defined(OS_CHROMEOS) + setenv(kTraceFile, "/tmp/gltrace.dat", /*overwrite=*/0); +#else + if (!getenv(kTraceFile)) { + LOG(ERROR) << "egltrace requires valid TRACE_FILE environment variable but " + "none were found. Chrome will probably crash."; + } +#endif + + LOG(WARNING) << "Loading egltrace.so with TRACE_LIBEGL=" + << getenv(kTraceLibegl) + << " TRACE_LIBGLESV2=" << getenv(kTraceLibglesv2) + << " TRACE_FILE=" << getenv(kTraceFile); + const base::FilePath::CharType kDefaultTraceSoname[] = + FILE_PATH_LITERAL("egltrace.so"); + base::NativeLibrary trace_library = + base::LoadNativeLibrary(base::FilePath(kDefaultTraceSoname), &error); + gl::AddGLNativeLibrary(trace_library); +#endif + gl::AddGLNativeLibrary(egl_library); gl::AddGLNativeLibrary(gles_library); diff --git a/chromium/ui/ozone/common/features.cc b/chromium/ui/ozone/common/features.cc index e8f6809760a..02f03c4dc14 100644 --- a/chromium/ui/ozone/common/features.cc +++ b/chromium/ui/ozone/common/features.cc @@ -10,7 +10,7 @@ namespace ui { const base::Feature kWaylandOverlayDelegation { "WaylandOverlayDelegation", -#if BUILDFLAG(IS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_LACROS) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT diff --git a/chromium/ui/ozone/common/gl_surface_egl_readback.cc b/chromium/ui/ozone/common/gl_surface_egl_readback.cc index 128d4f02273..1345001111d 100644 --- a/chromium/ui/ozone/common/gl_surface_egl_readback.cc +++ b/chromium/ui/ozone/common/gl_surface_egl_readback.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" +#include "ui/gl/gl_bindings.h" #include "ui/ozone/common/egl_util.h" namespace ui { diff --git a/chromium/ui/ozone/demo/window_manager.cc b/chromium/ui/ozone/demo/window_manager.cc index 611d6bff646..686f88da6ce 100644 --- a/chromium/ui/ozone/demo/window_manager.cc +++ b/chromium/ui/ozone/demo/window_manager.cc @@ -108,13 +108,10 @@ void WindowManager::OnDisplaysAcquired( } } -void WindowManager::OnDisplayConfigured( - const int64_t display_id, - const gfx::Rect& bounds, - const base::flat_map<int64_t, bool>& statuses) { - DCHECK_EQ(statuses.size(), 1UL); - - if (statuses.at(display_id)) { +void WindowManager::OnDisplayConfigured(const int64_t display_id, + const gfx::Rect& bounds, + bool config_success) { + if (config_success) { std::unique_ptr<DemoWindow> window( new DemoWindow(this, renderer_factory_.get(), bounds)); window->Start(); diff --git a/chromium/ui/ozone/demo/window_manager.h b/chromium/ui/ozone/demo/window_manager.h index 0415b01bf39..db65a5b7b05 100644 --- a/chromium/ui/ozone/demo/window_manager.h +++ b/chromium/ui/ozone/demo/window_manager.h @@ -41,7 +41,7 @@ class WindowManager : public display::NativeDisplayObserver { const std::vector<display::DisplaySnapshot*>& displays); void OnDisplayConfigured(const int64_t display_id, const gfx::Rect& bounds, - const base::flat_map<int64_t, bool>& statuses); + bool config_success); // display::NativeDisplayDelegate: void OnConfigurationChanged() override; diff --git a/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc b/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc index 40087309e67..d9c19703f36 100644 --- a/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc +++ b/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc @@ -40,7 +40,7 @@ class GLImageNativePixmapTestDelegate : public GLImageTestDelegateBase { DCHECK(pixmap) << "Offending format: " << gfx::BufferFormatToString(format); if (usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE || usage == gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE || - usage == gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE) { + usage == gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE) { auto client_pixmap = client_native_pixmap_factory_->ImportFromHandle( pixmap->ExportHandle(), size, format, usage); bool mapped = client_pixmap->Map(); @@ -120,14 +120,14 @@ using GLImageBindTestTypes = testing::Types< GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::BufferFormat::YVU_420>, GLImageNativePixmapTestDelegate< - gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE, + gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE, gfx::BufferFormat::YVU_420>, GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::BufferFormat::YUV_420_BIPLANAR>, GLImageNativePixmapTestDelegate<gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gfx::BufferFormat::YUV_420_BIPLANAR>, GLImageNativePixmapTestDelegate< - gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE, + gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE, gfx::BufferFormat::YUV_420_BIPLANAR>, GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::BufferFormat::P010>>; diff --git a/chromium/ui/ozone/platform/cast/DIR_METADATA b/chromium/ui/ozone/platform/cast/DIR_METADATA new file mode 100644 index 00000000000..774785b830c --- /dev/null +++ b/chromium/ui/ozone/platform/cast/DIR_METADATA @@ -0,0 +1,3 @@ +monorail: { + component: "Chromecast" +} diff --git a/chromium/ui/ozone/platform/cast/OWNERS b/chromium/ui/ozone/platform/cast/OWNERS index 58f4b71ee3c..281daa82ed2 100644 --- a/chromium/ui/ozone/platform/cast/OWNERS +++ b/chromium/ui/ozone/platform/cast/OWNERS @@ -1,4 +1,2 @@ halliwell@chromium.org lcwu@chromium.org - -# COMPONENT: Chromecast diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 37a412b4422..5e5c1eb225c 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/chromecast_build.gni") +import("//build/config/chromeos/ui_mode.gni") import("//build/config/linux/pkg_config.gni") import("//build/config/ozone.gni") import("//gpu/vulkan/features.gni") @@ -119,9 +120,11 @@ source_set("gbm") { deps = [ "//base", + "//build:chromeos_buildflags", "//build/config/linux/libdrm", "//gpu/vulkan:buildflags", "//ipc", + "//media:media_buildflags", "//mojo/public/cpp/system", "//services/service_manager/public/cpp", "//skia", @@ -158,7 +161,7 @@ source_set("gbm") { ] } - if (is_chromeos) { + if (is_chromeos_ash) { deps += [ "//ui/base/ime/chromeos" ] } diff --git a/chromium/ui/ozone/platform/drm/DIR_METADATA b/chromium/ui/ozone/platform/drm/DIR_METADATA new file mode 100644 index 00000000000..91a288c1659 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/DIR_METADATA @@ -0,0 +1,3 @@ +monorail: { + component: "OS>Systems>Display" +} diff --git a/chromium/ui/ozone/platform/drm/OWNERS b/chromium/ui/ozone/platform/drm/OWNERS index 5392283e6fe..363a93a5f95 100644 --- a/chromium/ui/ozone/platform/drm/OWNERS +++ b/chromium/ui/ozone/platform/drm/OWNERS @@ -1,4 +1,2 @@ dcastagna@chromium.org dnicoara@chromium.org - -# COMPONENT: OS>Systems>Display diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.cc b/chromium/ui/ozone/platform/drm/common/drm_util.cc index a03a4a94935..0c78e5fd8d5 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util.cc @@ -13,16 +13,23 @@ #include <algorithm> #include <memory> +#include <string> #include <utility> +#include <vector> #include "base/containers/flat_map.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" #include "base/notreached.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/display_mode.h" #include "ui/display/util/display_util.h" #include "ui/display/util/edid_parser.h" +#include "ui/ozone/platform/drm/common/scoped_drm_types.h" namespace ui { @@ -118,6 +125,8 @@ display::DisplayConnectionType GetDisplayType(drmModeConnector* connector) { case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_DVIA: return display::DISPLAY_CONNECTION_TYPE_DVI; + case DRM_MODE_CONNECTOR_VIRTUAL: + // A display on VM is treated as an internal display. case DRM_MODE_CONNECTOR_LVDS: case DRM_MODE_CONNECTOR_eDP: case DRM_MODE_CONNECTOR_DSI: @@ -195,6 +204,20 @@ display::PrivacyScreenState GetPrivacyScreenState(int fd, connector->prop_values[index]); } +std::vector<uint64_t> GetPathTopology(int fd, drmModeConnector* connector) { + ScopedDrmPropertyBlobPtr path_blob = + GetDrmPropertyBlob(fd, connector, "PATH"); + + if (!path_blob) { + DCHECK_GT(connector->connector_id, 0u); + + // The topology is consisted solely of the connector id. + return {base::strict_cast<uint64_t>(connector->connector_id)}; + } + + return ParsePathBlob(*path_blob); +} + bool IsAspectPreserving(int fd, drmModeConnector* connector) { ScopedDrmPropertyPtr property; int index = GetDrmProperty(fd, connector, "scaling mode", &property); @@ -437,6 +460,12 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( const gfx::Size physical_size = gfx::Size(info->connector()->mmWidth, info->connector()->mmHeight); const display::DisplayConnectionType type = GetDisplayType(info->connector()); + uint64_t base_connector_id = 0u; + std::vector<uint64_t> path_topology = GetPathTopology(fd, info->connector()); + if (!path_topology.empty()) { + base_connector_id = path_topology.front(); + path_topology.erase(path_topology.begin()); + } const bool is_aspect_preserving_scaling = IsAspectPreserving(fd, info->connector()); const display::PanelOrientation panel_orientation = @@ -454,7 +483,8 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( const gfx::Size maximum_cursor_size = GetMaximumCursorSize(fd); std::string display_name; - int64_t display_id = display_index; + // Make sure the ID contains non index part. + int64_t display_id = display_index | 0x100; int64_t product_code = display::DisplaySnapshot::kInvalidProductCode; int32_t year_of_manufacture = display::kInvalidYearOfManufacture; bool has_overscan = false; @@ -497,21 +527,13 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( display::DisplaySnapshot::DisplayModeList modes = ExtractDisplayModes(info, active_pixel_size, ¤t_mode, &native_mode); - // TODO(https://crbug.com/1105919): Needed for investigating an issue where - // non-supporting devices broadcast privacy screen support on certain - // displays. - VLOG(1) << "DisplaySnapshot created: display_id=" << display_id - << " type=" << type << " current_mode=" - << (current_mode ? current_mode->ToString() : "nullptr") - << " privacy_screen_state=" << privacy_screen_state; - return std::make_unique<display::DisplaySnapshot>( - display_id, origin, physical_size, type, is_aspect_preserving_scaling, - has_overscan, privacy_screen_state, has_color_correction_matrix, - color_correction_in_linear_space, display_color_space, bits_per_channel, - display_name, sys_path, std::move(modes), panel_orientation, edid, - current_mode, native_mode, product_code, year_of_manufacture, - maximum_cursor_size); + display_id, origin, physical_size, type, base_connector_id, path_topology, + is_aspect_preserving_scaling, has_overscan, privacy_screen_state, + has_color_correction_matrix, color_correction_in_linear_space, + display_color_space, bits_per_channel, display_name, sys_path, + std::move(modes), panel_orientation, edid, current_mode, native_mode, + product_code, year_of_manufacture, maximum_cursor_size); } int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) { @@ -535,6 +557,8 @@ int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) { return DRM_FORMAT_NV12; case gfx::BufferFormat::YVU_420: return DRM_FORMAT_YVU420; + case gfx::BufferFormat::P010: + return DRM_FORMAT_P010; default: NOTREACHED(); return 0; @@ -552,4 +576,64 @@ uint64_t GetEnumValueForName(int fd, int property_id, const char* str) { return 0; } +// Returns a vector that holds the path topology of the display. Returns an +// empty vector upon failure. +// +// A path topology c-string is of the format: +// mst:{DRM_BASE_CONNECTOR_ID#}-{BRANCH_1_PORT#}-...-{BRANCH_N_PORT#}\0 +// +// For example, the display configuration: +// Device <--conn6-- MST1 <--port2-- MST2 <--port1-- Display +// may produce the following topology c-string: +// "mst:6-2-1" +// +// To see how this string is constructed in the DRM: +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/drm_dp_mst_topology.c?h=v5.10-rc3#n2229 +std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob) { + if (!path_blob.length) { + LOG(ERROR) << "PATH property blob is empty."; + return {}; + } + + std::string path_str( + static_cast<char*>(path_blob.data), + base::strict_cast<std::string::size_type>(path_blob.length)); + base::StringPiece path_string_piece(path_str); + path_string_piece = base::TrimString(path_string_piece, std::string("\0", 1u), + base::TRIM_TRAILING); + + const std::string prefix("mst:"); + if (!base::StartsWith(path_string_piece, prefix, + base::CompareCase::SENSITIVE)) { + LOG(ERROR) << "Invalid PATH string prefix. Does not contain '" << prefix + << "'. Input: '" << path_str << "'"; + return {}; + } + path_string_piece.remove_prefix(prefix.length()); + + std::vector<uint64_t> path; + for (const auto& string_port : + base::SplitStringPiece(path_string_piece, "-", base::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL)) { + uint64_t int_port = 0; + if (base::StringToUint64(string_port, &int_port) && int_port > 0) { + path.push_back(int_port); + } else { + LOG(ERROR) + << "One or more port values in the PATH string are invalid. Input: '" + << path_str << "'"; + return {}; + } + } + + if (path.size() < 2) { + LOG(ERROR) + << "Insufficient number of ports (should be at least 2 but found " + << path.size() << "). Input: '" << path_str << "'"; + return {}; + } + + return path; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.h b/chromium/ui/ozone/platform/drm/common/drm_util.h index c8fc1b71124..f86e91fdeda 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.h +++ b/chromium/ui/ozone/platform/drm/common/drm_util.h @@ -100,6 +100,8 @@ bool ModeIsInterlaced(const drmModeModeInfo& mode); uint64_t GetEnumValueForName(int fd, int property_id, const char* str); +std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob); + } // namespace ui #endif // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_ diff --git a/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc b/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc index a0144bf0b10..3be4bc957f4 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc @@ -16,6 +16,7 @@ #include "ui/display/types/display_snapshot.h" #include "ui/display/util/edid_parser.h" #include "ui/gfx/geometry/size.h" +#include "ui/ozone/platform/drm/common/scoped_drm_types.h" namespace ui { @@ -180,4 +181,176 @@ TEST_F(DrmUtilTest, TestDisplayModesExtraction) { EXPECT_EQ(extracted_modes[1].get(), native_mode); } +TEST(PathBlobParser, InvalidBlob) { + char data[] = "this doesn't matter"; + drmModePropertyBlobRes blob{1, 0, data}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); +} + +TEST(PathBlobParser, EmptyOrNullString) { + { + char empty[] = ""; + drmModePropertyBlobRes blob{1, sizeof(empty), empty}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char null[] = "\0"; + drmModePropertyBlobRes blob{1, sizeof(null), null}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } +} + +TEST(PathBlobParser, InvalidPathFormat) { + // Space(s) + { + char s[] = " "; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = " "; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // Missing colon + { + char s[] = "mst6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // Caps 'mst:' + { + char s[] = "MST:6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // Subset of "mst:" + { + char s[] = "ms"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // No 'mst:' + { + char s[] = "6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // Other colon-delimited prefix + { + char s[] = "path:6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // Invalid port number or format + { + char s[] = "mst:"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst::6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:-6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6-2-1-"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:c7"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6-b-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6--2"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:---"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6- -2- -1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6 -2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + { + char s[] = "mst:6-'2'-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } + + // Null character + { + char s[] = "mst:6-\0-2-1"; + drmModePropertyBlobRes blob{1, sizeof(s), s}; + EXPECT_TRUE(ParsePathBlob(blob).empty()); + } +} + +TEST(PathBlobParser, ValidPathFormat) { + std::vector<uint64_t> expected = {6u, 2u, 1u}; + + { + char valid[] = "mst:6-2-1"; + drmModePropertyBlobRes blob{1, sizeof(valid), valid}; + EXPECT_EQ(expected, ParsePathBlob(blob)); + } + + { + char valid[] = "mst:6-2-1\0"; + drmModePropertyBlobRes blob{1, sizeof(valid), valid}; + EXPECT_EQ(expected, ParsePathBlob(blob)); + } + + { + char valid[] = {'m', 's', 't', ':', '6', '-', '2', '-', '1'}; + drmModePropertyBlobRes blob{1, sizeof(valid), valid}; + EXPECT_EQ(expected, ParsePathBlob(blob)); + } +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/DEPS b/chromium/ui/ozone/platform/drm/gpu/DEPS index 3fb5fa6f6a3..a4e9f0a5006 100644 --- a/chromium/ui/ozone/platform/drm/gpu/DEPS +++ b/chromium/ui/ozone/platform/drm/gpu/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+media/media_buildflags.h", "-ui/ozone/platform/drm", "+ui/ozone/platform/drm/common", "+ui/ozone/platform/drm/gpu", diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc index 8127e1633f2..6f4c17b6fa9 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.cc @@ -9,6 +9,7 @@ namespace ui { CrtcCommitRequest::CrtcCommitRequest(uint32_t crtc_id, uint32_t connector_id, drmModeModeInfo mode, + gfx::Point origin, HardwareDisplayPlaneList* plane_list, DrmOverlayPlaneList overlays, bool should_enable) @@ -16,8 +17,11 @@ CrtcCommitRequest::CrtcCommitRequest(uint32_t crtc_id, crtc_id_(crtc_id), connector_id_(connector_id), mode_(mode), + origin_(origin), plane_list_(plane_list), overlays_(std::move(overlays)) { + // Verify that at least one overlay plane is a primary plane if we're enabling + // a CRTC. DCHECK(!should_enable || DrmOverlayPlane::GetPrimaryPlane(overlays_)); } @@ -28,6 +32,7 @@ CrtcCommitRequest::CrtcCommitRequest(const CrtcCommitRequest& other) crtc_id_(other.crtc_id_), connector_id_(other.connector_id_), mode_(other.mode_), + origin_(other.origin_), plane_list_(other.plane_list_), overlays_(DrmOverlayPlane::Clone(other.overlays_)) {} @@ -36,11 +41,12 @@ CrtcCommitRequest CrtcCommitRequest::EnableCrtcRequest( uint32_t crtc_id, uint32_t connector_id, drmModeModeInfo mode, + gfx::Point origin, HardwareDisplayPlaneList* plane_list, DrmOverlayPlaneList overlays) { DCHECK(plane_list && !overlays.empty()); - return CrtcCommitRequest(crtc_id, connector_id, mode, plane_list, + return CrtcCommitRequest(crtc_id, connector_id, mode, origin, plane_list, std::move(overlays), /*should_enable=*/true); } @@ -49,7 +55,7 @@ CrtcCommitRequest CrtcCommitRequest::DisableCrtcRequest( uint32_t crtc_id, uint32_t connector_id, HardwareDisplayPlaneList* plane_list) { - return CrtcCommitRequest(crtc_id, connector_id, {}, plane_list, + return CrtcCommitRequest(crtc_id, connector_id, {}, gfx::Point(), plane_list, DrmOverlayPlaneList(), /*should_enable=*/false); } diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h index ad8eab84e7f..f3597f3bb31 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_commit_request.h @@ -9,6 +9,7 @@ #include <stdint.h> #include <xf86drmMode.h> +#include "ui/gfx/geometry/point.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h" @@ -31,6 +32,7 @@ class CrtcCommitRequest { uint32_t crtc_id, uint32_t connector_id, drmModeModeInfo mode, + gfx::Point origin, HardwareDisplayPlaneList* plane_list, DrmOverlayPlaneList overlays); @@ -43,6 +45,7 @@ class CrtcCommitRequest { uint32_t crtc_id() const { return crtc_id_; } uint32_t connector_id() const { return connector_id_; } const drmModeModeInfo& mode() const { return mode_; } + const gfx::Point& origin() const { return origin_; } HardwareDisplayPlaneList* plane_list() const { return plane_list_; } const DrmOverlayPlaneList& overlays() const { return overlays_; } @@ -50,6 +53,7 @@ class CrtcCommitRequest { CrtcCommitRequest(uint32_t crtc_id, uint32_t connector_id, drmModeModeInfo mode, + gfx::Point origin, HardwareDisplayPlaneList* plane_list, DrmOverlayPlaneList overlays, bool should_enable); @@ -58,6 +62,7 @@ class CrtcCommitRequest { const uint32_t crtc_id_ = 0; const uint32_t connector_id_ = 0; const drmModeModeInfo mode_ = {}; + const gfx::Point origin_; HardwareDisplayPlaneList* plane_list_ = nullptr; const DrmOverlayPlaneList overlays_; }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc index cc5e8c5f213..6e262e27bbb 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc @@ -308,9 +308,15 @@ bool DrmDevice::SetCrtc(uint32_t crtc_id, TRACE_EVENT2("drm", "DrmDevice::SetCrtc", "crtc", crtc_id, "size", gfx::Size(mode.hdisplay, mode.vdisplay).ToString()); - return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0, - connectors.data(), connectors.size(), - const_cast<drmModeModeInfo*>(&mode)); + + if (!drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0, + connectors.data(), connectors.size(), + const_cast<drmModeModeInfo*>(&mode))) { + ++modeset_sequence_id_; + return true; + } + + return false; } bool DrmDevice::DisableCrtc(uint32_t crtc_id) { @@ -540,14 +546,45 @@ bool DrmDevice::CommitProperties( uint32_t flags, uint32_t crtc_count, scoped_refptr<PageFlipRequest> page_flip_request) { + bool success = CommitPropertiesInternal(properties, flags, crtc_count, + page_flip_request); + + if (success && flags == DRM_MODE_ATOMIC_ALLOW_MODESET) + ++modeset_sequence_id_; + + return success; +} + +bool DrmDevice::CommitPropertiesInternal( + drmModeAtomicReq* properties, + uint32_t flags, + uint32_t crtc_count, + scoped_refptr<PageFlipRequest> page_flip_request) { uint64_t id = 0; + if (page_flip_request) { flags |= DRM_MODE_PAGE_FLIP_EVENT; id = page_flip_manager_->GetNextId(); } - if (!drmModeAtomicCommit(file_.GetPlatformFile(), properties, flags, - reinterpret_cast<void*>(id))) { + int result = drmModeAtomicCommit(file_.GetPlatformFile(), properties, flags, + reinterpret_cast<void*>(id)); + if (result && errno == EBUSY && (flags & DRM_MODE_ATOMIC_NONBLOCK)) { + VLOG(1) << "Nonblocking atomic commit failed with EBUSY, retry without " + "nonblock"; + // There have been cases where we get back EBUSY when attempting a + // non-blocking atomic commit. If we return false from here, that will cause + // the GPU process to CHECK itself. These are likely due to kernel bugs, + // which should be fixed, but rather than crashing we should retry the + // commit without the non-blocking flag and then it should work. This will + // cause a slight delay, but that should be imperceptible and better than + // crashing. We still do want the underlying driver bugs fixed, but this + // provide a better user experience. + flags &= ~DRM_MODE_ATOMIC_NONBLOCK; + result = drmModeAtomicCommit(file_.GetPlatformFile(), properties, flags, + reinterpret_cast<void*>(id)); + } + if (!result) { if (page_flip_request) { page_flip_manager_->RegisterCallback(id, crtc_count, page_flip_request->AddPageFlip()); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.h b/chromium/ui/ozone/platform/drm/gpu/drm_device.h index fdc7f2d0a38..400c9a80e66 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.h @@ -215,11 +215,10 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { // On success, true is returned and |page_flip_request| will receive a // callback signalling completion of the flip, if provided. - virtual bool CommitProperties( - drmModeAtomicReq* properties, - uint32_t flags, - uint32_t crtc_count, - scoped_refptr<PageFlipRequest> page_flip_request); + bool CommitProperties(drmModeAtomicReq* properties, + uint32_t flags, + uint32_t crtc_count, + scoped_refptr<PageFlipRequest> page_flip_request); virtual bool SetCapability(uint64_t capability, uint64_t value); @@ -230,6 +229,8 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { virtual bool SetMaster(); virtual bool DropMaster(); + int modeset_sequence_id() const { return modeset_sequence_id_; } + int get_fd() const { return file_.GetPlatformFile(); } base::FilePath device_path() const { return device_path_; } @@ -244,8 +245,19 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { virtual ~DrmDevice(); + virtual bool CommitPropertiesInternal( + drmModeAtomicReq* properties, + uint32_t flags, + uint32_t crtc_count, + scoped_refptr<PageFlipRequest> page_flip_request); + std::unique_ptr<HardwareDisplayPlaneManager> plane_manager_; + // Sequence ID incremented at each modeset. + // Currently used by DRM Framebuffer to indicate when was the fb initialized + // wrt the preceding modeset. + int modeset_sequence_id_ = 0; + private: class IOWatcher; class PageFlipManager; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc index 4f84ef488cf..b1ccd606e67 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" +#include "build/chromeos_buildflags.h" #include "ui/display/display_features.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" @@ -156,7 +157,7 @@ std::unique_ptr<display::DisplaySnapshot> DrmDisplay::Update( modes_ = GetDrmModeVector(info->connector()); is_hdr_capable_ = params->bits_per_channel() > 8 && params->color_space().IsHDR(); -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) is_hdr_capable_ = is_hdr_capable_ && base::FeatureList::IsEnabled(display::features::kUseHDRTransferFunction); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc index bdf7db0ec19..4eb71a4d0f7 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.cc @@ -113,7 +113,8 @@ DrmFramebuffer::DrmFramebuffer(scoped_refptr<DrmDevice> drm_device, opaque_framebuffer_pixel_format_(opaque_framebuffer_pixel_format), format_modifier_(format_modifier), preferred_modifiers_(modifiers), - size_(size) {} + size_(size), + modeset_sequence_id_at_allocation_(drm_device_->modeset_sequence_id()) {} DrmFramebuffer::~DrmFramebuffer() { if (!drm_device_->RemoveFramebuffer(framebuffer_id_)) diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h index 079297ac1a7..8dbfe7f2bd4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h @@ -92,6 +92,10 @@ class DrmFramebuffer : public base::RefCountedThreadSafe<DrmFramebuffer> { // Device on which the buffer was created. const scoped_refptr<DrmDevice>& drm_device() const { return drm_device_; } + int modeset_sequence_id_at_allocation() const { + return modeset_sequence_id_at_allocation_; + } + private: ~DrmFramebuffer(); @@ -110,6 +114,10 @@ class DrmFramebuffer : public base::RefCountedThreadSafe<DrmFramebuffer> { const std::vector<uint64_t> preferred_modifiers_; const gfx::Size size_; + // The latest modeset sequence ID that was retrieved from DrmDevice when the + // buffer is initialized. + const int modeset_sequence_id_at_allocation_ = 0; + friend class base::RefCountedThreadSafe<DrmFramebuffer>; }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc index 8fdb11aba63..bd67bd3d60a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc @@ -166,9 +166,8 @@ void DrmGpuDisplayManager::RelinquishDisplayControl() { drm->DropMaster(); } -base::flat_map<int64_t, bool> DrmGpuDisplayManager::ConfigureDisplays( +bool DrmGpuDisplayManager::ConfigureDisplays( const std::vector<display::DisplayConfigurationParams>& config_requests) { - base::flat_map<int64_t, bool> statuses; ScreenManager::ControllerConfigsList controllers_to_configure; for (const auto& config : config_requests) { @@ -176,8 +175,7 @@ base::flat_map<int64_t, bool> DrmGpuDisplayManager::ConfigureDisplays( DrmDisplay* display = FindDisplay(display_id); if (!display) { LOG(ERROR) << "There is no display with ID " << display_id; - statuses.insert(std::make_pair(display_id, false)); - continue; + return false; } std::unique_ptr<drmModeModeInfo> mode_ptr = @@ -185,8 +183,7 @@ base::flat_map<int64_t, bool> DrmGpuDisplayManager::ConfigureDisplays( if (config.mode) { if (!FindModeForDisplay(mode_ptr.get(), *config.mode.value(), display->modes(), displays_)) { - statuses.insert(std::make_pair(display_id, false)); - continue; + return false; } } @@ -205,41 +202,30 @@ base::flat_map<int64_t, bool> DrmGpuDisplayManager::ConfigureDisplays( controllers_to_configure.push_back(std::move(params)); } - if (controllers_to_configure.empty()) - return statuses; - if (clear_overlay_cache_callback_) clear_overlay_cache_callback_.Run(); - auto config_statuses = + bool config_success = screen_manager_->ConfigureDisplayControllers(controllers_to_configure); - for (const auto& status : config_statuses) { - int64_t display_id = status.first; - bool success = status.second; - DrmDisplay* display = FindDisplay(display_id); - auto config = std::find_if( - config_requests.begin(), config_requests.end(), - [display_id](const auto& request) { return request.id == display_id; }); - if (success) { - display->SetOrigin(config->origin); + for (const auto& controller : controllers_to_configure) { + if (config_success) { + FindDisplay(controller.display_id)->SetOrigin(controller.origin); } else { - if (config->mode) { + if (controller.mode) { VLOG(1) << "Failed to enable device=" - << display->drm()->device_path().value() - << " crtc=" << display->crtc() - << " connector=" << display->connector(); + << controller.drm->device_path().value() + << " crtc=" << controller.crtc + << " connector=" << controller.connector; } else { VLOG(1) << "Failed to disable device=" - << display->drm()->device_path().value() - << " crtc=" << display->crtc(); + << controller.drm->device_path().value() + << " crtc=" << controller.crtc; } } - - statuses.insert(std::make_pair(display_id, success)); } - return statuses; + return config_success; } bool DrmGpuDisplayManager::GetHDCPState( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h index 2dbe2ba68a4..5bbdc84144e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h @@ -52,7 +52,7 @@ class DrmGpuDisplayManager { bool TakeDisplayControl(); void RelinquishDisplayControl(); - base::flat_map<int64_t, bool> ConfigureDisplays( + bool ConfigureDisplays( const std::vector<display::DisplayConfigurationParams>& config_requests); bool GetHDCPState(int64_t display_id, display::HDCPState* state, diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc index 231d1728494..928ad048588 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.cc @@ -40,7 +40,10 @@ std::vector<OverlaySurfaceCandidate> ToCacheKey( } // namespace -DrmOverlayManager::DrmOverlayManager() { +DrmOverlayManager::DrmOverlayManager( + bool allow_sync_and_real_buffer_page_flip_testing) { + allow_sync_and_real_buffer_page_flip_testing_ = + allow_sync_and_real_buffer_page_flip_testing; DETACH_FROM_THREAD(thread_checker_); } @@ -79,7 +82,8 @@ void DrmOverlayManager::CheckOverlaySupport( result_candidates.back().overlay_handled = can_handle; } - if (features::IsSynchronousPageFlipTestingEnabled()) { + if (allow_sync_and_real_buffer_page_flip_testing_ && + features::IsSynchronousPageFlipTestingEnabled()) { std::vector<OverlayStatus> status = SendOverlayValidationRequestSync(result_candidates, widget); size_t size = candidates->size(); @@ -145,6 +149,11 @@ bool DrmOverlayManager::CanHandleCandidate( if (candidate.transform == gfx::OVERLAY_TRANSFORM_INVALID) return false; + // The remaining checks are for ensuring consistency between GL compositing + // and overlays. If we must use an overlay, then skip the remaining checks. + if (candidate.requires_overlay) + return true; + // Reject candidates that don't fall on a pixel boundary. if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f)) return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.h index 87173fa824a..56112a3f508 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager.h @@ -23,7 +23,8 @@ class OverlaySurfaceCandidate; // of recent configurations. class DrmOverlayManager : public OverlayManagerOzone { public: - DrmOverlayManager(); + explicit DrmOverlayManager( + bool allow_sync_and_real_buffer_page_flip_testing = true); ~DrmOverlayManager() override; // OverlayManagerOzone: diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc index 2137523fbd8..89c86a5f80e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc @@ -8,13 +8,17 @@ #include "base/bind.h" #include "base/trace_event/trace_event.h" +#include "ui/ozone/platform/drm/gpu/drm_overlay_manager.h" #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h" #include "ui/ozone/public/overlay_surface_candidate.h" namespace ui { -DrmOverlayManagerGpu::DrmOverlayManagerGpu(DrmThreadProxy* drm_thread_proxy) - : drm_thread_proxy_(drm_thread_proxy) {} +DrmOverlayManagerGpu::DrmOverlayManagerGpu( + DrmThreadProxy* drm_thread_proxy, + bool allow_sync_and_real_buffer_page_flip_testing) + : DrmOverlayManager(allow_sync_and_real_buffer_page_flip_testing), + drm_thread_proxy_(drm_thread_proxy) {} DrmOverlayManagerGpu::~DrmOverlayManagerGpu() = default; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.h b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.h index 3931e02dac5..cb759538344 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.h @@ -18,7 +18,9 @@ class DrmThreadProxy; // overlay validations requests to the DRM thread. class DrmOverlayManagerGpu : public DrmOverlayManager { public: - explicit DrmOverlayManagerGpu(DrmThreadProxy* drm_thread_proxy); + explicit DrmOverlayManagerGpu( + DrmThreadProxy* drm_thread_proxy, + bool allow_sync_and_real_buffer_page_flip_testing); ~DrmOverlayManagerGpu() override; private: diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc index 4c496547411..348cba77ec8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc @@ -46,7 +46,7 @@ scoped_refptr<DrmFramebuffer> GetBufferForPageFlipTest( // flip commits. std::vector<uint64_t> modifiers = is_0th_plane - ? drm_window->GetController()->GetFormatModifiers(fourcc_format) + ? drm_window->GetController()->GetSupportedModifiers(fourcc_format) : std::vector<uint64_t>(); // Check if we can re-use existing buffers. diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc index 76577c6e485..f5938777c47 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc @@ -81,15 +81,16 @@ class DrmOverlayValidatorTest : public testing::Test { bool ModesetController(ui::HardwareDisplayController* controller) { ui::CommitRequest commit_request; - ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); - controller->GetModesetProps(&commit_request, plane, kDefaultMode); + controller->GetModesetProps(&commit_request, modeset_planes, kDefaultMode); ui::CommitRequest request_for_update = commit_request; bool status = drm_->plane_manager()->Commit(std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); - controller->UpdateState( - /*enable_requested=*/true, - ui::DrmOverlayPlane::GetPrimaryPlane(request_for_update[0].overlays())); + + for (const ui::CrtcCommitRequest& crtc_request : commit_request) + controller->UpdateState(crtc_request); return status; } diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index d77131c3dfc..115231c7d02 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -142,7 +142,7 @@ void DrmThread::CreateBuffer(gfx::AcceleratedWidget widget, std::vector<uint64_t> modifiers; if (window && window->GetController() && !(flags & GBM_BO_USE_LINEAR) && !(client_flags & GbmPixmap::kFlagNoModifiers)) { - modifiers = window->GetController()->GetFormatModifiers(fourcc_format); + modifiers = window->GetController()->GetSupportedModifiers(fourcc_format); } CreateBufferWithGbmFlags(drm, fourcc_format, size, framebuffer_size, flags, @@ -328,13 +328,11 @@ void DrmThread::RefreshNativeDisplays( void DrmThread::ConfigureNativeDisplays( const std::vector<display::DisplayConfigurationParams>& config_requests, - base::OnceCallback<void(const base::flat_map<int64_t, bool>&)> callback) { + base::OnceCallback<void(bool)> callback) { TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplays"); - base::flat_map<int64_t, bool> statuses = - display_manager_->ConfigureDisplays(config_requests); - - std::move(callback).Run(statuses); + bool config_success = display_manager_->ConfigureDisplays(config_requests); + std::move(callback).Run(config_success); } void DrmThread::TakeDisplayControl(base::OnceCallback<void(bool)> callback) { diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc index fa04ef64c35..f2774c1e4aa 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/bind.h" +#include "base/threading/thread_restrictions.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/linux/gbm_wrapper.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc index ec3aa6b951d..d83a4f53d8b 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc @@ -31,11 +31,9 @@ DrmWindow::DrmWindow(gfx::AcceleratedWidget widget, ScreenManager* screen_manager) : widget_(widget), device_manager_(device_manager), - screen_manager_(screen_manager) { -} + screen_manager_(screen_manager) {} -DrmWindow::~DrmWindow() { -} +DrmWindow::~DrmWindow() {} void DrmWindow::Initialize() { TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_); @@ -133,10 +131,6 @@ OverlayStatusList DrmWindow::TestPageFlip( last_submitted_planes_); } -const DrmOverlayPlane* DrmWindow::GetLastModesetBuffer() const { - return DrmOverlayPlane::GetPrimaryPlane(last_submitted_planes_); -} - void DrmWindow::UpdateCursorImage() { if (!controller_) return; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.h b/chromium/ui/ozone/platform/drm/gpu/drm_window.h index ea2a2721408..ea1ef54b324 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.h @@ -84,8 +84,9 @@ class DrmWindow { OverlayStatusList TestPageFlip( const OverlaySurfaceCandidateList& overlay_params); - // Returns the last buffer associated with this window. - const DrmOverlayPlane* GetLastModesetBuffer() const; + const DrmOverlayPlaneList& last_submitted_planes() const { + return last_submitted_planes_; + } private: // Draw next frame in an animated cursor. diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc index 163a89f699c..2fdc72f9a49 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc @@ -12,6 +12,7 @@ #include "base/files/file_path.h" #include "base/strings/stringprintf.h" +#include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "third_party/khronos/EGL/egl.h" #include "ui/gfx/buffer_format_util.h" @@ -20,6 +21,7 @@ #include "ui/gfx/linux/gbm_defines.h" #include "ui/gfx/linux/scoped_gbm_device.h" #include "ui/gfx/native_pixmap.h" +#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_surface_egl.h" #include "ui/ozone/common/egl_util.h" #include "ui/ozone/common/gl_ozone_egl.h" @@ -275,8 +277,11 @@ GLOzone* GbmSurfaceFactory::GetGLOzone(gl::GLImplementation implementation) { #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> -GbmSurfaceFactory::CreateVulkanImplementation(bool allow_protected_memory, +GbmSurfaceFactory::CreateVulkanImplementation(bool use_swiftshader, + bool allow_protected_memory, bool enforce_protected_memory) { + DCHECK(!use_swiftshader) + << "Vulkan Swiftshader is not supported on this platform."; return std::make_unique<ui::VulkanImplementationGbm>(); } diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h index 4ac05739713..4aa40139d7d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h @@ -39,6 +39,7 @@ class GbmSurfaceFactory : public SurfaceFactoryOzone { #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory) override; scoped_refptr<gfx::NativePixmap> CreateNativePixmapForVulkan( diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index 664381d52a2..67cb75493b6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc @@ -15,12 +15,17 @@ #include "base/trace_event/trace_event.h" #include "ui/gfx/gpu_fence.h" #include "ui/gfx/presentation_feedback.h" +#include "ui/gl/gl_bindings.h" #include "ui/ozone/common/egl_util.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h" +#if BUILDFLAG(USE_OPENGL_APITRACE) +#include "ui/gl/gl_implementation.h" +#endif + namespace ui { namespace { @@ -133,6 +138,10 @@ void GbmSurfaceless::SwapBuffersAsync( glFlush(); } +#if BUILDFLAG(USE_OPENGL_APITRACE) + gl::TerminateFrame(); // Notify end of frame at buffer swap request. +#endif + unsubmitted_frames_.back()->Flush(); PendingFrame* frame = unsubmitted_frames_.back().get(); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc index 47a684e7d22..ed387cbc7dc 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc @@ -16,9 +16,11 @@ #include "base/trace_event/trace_event.h" #include "third_party/libdrm/src/include/drm/drm_fourcc.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkImage.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gpu_fence.h" +#include "ui/gfx/linux/drm_util_linux.h" #include "ui/gfx/native_pixmap.h" #include "ui/gfx/presentation_feedback.h" #include "ui/gfx/swap_result.h" @@ -58,7 +60,7 @@ void DrawCursor(DrmDumbBuffer* cursor, const SkBitmap& image) { // Clear to transparent in case |image| is smaller than the canvas. SkCanvas* canvas = cursor->GetCanvas(); canvas->clear(SK_ColorTRANSPARENT); - canvas->drawBitmapRect(image, damage, nullptr); + canvas->drawImageRect(image.asImage(), damage, SkSamplingOptions()); } } // namespace @@ -73,26 +75,26 @@ HardwareDisplayController::HardwareDisplayController( HardwareDisplayController::~HardwareDisplayController() = default; -void HardwareDisplayController::GetModesetProps(CommitRequest* commit_request, - const DrmOverlayPlane& primary, - const drmModeModeInfo& mode) { - TRACE_EVENT0("drm", "HDC::GetModesetProps"); - GetModesetPropsForCrtcs(commit_request, primary, +void HardwareDisplayController::GetModesetProps( + CommitRequest* commit_request, + const DrmOverlayPlaneList& modeset_planes, + const drmModeModeInfo& mode) { + GetModesetPropsForCrtcs(commit_request, modeset_planes, /*use_current_crtc_mode=*/false, mode); } -void HardwareDisplayController::GetEnableProps(CommitRequest* commit_request, - const DrmOverlayPlane& primary) { - TRACE_EVENT0("drm", "HDC::GetEnableProps"); +void HardwareDisplayController::GetEnableProps( + CommitRequest* commit_request, + const DrmOverlayPlaneList& modeset_planes) { // TODO(markyacoub): Simplify and remove the use of empty_mode. drmModeModeInfo empty_mode = {}; - GetModesetPropsForCrtcs(commit_request, primary, + GetModesetPropsForCrtcs(commit_request, modeset_planes, /*use_current_crtc_mode=*/true, empty_mode); } void HardwareDisplayController::GetModesetPropsForCrtcs( CommitRequest* commit_request, - const DrmOverlayPlane& primary, + const DrmOverlayPlaneList& modeset_planes, bool use_current_crtc_mode, const drmModeModeInfo& mode) { DCHECK(commit_request); @@ -103,19 +105,16 @@ void HardwareDisplayController::GetModesetPropsForCrtcs( drmModeModeInfo modeset_mode = use_current_crtc_mode ? controller->mode() : mode; - DrmOverlayPlaneList overlays; - overlays.push_back(primary.Clone()); + DrmOverlayPlaneList overlays = DrmOverlayPlane::Clone(modeset_planes); CrtcCommitRequest request = CrtcCommitRequest::EnableCrtcRequest( - controller->crtc(), controller->connector(), modeset_mode, + controller->crtc(), controller->connector(), modeset_mode, origin_, &owned_hardware_planes_, std::move(overlays)); commit_request->push_back(std::move(request)); } } void HardwareDisplayController::GetDisableProps(CommitRequest* commit_request) { - TRACE_EVENT0("drm", "HDC::GetDisableProps"); - for (const auto& controller : crtc_controllers_) { CrtcCommitRequest request = CrtcCommitRequest::DisableCrtcRequest( controller->crtc(), controller->connector(), &owned_hardware_planes_); @@ -124,14 +123,13 @@ void HardwareDisplayController::GetDisableProps(CommitRequest* commit_request) { } void HardwareDisplayController::UpdateState( - bool enable_requested, - const DrmOverlayPlane* primary_plane) { + const CrtcCommitRequest& crtc_request) { // Verify that the current state matches the requested state. - if (enable_requested && IsEnabled()) { - DCHECK(primary_plane); + if (crtc_request.should_enable() && IsEnabled()) { + DCHECK(!crtc_request.overlays().empty()); // TODO(markyacoub): This should be absorbed in the commit request. ResetCursor(); - OnModesetComplete(*primary_plane); + OnModesetComplete(crtc_request.overlays()); } } @@ -146,6 +144,24 @@ void HardwareDisplayController::SchedulePageFlip( bool status = ScheduleOrTestPageFlip(plane_list, page_flip_request, &out_fence); + if (!status) { + for (const auto& plane : plane_list) { + // If the page flip failed and we see that the buffer has been allocated + // before the latest modeset, it could mean it was an in-flight buffer + // carrying an obsolete configuration. + // Request a buffer reallocation to reflect the new change. + if (plane.buffer && + plane.buffer->modeset_sequence_id_at_allocation() < + plane.buffer->drm_device()->modeset_sequence_id()) { + std::move(submission_callback) + .Run(gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS, nullptr); + std::move(presentation_callback) + .Run(gfx::PresentationFeedback::Failure()); + return; + } + } + } + CHECK(status) << "SchedulePageFlip failed"; if (page_flip_request->page_flip_count() == 0) { @@ -204,17 +220,16 @@ bool HardwareDisplayController::ScheduleOrTestPageFlip( } std::vector<uint64_t> HardwareDisplayController::GetFormatModifiers( - uint32_t format) const { - std::vector<uint64_t> modifiers; - + uint32_t fourcc_format) const { if (crtc_controllers_.empty()) - return modifiers; + return std::vector<uint64_t>(); - modifiers = crtc_controllers_[0]->GetFormatModifiers(format); + std::vector<uint64_t> modifiers = + crtc_controllers_[0]->GetFormatModifiers(fourcc_format); for (size_t i = 1; i < crtc_controllers_.size(); ++i) { std::vector<uint64_t> other = - crtc_controllers_[i]->GetFormatModifiers(format); + crtc_controllers_[i]->GetFormatModifiers(fourcc_format); std::vector<uint64_t> intersection; std::set_intersection(modifiers.begin(), modifiers.end(), other.begin(), @@ -225,11 +240,26 @@ std::vector<uint64_t> HardwareDisplayController::GetFormatModifiers( return modifiers; } -std::vector<uint64_t> -HardwareDisplayController::GetFormatModifiersForModesetting( +std::vector<uint64_t> HardwareDisplayController::GetSupportedModifiers( uint32_t fourcc_format) const { - const auto& modifiers = GetFormatModifiers(fourcc_format); + if (preferred_format_modifier_.empty()) + return std::vector<uint64_t>(); + + auto it = preferred_format_modifier_.find(fourcc_format); + if (it != preferred_format_modifier_.end()) + return std::vector<uint64_t>{it->second}; + + return GetFormatModifiers(fourcc_format); +} + +std::vector<uint64_t> +HardwareDisplayController::GetFormatModifiersForTestModeset( + uint32_t fourcc_format) { + // If we're about to test, clear the current preferred modifier. + preferred_format_modifier_.clear(); + std::vector<uint64_t> filtered_modifiers; + const auto& modifiers = GetFormatModifiers(fourcc_format); for (auto modifier : modifiers) { // AFBC for modeset buffers doesn't work correctly, as we can't fill it with // a valid AFBC buffer. For now, don't use AFBC for modeset buffers. @@ -242,6 +272,18 @@ HardwareDisplayController::GetFormatModifiersForModesetting( return filtered_modifiers; } +void HardwareDisplayController::UpdatePreferredModiferForFormat( + gfx::BufferFormat buffer_format, + uint64_t modifier) { + uint32_t fourcc_format = GetFourCCFormatFromBufferFormat(buffer_format); + base::InsertOrAssign(preferred_format_modifier_, fourcc_format, modifier); + + uint32_t opaque_fourcc_format = + GetFourCCFormatForOpaqueFramebuffer(buffer_format); + base::InsertOrAssign(preferred_format_modifier_, opaque_fourcc_format, + modifier); +} + void HardwareDisplayController::MoveCursor(const gfx::Point& location) { cursor_location_ = location; UpdateCursorLocation(); @@ -368,22 +410,21 @@ void HardwareDisplayController::OnPageFlipComplete( time_of_last_flip_ = presentation_feedback.timestamp; current_planes_ = std::move(pending_planes); for (const auto& controller : crtc_controllers_) { - GetDrmDevice()->plane_manager()->ResetModesetBufferOfCrtc( + GetDrmDevice()->plane_manager()->ResetModesetStateForCrtc( controller->crtc()); } page_flip_request_ = nullptr; } void HardwareDisplayController::OnModesetComplete( - const DrmOverlayPlane& primary) { - // drmModeSetCrtc has an immediate effect, so we can assume that the current - // planes have been updated. However if a page flip is still pending, set the - // pending planes to the same values so that the callback keeps the correct - // state. + const DrmOverlayPlaneList& modeset_planes) { + // Modesetting is blocking so it has an immediate effect. We can assume that + // the current planes have been updated. However, if a page flip is still + // pending, set the pending planes to the same values so that the callback + // keeps the correct state. page_flip_request_ = nullptr; owned_hardware_planes_.legacy_page_flips.clear(); - current_planes_.clear(); - current_planes_.push_back(primary.Clone()); + current_planes_ = DrmOverlayPlane::Clone(modeset_planes); time_of_last_flip_ = base::TimeTicks::Now(); } diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h index 17a917183ae..27e4dd4bcbf 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h @@ -14,9 +14,11 @@ #include <vector> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" #include "base/time/time.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/buffer_types.h" #include "ui/gfx/swap_result.h" #include "ui/ozone/platform/drm/gpu/drm_overlay_plane.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" @@ -96,15 +98,15 @@ class HardwareDisplayController { // Gets the props required to modeset a CRTC with a |mode| onto // |commit_request|. void GetModesetProps(CommitRequest* commit_request, - const DrmOverlayPlane& primary, + const DrmOverlayPlaneList& modeset_planes, const drmModeModeInfo& mode); // Gets the props required to enable/disable a CRTC onto |commit_request|. void GetEnableProps(CommitRequest* commit_request, - const DrmOverlayPlane& primary); + const DrmOverlayPlaneList& modeset_planes); void GetDisableProps(CommitRequest* commit_request); // Updates state of the controller after modeset/enable/disable is performed. - void UpdateState(bool enable_requested, const DrmOverlayPlane* primary_plane); + void UpdateState(const CrtcCommitRequest& crtc_request); // Schedules the |overlays|' framebuffers to be displayed on the next vsync // event. The event will be posted on the graphics card file descriptor |fd_| @@ -128,17 +130,19 @@ class HardwareDisplayController { // doesn't change any state. bool TestPageFlip(const DrmOverlayPlaneList& plane_list); - // Return the supported modifiers for |fourcc_format| for this - // controller. - std::vector<uint64_t> GetFormatModifiers(uint32_t fourcc_format) const; + // Return the supported modifiers for |fourcc_format| for this controller. + std::vector<uint64_t> GetSupportedModifiers(uint32_t fourcc_format) const; // Return the supported modifiers for |fourcc_format| for this // controller to be used for modeset buffers. Currently, this only exists // because we can't provide valid AFBC buffers during modeset. // See https://crbug.com/852675 // TODO: Remove this. - std::vector<uint64_t> GetFormatModifiersForModesetting( - uint32_t fourcc_format) const; + std::vector<uint64_t> GetFormatModifiersForTestModeset( + uint32_t fourcc_format); + + void UpdatePreferredModiferForFormat(gfx::BufferFormat buffer_format, + uint64_t modifier); // Moves the hardware cursor to |location|. void MoveCursor(const gfx::Point& location); @@ -175,10 +179,10 @@ class HardwareDisplayController { // Loops over |crtc_controllers_| and save their props into |commit_request| // to be enabled/modeset. void GetModesetPropsForCrtcs(CommitRequest* commit_request, - const DrmOverlayPlane& primary, + const DrmOverlayPlaneList& modeset_planes, bool use_current_crtc_mode, const drmModeModeInfo& mode); - void OnModesetComplete(const DrmOverlayPlane& primary); + void OnModesetComplete(const DrmOverlayPlaneList& modeset_planes); bool ScheduleOrTestPageFlip(const DrmOverlayPlaneList& plane_list, scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence); @@ -189,6 +193,8 @@ class HardwareDisplayController { void ResetCursor(); void DisableCursor(); + std::vector<uint64_t> GetFormatModifiers(uint32_t fourcc_format) const; + HardwareDisplayPlaneList owned_hardware_planes_; // Stores the CRTC configuration. This is used to identify monitors and @@ -207,6 +213,11 @@ class HardwareDisplayController { int cursor_frontbuffer_ = 0; DrmDumbBuffer* current_cursor_ = nullptr; + // Maps each fourcc_format to its preferred modifier which was generated + // through modeset-test and updated in UpdatePreferredModifierForFormat(). + base::flat_map<uint32_t /*fourcc_format*/, uint64_t /*preferred_modifier*/> + preferred_format_modifier_; + base::WeakPtrFactory<HardwareDisplayController> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(HardwareDisplayController); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc index 739c8dd5d62..7cc0dddf155 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc @@ -9,6 +9,7 @@ #include <utility> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -123,7 +124,7 @@ class HardwareDisplayControllerTest : public testing::Test { } protected: - bool ModesetWithPlane(const ui::DrmOverlayPlane& plane); + bool ModesetWithPlanes(const ui::DrmOverlayPlaneList& modeset_planes); bool DisableController(); std::unique_ptr<ui::HardwareDisplayController> controller_; @@ -242,16 +243,15 @@ void HardwareDisplayControllerTest::InitializeDrmDevice(bool use_atomic) { gfx::Point()); } -bool HardwareDisplayControllerTest::ModesetWithPlane( - const ui::DrmOverlayPlane& plane) { +bool HardwareDisplayControllerTest::ModesetWithPlanes( + const ui::DrmOverlayPlaneList& modeset_planes) { ui::CommitRequest commit_request; - controller_->GetModesetProps(&commit_request, plane, kDefaultMode); + controller_->GetModesetProps(&commit_request, modeset_planes, kDefaultMode); ui::CommitRequest request_for_update = commit_request; bool status = drm_->plane_manager()->Commit(std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); - controller_->UpdateState( - /*enable_requested=*/true, - ui::DrmOverlayPlane::GetPrimaryPlane(request_for_update[0].overlays())); + for (const ui::CrtcCommitRequest& crtc_request : commit_request) + controller_->UpdateState(crtc_request); return status; } @@ -262,7 +262,8 @@ bool HardwareDisplayControllerTest::DisableController() { ui::CommitRequest request_for_update = commit_request; bool status = drm_->plane_manager()->Commit(std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); - controller_->UpdateState(/*enable_requested=*/false, nullptr); + for (const ui::CrtcCommitRequest& crtc_request : commit_request) + controller_->UpdateState(crtc_request); return status; } @@ -301,15 +302,18 @@ uint64_t HardwareDisplayControllerTest::GetPlanePropertyValue( } TEST_F(HardwareDisplayControllerTest, CheckModesettingResult) { - ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane)); - EXPECT_FALSE(plane.buffer->HasOneRef()); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); + EXPECT_FALSE(ui::DrmOverlayPlane::GetPrimaryPlane(modeset_planes) + ->buffer->HasOneRef()); } TEST_F(HardwareDisplayControllerTest, CrtcPropsAfterModeset) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); ui::ScopedDrmObjectPropertyPtr crtc_props = drm_->GetObjectProperties(kPrimaryCrtc, DRM_MODE_OBJECT_CRTC); @@ -328,8 +332,9 @@ TEST_F(HardwareDisplayControllerTest, CrtcPropsAfterModeset) { } TEST_F(HardwareDisplayControllerTest, ConnectorPropsAfterModeset) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); ui::ScopedDrmObjectPropertyPtr connector_props = drm_->GetObjectProperties(kConnectorIdBase, DRM_MODE_OBJECT_CONNECTOR); @@ -343,11 +348,15 @@ TEST_F(HardwareDisplayControllerTest, ConnectorPropsAfterModeset) { TEST_F(HardwareDisplayControllerTest, PlanePropsAfterModeset) { const FakeFenceFD fake_fence_fd; - ui::DrmOverlayPlane plane1(CreateBuffer(), fake_fence_fd.GetGpuFence()); - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), fake_fence_fd.GetGpuFence()); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); ui::ScopedDrmObjectPropertyPtr plane_props = drm_->GetObjectProperties(kPlaneOffset, DRM_MODE_OBJECT_PLANE); + const ui::DrmOverlayPlane* primary_plane = + ui::DrmOverlayPlane::GetPrimaryPlane(modeset_planes); + { ui::DrmDevice::Property prop = {}; ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "CRTC_ID", &prop); @@ -358,13 +367,13 @@ TEST_F(HardwareDisplayControllerTest, PlanePropsAfterModeset) { ui::DrmDevice::Property prop = {}; ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "CRTC_X", &prop); EXPECT_EQ(kCrtcX, prop.id); - EXPECT_EQ(plane1.display_bounds.x(), static_cast<int>(prop.value)); + EXPECT_EQ(primary_plane->display_bounds.x(), static_cast<int>(prop.value)); } { ui::DrmDevice::Property prop = {}; ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "CRTC_Y", &prop); EXPECT_EQ(kCrtcY, prop.id); - EXPECT_EQ(plane1.display_bounds.y(), static_cast<int>(prop.value)); + EXPECT_EQ(primary_plane->display_bounds.y(), static_cast<int>(prop.value)); } { ui::DrmDevice::Property prop = {}; @@ -382,13 +391,13 @@ TEST_F(HardwareDisplayControllerTest, PlanePropsAfterModeset) { ui::DrmDevice::Property prop = {}; ui::GetDrmPropertyForName(drm_.get(), plane_props.get(), "FB_ID", &prop); EXPECT_EQ(kPlaneFbId, prop.id); - EXPECT_EQ(plane1.buffer->opaque_framebuffer_id(), + EXPECT_EQ(primary_plane->buffer->opaque_framebuffer_id(), static_cast<uint32_t>(prop.value)); } - gfx::RectF crop_rectf = plane1.crop_rect; - crop_rectf.Scale(plane1.buffer->size().width(), - plane1.buffer->size().height()); + gfx::RectF crop_rectf = primary_plane->crop_rect; + crop_rectf.Scale(primary_plane->buffer->size().width(), + primary_plane->buffer->size().height()); gfx::Rect crop_rect = gfx::ToNearestRect(crop_rectf); gfx::Rect fixed_point_rect = gfx::Rect(crop_rect.x() << 16, crop_rect.y() << 16, @@ -427,8 +436,10 @@ TEST_F(HardwareDisplayControllerTest, PlanePropsAfterModeset) { } TEST_F(HardwareDisplayControllerTest, FenceFdValueChange) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); + modeset_planes.push_back(plane.Clone()); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); // Test invalid fence fd { @@ -443,9 +454,9 @@ TEST_F(HardwareDisplayControllerTest, FenceFdValueChange) { } const FakeFenceFD fake_fence_fd; - plane1.gpu_fence = fake_fence_fd.GetGpuFence(); + plane.gpu_fence = fake_fence_fd.GetGpuFence(); std::vector<ui::DrmOverlayPlane> planes = {}; - planes.push_back(plane1.Clone()); + planes.push_back(plane.Clone()); SchedulePageFlip(std::move(planes)); // Verify fence FD after a GPU Fence is added to the plane. @@ -460,8 +471,10 @@ TEST_F(HardwareDisplayControllerTest, FenceFdValueChange) { static_cast<int>(fence_fd_prop.value)); } - plane1.gpu_fence = nullptr; - EXPECT_TRUE(ModesetWithPlane(plane1)); + plane.gpu_fence = nullptr; + modeset_planes.clear(); + modeset_planes.push_back(plane.Clone()); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); // Test an invalid FD again after the fence is removed. { @@ -477,9 +490,9 @@ TEST_F(HardwareDisplayControllerTest, FenceFdValueChange) { } TEST_F(HardwareDisplayControllerTest, CheckDisableResetsProps) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); // Test props values after disabling. DisableController(); @@ -581,20 +594,21 @@ TEST_F(HardwareDisplayControllerTest, CheckDisableResetsProps) { } TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); EXPECT_EQ(1, drm_->get_commit_count()); - ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane2.Clone()); + ui::DrmOverlayPlane page_flip_plane(CreateBuffer(), nullptr); + std::vector<ui::DrmOverlayPlane> page_flip_planes; + page_flip_planes.push_back(page_flip_plane.Clone()); - SchedulePageFlip(std::move(planes)); + SchedulePageFlip(std::move(page_flip_planes)); drm_->RunCallbacks(); - EXPECT_TRUE(plane1.buffer->HasOneRef()); - EXPECT_FALSE(plane2.buffer->HasOneRef()); + EXPECT_TRUE(ui::DrmOverlayPlane::GetPrimaryPlane(modeset_planes) + ->buffer->HasOneRef()); + EXPECT_FALSE(page_flip_plane.buffer->HasOneRef()); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -608,24 +622,21 @@ TEST_F(HardwareDisplayControllerTest, CheckStateIfModesetFails) { InitializeDrmDevice(/* use_atomic */ false); drm_->set_set_crtc_expectation(false); - ui::DrmOverlayPlane plane(CreateBuffer(), nullptr); - - EXPECT_FALSE(ModesetWithPlane(plane)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_FALSE(ModesetWithPlanes(modeset_planes)); } TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - ui::DrmOverlayPlane plane2( - CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, - gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), true, nullptr); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + planes.emplace_back(CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), + true, nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + EXPECT_TRUE(ModesetWithPlanes(planes)); EXPECT_EQ(1, drm_->get_commit_count()); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); - planes.push_back(plane2.Clone()); - SchedulePageFlip(std::move(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -637,18 +648,15 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { } TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - ui::DrmOverlayPlane plane2( - CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, - gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), true, nullptr); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + planes.emplace_back(CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), + true, nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + EXPECT_TRUE(ModesetWithPlanes(planes)); EXPECT_EQ(1, drm_->get_commit_count()); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); - planes.push_back(plane2.Clone()); - SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); EXPECT_EQ(2, drm_->get_commit_count()); // Verify both planes on the primary display have a valid framebuffer. @@ -674,16 +682,13 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { } TEST_F(HardwareDisplayControllerTest, AcceptUnderlays) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - ui::DrmOverlayPlane plane2(CreateBuffer(), -1, gfx::OVERLAY_TRANSFORM_NONE, - gfx::Rect(kDefaultModeSize), - gfx::RectF(kDefaultModeSizeF), true, nullptr); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + planes.emplace_back(CreateBuffer(), -1, gfx::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kDefaultModeSize), + gfx::RectF(kDefaultModeSizeF), true, nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); - planes.push_back(plane2.Clone()); + EXPECT_TRUE(ModesetWithPlanes(planes)); SchedulePageFlip(std::move(planes)); drm_->RunCallbacks(); @@ -695,13 +700,12 @@ TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) { controller_->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + + EXPECT_TRUE(ModesetWithPlanes(planes)); EXPECT_EQ(1, drm_->get_commit_count()); - ui::DrmOverlayPlane plane2(CreateBuffer(), nullptr); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane2.Clone()); SchedulePageFlip(std::move(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -720,10 +724,10 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { controller_->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); + SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -762,10 +766,10 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { } TEST_F(HardwareDisplayControllerTest, PlaneStateAfterDestroyingCrtc) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); + SchedulePageFlip(std::move(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -790,10 +794,10 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { controller_->AddCrtc(std::make_unique<ui::CrtcController>( drm_.get(), kSecondaryCrtc, kConnectorIdBase + 1)); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); + SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -835,39 +839,52 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { } TEST_F(HardwareDisplayControllerTest, ModesetWhilePageFlipping) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); - SchedulePageFlip(std::move(planes)); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); - EXPECT_TRUE(ModesetWithPlane(plane1)); + SchedulePageFlip(ui::DrmOverlayPlane::Clone(planes)); + + EXPECT_TRUE(ModesetWithPlanes(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); } TEST_F(HardwareDisplayControllerTest, FailPageFlipping) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + ASSERT_TRUE(ModesetWithPlanes(modeset_planes)); + + std::vector<ui::DrmOverlayPlane> page_flip_planes; + page_flip_planes.emplace_back(CreateBuffer(), nullptr); drm_->set_commit_expectation(false); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); - EXPECT_DEATH_IF_SUPPORTED(SchedulePageFlip(std::move(planes)), + EXPECT_DEATH_IF_SUPPORTED(SchedulePageFlip(std::move(page_flip_planes)), "SchedulePageFlip failed"); } -TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlaneOnFlip) { - ui::DrmOverlayPlane modeset_primary_plane(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(modeset_primary_plane)); - - ui::DrmOverlayPlane plane1(CreateBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, - gfx::Rect(kDefaultModeSize), - gfx::RectF(0, 0, 1, 1), true, nullptr); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); +TEST_F(HardwareDisplayControllerTest, + RecreateBuffersOnOldPlanesPageFlipFailure) { + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + ASSERT_TRUE(ModesetWithPlanes(planes)); + + drm_->set_commit_expectation(false); SchedulePageFlip(std::move(planes)); + EXPECT_EQ(gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS, last_swap_result_); +} + +TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlaneOnFlip) { + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(modeset_planes)); + + std::vector<ui::DrmOverlayPlane> page_flip_planes; + page_flip_planes.emplace_back(CreateBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kDefaultModeSize), + gfx::RectF(0, 0, 1, 1), true, nullptr); + SchedulePageFlip(std::move(page_flip_planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -875,10 +892,10 @@ TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlaneOnFlip) { } TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); + SchedulePageFlip(std::move(planes)); controller_->AddCrtc(std::make_unique<ui::CrtcController>( @@ -890,10 +907,10 @@ TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) { } TEST_F(HardwareDisplayControllerTest, RemoveCrtcMidPageFlip) { - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); + SchedulePageFlip(std::move(planes)); controller_->RemoveCrtc(drm_, kPrimaryCrtc); @@ -907,16 +924,13 @@ TEST_F(HardwareDisplayControllerTest, Disable) { // Page flipping overlays is only supported on atomic configurations. InitializeDrmDevice(/* use_atomic= */ true); - ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr); - EXPECT_TRUE(ModesetWithPlane(plane1)); - - ui::DrmOverlayPlane plane2( - CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, - gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), true, nullptr); - std::vector<ui::DrmOverlayPlane> planes; - planes.push_back(plane1.Clone()); - planes.push_back(plane2.Clone()); + ui::DrmOverlayPlaneList planes; + planes.emplace_back(CreateBuffer(), nullptr); + EXPECT_TRUE(ModesetWithPlanes(planes)); + planes.emplace_back(CreateOverlayBuffer(), 1, gfx::OVERLAY_TRANSFORM_NONE, + gfx::Rect(kOverlaySize), gfx::RectF(kDefaultModeSizeF), + true, nullptr); SchedulePageFlip(std::move(planes)); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -931,3 +945,20 @@ TEST_F(HardwareDisplayControllerTest, Disable) { // No plane should be in use. ASSERT_EQ(0, planes_in_use); } + +TEST_F(HardwareDisplayControllerTest, MultiplePlanesModeset) { + ui::DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(CreateBuffer(), nullptr); + modeset_planes.emplace_back(CreateBuffer(), nullptr); + ASSERT_TRUE(ModesetWithPlanes(modeset_planes)); + EXPECT_EQ(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers.size(), + 2UL); + for (const auto& plane : modeset_planes) { + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + plane.buffer)); + } +} diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc index acff6ed2116..a24b5b83e70 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc @@ -4,7 +4,10 @@ #include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h" +#include <drm_fourcc.h> + #include "base/logging.h" +#include "media/media_buildflags.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" @@ -20,12 +23,15 @@ uint32_t OverlayTransformToDrmRotationPropertyValue( return DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_0; case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: return DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_0; + // Driver code swaps 90 and 270 to be compliant with how xrandr uses these + // values, so we need to invert them here as well to get them back to the + // proper value. case gfx::OVERLAY_TRANSFORM_ROTATE_90: - return DRM_MODE_ROTATE_90; + return DRM_MODE_ROTATE_270; case gfx::OVERLAY_TRANSFORM_ROTATE_180: return DRM_MODE_ROTATE_180; case gfx::OVERLAY_TRANSFORM_ROTATE_270: - return DRM_MODE_ROTATE_270; + return DRM_MODE_ROTATE_90; default: NOTREACHED(); } @@ -35,8 +41,22 @@ uint32_t OverlayTransformToDrmRotationPropertyValue( // Rotations are dependent on modifiers. Tiled formats can be rotated, // linear formats cannot. Atomic tests currently ignore modifiers, so there // isn't a way of determining if the rotation is supported. -// TODO(https://crbug/880464): Remove this. -bool IsRotationTransformSupported(gfx::OverlayTransform transform) { +// TODO(https://b/172210707): Atomic tests should work if we are using +// kUseRealBuffersForPageFlipTest, so this should be revisited and tested more +// broadly with a condition on that. +// NOTE: This is enabled for TGL+ and NV12/P010 formats for 90/270 rotation +// currently since we always allocate those formats as Y-tiled and 90/270 +// rotation is supported by the HW in that case. This is needed for protected +// content that requires overlays. +bool IsRotationTransformSupported(gfx::OverlayTransform transform, + uint32_t format_fourcc) { +#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) + if ((format_fourcc == DRM_FORMAT_NV12 || format_fourcc == DRM_FORMAT_P010) && + (transform == gfx::OVERLAY_TRANSFORM_ROTATE_90 || + transform == gfx::OVERLAY_TRANSFORM_ROTATE_270)) { + return true; + } +#endif if ((transform == gfx::OVERLAY_TRANSFORM_ROTATE_90) || (transform == gfx::OVERLAY_TRANSFORM_ROTATE_270) || (transform == gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL)) { @@ -80,11 +100,12 @@ bool HardwareDisplayPlaneAtomic::AssignPlaneProps( const gfx::Rect& crtc_rect, const gfx::Rect& src_rect, const gfx::OverlayTransform transform, - int in_fence_fd) { + int in_fence_fd, + uint32_t format_fourcc) { if (transform != gfx::OVERLAY_TRANSFORM_NONE && !properties_.rotation.id) return false; - if (!IsRotationTransformSupported(transform)) + if (!IsRotationTransformSupported(transform, format_fourcc)) return false; // Make a copy of properties to get the props IDs for the new intermediate diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h index 5043b987468..ae8d07997ec 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h @@ -31,7 +31,8 @@ class HardwareDisplayPlaneAtomic : public HardwareDisplayPlane { const gfx::Rect& crtc_rect, const gfx::Rect& src_rect, const gfx::OverlayTransform transform, - int in_fence_fd); + int in_fence_fd, + uint32_t format_fourcc); // Sets the props on |property_set| for commit. bool SetPlaneProps(drmModeAtomicReq* property_set); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc index 634fb042481..448cfa9a14b 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc @@ -459,8 +459,10 @@ void HardwareDisplayPlaneManager::UpdateCrtcAndPlaneStatesAfterModeset( if (is_enabled) { crtc_state.mode = crtc_request.mode(); - crtc_state.modeset_framebuffer = - DrmOverlayPlane::GetPrimaryPlane(crtc_request.overlays())->buffer; + crtc_state.modeset_framebuffers.clear(); + for (const auto& overlay : crtc_request.overlays()) + crtc_state.modeset_framebuffers.push_back(overlay.buffer); + } else { if (crtc_request.plane_list()) disable_planes_lists.insert(crtc_request.plane_list()); @@ -483,9 +485,9 @@ void HardwareDisplayPlaneManager::UpdateCrtcAndPlaneStatesAfterModeset( } } -void HardwareDisplayPlaneManager::ResetModesetBufferOfCrtc(uint32_t crtc_id) { +void HardwareDisplayPlaneManager::ResetModesetStateForCrtc(uint32_t crtc_id) { CrtcState& crtc_state = CrtcStateForCrtcId(crtc_id); - crtc_state.modeset_framebuffer = nullptr; + crtc_state.modeset_framebuffers.clear(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h index a4593292e8d..0cd18997ce8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h @@ -82,7 +82,7 @@ class HardwareDisplayPlaneManager { CrtcState(CrtcState&&); drmModeModeInfo mode = {}; - scoped_refptr<DrmFramebuffer> modeset_framebuffer; + std::vector<scoped_refptr<DrmFramebuffer>> modeset_framebuffers; CrtcProperties properties = {}; @@ -187,7 +187,7 @@ class HardwareDisplayPlaneManager { // which resources needed to be tracked internally in // HardwareDisplayPlaneManager and which should be taken care of by the // caller. - void ResetModesetBufferOfCrtc(uint32_t crtc_id); + void ResetModesetStateForCrtc(uint32_t crtc_id); protected: struct ConnectorProperties { diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc index db7e3cad473..2fae9fcc8ff 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc @@ -11,10 +11,10 @@ #include <utility> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/files/platform_file.h" #include "base/logging.h" -#include "base/stl_util.h" #include "base/threading/sequenced_task_runner_handle.h" #include "ui/gfx/gpu_fence.h" #include "ui/gfx/gpu_fence_handle.h" @@ -142,8 +142,11 @@ bool HardwareDisplayPlaneManagerAtomic::Commit(CommitRequest commit_request, if (crtc_request.should_enable()) { DCHECK(crtc_request.plane_list()); - status &= AssignOverlayPlanes(crtc_request.plane_list(), - crtc_request.overlays(), crtc_id); + if (!AssignOverlayPlanes(crtc_request.plane_list(), + crtc_request.overlays(), crtc_id)) { + LOG_IF(ERROR, !is_testing) << "Failed to Assign Overlay Planes"; + status = false; + } enable_planes_lists.insert(crtc_request.plane_list()); } } @@ -204,9 +207,9 @@ void HardwareDisplayPlaneManagerAtomic::SetAtomicPropsForCommit( plane->set_in_use(false); HardwareDisplayPlaneAtomic* atomic_plane = static_cast<HardwareDisplayPlaneAtomic*>(plane); - atomic_plane->AssignPlaneProps(0, 0, gfx::Rect(), gfx::Rect(), - gfx::OVERLAY_TRANSFORM_NONE, - base::kInvalidPlatformFile); + atomic_plane->AssignPlaneProps( + 0, 0, gfx::Rect(), gfx::Rect(), gfx::OVERLAY_TRANSFORM_NONE, + base::kInvalidPlatformFile, DRM_FORMAT_INVALID); atomic_plane->SetPlaneProps(atomic_request); } } @@ -307,9 +310,9 @@ bool HardwareDisplayPlaneManagerAtomic::DisableOverlayPlanes( HardwareDisplayPlaneAtomic* atomic_plane = static_cast<HardwareDisplayPlaneAtomic*>(plane); - atomic_plane->AssignPlaneProps(0, 0, gfx::Rect(), gfx::Rect(), - gfx::OVERLAY_TRANSFORM_NONE, - base::kInvalidPlatformFile); + atomic_plane->AssignPlaneProps( + 0, 0, gfx::Rect(), gfx::Rect(), gfx::OVERLAY_TRANSFORM_NONE, + base::kInvalidPlatformFile, DRM_FORMAT_INVALID); atomic_plane->SetPlaneProps(plane_list->atomic_property_set.get()); } ret = drm_->CommitProperties(plane_list->atomic_property_set.get(), @@ -365,7 +368,7 @@ void HardwareDisplayPlaneManagerAtomic::RequestPlanesReadyCallback( } bool HardwareDisplayPlaneManagerAtomic::SetPlaneData( - HardwareDisplayPlaneList* plane_list, + HardwareDisplayPlaneList*, HardwareDisplayPlane* hw_plane, const DrmOverlayPlane& overlay, uint32_t crtc_id, @@ -382,9 +385,10 @@ bool HardwareDisplayPlaneManagerAtomic::SetPlaneData( fence_fd = gpu_fence_handle.owned_fd.get(); } - if (!atomic_plane->AssignPlaneProps(crtc_id, framebuffer_id, - overlay.display_bounds, src_rect, - overlay.plane_transform, fence_fd)) { + if (!atomic_plane->AssignPlaneProps( + crtc_id, framebuffer_id, overlay.display_bounds, src_rect, + overlay.plane_transform, fence_fd, + overlay.buffer->framebuffer_pixel_format())) { return false; } return true; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc index 6f73d1747a0..1f23be53932 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc @@ -288,7 +288,7 @@ TEST_P(HardwareDisplayPlaneManagerTest, ResettingConnectorCache) { ui::CrtcCommitRequest request = ui::CrtcCommitRequest::EnableCrtcRequest( crtc_properties_[i].id, connector_properties[i].id, kDefaultMode, - &state, std::move(overlays)); + gfx::Point(), &state, std::move(overlays)); commit_request.push_back(std::move(request)); } @@ -309,22 +309,22 @@ TEST_P(HardwareDisplayPlaneManagerTest, ResettingConnectorCache) { ui::DrmOverlayPlaneList overlays; overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( - crtc_properties_[0].id, kConnectorIdBase, kDefaultMode, &state, - std::move(overlays))); + crtc_properties_[0].id, kConnectorIdBase, kDefaultMode, gfx::Point(), + &state, std::move(overlays))); } { ui::DrmOverlayPlaneList overlays; overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( - crtc_properties_[1].id, kConnectorIdBase + 1, kDefaultMode, &state, - std::move(overlays))); + crtc_properties_[1].id, kConnectorIdBase + 1, kDefaultMode, + gfx::Point(), &state, std::move(overlays))); } { ui::DrmOverlayPlaneList overlays; overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( - crtc_properties_[2].id, kConnectorIdBase + 3, kDefaultMode, &state, - std::move(overlays))); + crtc_properties_[2].id, kConnectorIdBase + 3, kDefaultMode, + gfx::Point(), &state, std::move(overlays))); } EXPECT_TRUE(fake_drm_->plane_manager()->Commit( @@ -332,6 +332,47 @@ TEST_P(HardwareDisplayPlaneManagerTest, ResettingConnectorCache) { } } +TEST_P(HardwareDisplayPlaneManagerTest, SequenceIncrementOnModesetOnly) { + fake_drm_->InitializeState(crtc_properties_, connector_properties_, + plane_properties_, property_names_, + /*use_atomic=*/true); + + // Modeset Test + { + int pre_test_sequence_id = fake_drm_->modeset_sequence_id(); + ASSERT_TRUE(fake_drm_->plane_manager()->Commit( + ui::CommitRequest(), + DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET)); + EXPECT_EQ(pre_test_sequence_id, fake_drm_->modeset_sequence_id()); + } + + // Successful Modeset + { + int pre_modeset_sequence_id = fake_drm_->modeset_sequence_id(); + ASSERT_TRUE(fake_drm_->plane_manager()->Commit( + ui::CommitRequest(), DRM_MODE_ATOMIC_ALLOW_MODESET)); + EXPECT_EQ(pre_modeset_sequence_id + 1, fake_drm_->modeset_sequence_id()); + } + + // Failed Modeset + { + int pre_modeset_sequence_id = fake_drm_->modeset_sequence_id(); + fake_drm_->set_set_crtc_expectation(false); + ASSERT_FALSE(fake_drm_->plane_manager()->Commit( + ui::CommitRequest(), DRM_MODE_ATOMIC_ALLOW_MODESET)); + fake_drm_->set_set_crtc_expectation(true); + EXPECT_EQ(pre_modeset_sequence_id, fake_drm_->modeset_sequence_id()); + } + + // Page Flip + { + int pre_flip_sequence_id = fake_drm_->modeset_sequence_id(); + ASSERT_TRUE(fake_drm_->plane_manager()->Commit(ui::CommitRequest(), + DRM_MODE_ATOMIC_NONBLOCK)); + EXPECT_EQ(pre_flip_sequence_id, fake_drm_->modeset_sequence_id()); + } +} + TEST_P(HardwareDisplayPlaneManagerLegacyTest, Modeset) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); fake_drm_->InitializeState(crtc_properties_, connector_properties_, @@ -346,8 +387,8 @@ TEST_P(HardwareDisplayPlaneManagerLegacyTest, Modeset) { ui::DrmOverlayPlaneList overlays; overlays.push_back(plane.Clone()); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( - crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, &state, - std::move(overlays))); + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, + gfx::Point(), &state, std::move(overlays))); EXPECT_FALSE(fake_drm_->plane_manager()->Commit( std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); @@ -495,8 +536,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, Modeset) { overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( - crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, &state, - std::move(overlays))); + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, + gfx::Point(), &state, std::move(overlays))); EXPECT_TRUE(fake_drm_->plane_manager()->Commit( std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); @@ -530,8 +571,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, CheckPropsAfterModeset) { ui::DrmOverlayPlaneList overlays; overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( - crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, &state, - std::move(overlays))); + crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, + gfx::Point(), &state, std::move(overlays))); EXPECT_TRUE(fake_drm_->plane_manager()->Commit( std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); @@ -570,7 +611,7 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, CheckPropsAfterDisable) { overlays.push_back(ui::DrmOverlayPlane(fake_buffer_, nullptr)); commit_request.push_back(ui::CrtcCommitRequest::EnableCrtcRequest( crtc_properties_[0].id, connector_properties_[0].id, kDefaultMode, - &state, std::move(overlays))); + gfx::Point(), &state, std::move(overlays))); EXPECT_TRUE(fake_drm_->plane_manager()->Commit( std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); } @@ -1379,7 +1420,8 @@ class HardwareDisplayPlaneAtomicMock : public ui::HardwareDisplayPlaneAtomic { const gfx::Rect& crtc_rect, const gfx::Rect& src_rect, const gfx::OverlayTransform transform, - int in_fence_fd) override { + int in_fence_fd, + uint32_t format_fourcc) override { framebuffer_ = framebuffer; return true; } diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc index bf00ae93cfe..1ef0fe9f11f 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc @@ -9,7 +9,9 @@ #include <utility> #include "base/check.h" -#include "base/stl_util.h" +#include "base/containers/contains.h" +#include "base/containers/flat_map.h" +#include "base/logging.h" #include "skia/ext/legacy_display_globals.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -70,6 +72,13 @@ Type* FindObjectById(uint32_t id, std::vector<Type>& properties) { return it != properties.end() ? &(*it) : nullptr; } +// TODO(dnicoara): Generate all IDs internal to MockDrmDevice. +// For now generate something with a high enough ID to be unique in tests. +uint32_t GetUniqueNumber() { + static uint32_t value_generator = 0xff000000; + return ++value_generator; +} + } // namespace MockDrmDevice::CrtcProperties::CrtcProperties() = default; @@ -173,6 +182,34 @@ void MockDrmDevice::UpdateState( connector_properties_ = connector_properties; plane_properties_ = plane_properties; property_names_ = property_names; + + // Props IDs shouldn't change throughout a DRM state. Grab it once at + // UpdateState. + plane_crtc_id_prop_id_ = 0; + for (const PlaneProperties& plane_props : plane_properties) { + const std::vector<DrmDevice::Property>& props = plane_props.properties; + auto it = std::find_if( + props.begin(), props.end(), + [&names = property_names_](const DrmDevice::Property& prop) { + return names.at(prop.id) == "CRTC_ID"; + }); + if (it != props.end()) { + plane_crtc_id_prop_id_ = it->id; + // all planes should have the same prop ID for the name. Break right after + // the first one is found. + break; + } + } +} + +void MockDrmDevice::SetModifiersOverhead( + base::flat_map<uint64_t /*modifier*/, int /*overhead*/> + modifiers_overhead) { + modifiers_overhead_ = modifiers_overhead; +} + +void MockDrmDevice::SetSystemLimitOfModifiers(uint64_t limit) { + system_watermark_limitations_ = limit; } ScopedDrmResourcesPtr MockDrmDevice::GetResources() { @@ -258,8 +295,9 @@ bool MockDrmDevice::AddFramebuffer2(uint32_t width, uint32_t* framebuffer, uint32_t flags) { add_framebuffer_call_count_++; - *framebuffer = add_framebuffer_call_count_; + *framebuffer = GetUniqueNumber(); framebuffer_ids_.insert(*framebuffer); + fb_props_[*framebuffer] = {width, height, modifiers[0]}; return add_framebuffer_expectation_; } @@ -269,6 +307,11 @@ bool MockDrmDevice::RemoveFramebuffer(uint32_t framebuffer) { CHECK(it != framebuffer_ids_.end()); framebuffer_ids_.erase(it); } + { + auto it = fb_props_.find(framebuffer); + CHECK(it != fb_props_.end()); + fb_props_.erase(it); + } remove_framebuffer_call_count_++; std::vector<uint32_t> crtcs_to_clear; for (auto crtc_fb : crtc_fb_) { @@ -330,7 +373,7 @@ bool MockDrmDevice::SetProperty(uint32_t connector_id, ScopedDrmPropertyBlob MockDrmDevice::CreatePropertyBlob(const void* blob, size_t size) { - uint32_t id = ++property_id_generator_; + uint32_t id = GetUniqueNumber(); allocated_property_blobs_.insert(id); return std::make_unique<DrmPropertyBlobMetadata>(this, id); } @@ -427,25 +470,46 @@ bool MockDrmDevice::CloseBufferHandle(uint32_t handle) { return true; } -bool MockDrmDevice::CommitProperties( +bool MockDrmDevice::CommitPropertiesInternal( drmModeAtomicReq* request, uint32_t flags, uint32_t crtc_count, scoped_refptr<PageFlipRequest> page_flip_request) { + commit_count_++; if (flags == kTestModesetFlags) ++test_modeset_count_; else if (flags == kCommitModesetFlags) ++commit_modeset_count_; - commit_count_++; - if (!commit_expectation_) + if ((flags & kCommitModesetFlags && !set_crtc_expectation_) || + (flags & DRM_MODE_ATOMIC_NONBLOCK && !commit_expectation_)) { return false; + } + + uint64_t requested_resources = 0; + base::flat_map<uint64_t, int> crtc_planes_counter; for (uint32_t i = 0; i < request->cursor; ++i) { - bool res = ValidatePropertyValue(request->items[i].property_id, - request->items[i].value); - if (!res) + const drmModeAtomicReqItem& item = request->items[i]; + if (!ValidatePropertyValue(item.property_id, item.value)) return false; + + if (fb_props_.find(item.value) != fb_props_.end()) { + const FramebufferProps& props = fb_props_[item.value]; + requested_resources += modifiers_overhead_[props.modifier]; + } + + if (item.property_id == plane_crtc_id_prop_id_) { + if (++crtc_planes_counter[item.value] > 1 && + !modeset_with_overlays_expectation_) + return false; + } + } + + if (requested_resources > system_watermark_limitations_) { + LOG(ERROR) << "Requested display configuration exceeds system watermark " + "limitations"; + return false; } if (page_flip_request) @@ -463,6 +527,12 @@ bool MockDrmDevice::CommitProperties( return false; } + // Count all committed planes at the end just before returning true to reflect + // the number of planes that have successfully been committed. + last_planes_committed_count_ = 0; + for (const auto& planes_counter : crtc_planes_counter) + last_planes_committed_count_ += planes_counter.second; + return true; } diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h index 922d402147c..0c2d823f12a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h @@ -8,11 +8,13 @@ #include <drm_mode.h> #include <stddef.h> #include <stdint.h> +#include <limits> #include <map> #include <memory> #include <set> #include <vector> +#include "base/containers/flat_map.h" #include "base/containers/queue.h" #include "base/macros.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -90,11 +92,18 @@ class MockDrmDevice : public DrmDevice { legacy_gamma_ramp_expectation_ = state; } void set_commit_expectation(bool state) { commit_expectation_ = state; } + void set_overlay_modeset_expecation(bool state) { + modeset_with_overlays_expectation_ = state; + } uint32_t current_framebuffer() const { return current_framebuffer_; } const std::vector<sk_sp<SkSurface>> buffers() const { return buffers_; } + int last_planes_committed_count() const { + return last_planes_committed_count_; + } + uint32_t get_cursor_handle_for_crtc(uint32_t crtc) const { const auto it = crtc_cursor_map_.find(crtc); return it != crtc_cursor_map_.end() ? it->second : 0; @@ -122,6 +131,9 @@ class MockDrmDevice : public DrmDevice { void SetPropertyBlob(ScopedDrmPropertyBlobPtr blob); + void SetModifiersOverhead(base::flat_map<uint64_t, int> modifiers_overhead); + void SetSystemLimitOfModifiers(uint64_t limit); + // DrmDevice: ScopedDrmResourcesPtr GetResources() override; ScopedDrmPlaneResPtr GetPlaneResources() override; @@ -177,10 +189,6 @@ class MockDrmDevice : public DrmDevice { bool MapDumbBuffer(uint32_t handle, size_t size, void** pixels) override; bool UnmapDumbBuffer(void* pixels, size_t size) override; bool CloseBufferHandle(uint32_t handle) override; - bool CommitProperties(drmModeAtomicReq* request, - uint32_t flags, - uint32_t crtc_count, - scoped_refptr<PageFlipRequest> callback) override; bool SetGammaRamp( uint32_t crtc_id, const std::vector<display::GammaRampRGBEntry>& lut) override; @@ -188,8 +196,21 @@ class MockDrmDevice : public DrmDevice { uint32_t GetFramebufferForCrtc(uint32_t crtc_id) const; private: + // Properties of the plane associated with a fb. + struct FramebufferProps { + uint32_t width = 0; + uint32_t height = 0; + uint64_t modifier = 0; + }; + ~MockDrmDevice() override; + bool CommitPropertiesInternal( + drmModeAtomicReq* request, + uint32_t flags, + uint32_t crtc_count, + scoped_refptr<PageFlipRequest> callback) override; + bool UpdateProperty(uint32_t id, uint64_t value, std::vector<DrmDevice::Property>* properties); @@ -210,6 +231,7 @@ class MockDrmDevice : public DrmDevice { int commit_count_ = 0; int set_object_property_count_ = 0; int set_gamma_ramp_count_ = 0; + int last_planes_committed_count_ = 0; bool set_crtc_expectation_; bool add_framebuffer_expectation_; @@ -217,8 +239,10 @@ class MockDrmDevice : public DrmDevice { bool create_dumb_buffer_expectation_; bool legacy_gamma_ramp_expectation_ = false; bool commit_expectation_ = true; + bool modeset_with_overlays_expectation_ = true; uint32_t current_framebuffer_; + uint32_t plane_crtc_id_prop_id_ = 0; std::vector<sk_sp<SkSurface>> buffers_; @@ -237,12 +261,14 @@ class MockDrmDevice : public DrmDevice { std::map<uint32_t, std::string> property_names_; - // TODO(dnicoara): Generate all IDs internal to MockDrmDevice. - // For now generate something with a high enough ID to be unique in tests. - uint32_t property_id_generator_ = 0xff000000; - std::set<uint32_t> allocated_property_blobs_; + // Props of the plane associated with the generated fb_id. + base::flat_map<uint32_t /*fb_id*/, FramebufferProps> fb_props_; + + uint64_t system_watermark_limitations_ = std::numeric_limits<uint64_t>::max(); + base::flat_map<uint64_t /*modifier*/, int /*overhead*/> modifiers_overhead_; + DISALLOW_COPY_AND_ASSIGN(MockDrmDevice); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc index c65c5f6825c..af364e9c0e4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc +++ b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc @@ -6,6 +6,8 @@ #include <utility> +#include "base/bind.h" + namespace ui { void PostSyncTask( diff --git a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h index d5be9bd74e3..c488aea5a1d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h +++ b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h @@ -7,7 +7,7 @@ #include <utility> -#include "base/bind.h" +#include "base/bind_post_task.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/synchronization/waitable_event.h" @@ -15,19 +15,6 @@ namespace ui { -namespace internal { - -template <typename... Args> -void PostAsyncTask( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - base::OnceCallback<void(Args...)> callback, - Args... args) { - auto closure = base::BindOnce(std::move(callback), std::move(args)...); - task_runner->PostTask(FROM_HERE, std::move(closure)); -} - -} // namespace internal - // Posts a task to a different thread and blocks waiting for the task to finish // executing. void PostSyncTask( @@ -40,10 +27,10 @@ void PostSyncTask( // thread). template <typename... Args> base::RepeatingCallback<void(Args...)> CreateSafeRepeatingCallback( - base::RepeatingCallback<void(Args...)> callback) { - return base::BindRepeating(&internal::PostAsyncTask<Args...>, - base::ThreadTaskRunnerHandle::Get(), - std::move(callback)); + base::RepeatingCallback<void(Args...)> callback, + const base::Location& location = FROM_HERE) { + return base::BindPostTask(base::ThreadTaskRunnerHandle::Get(), + std::move(callback), location); } // Creates a OnceCallback that will run |callback| on the calling thread. Useful @@ -51,10 +38,10 @@ base::RepeatingCallback<void(Args...)> CreateSafeRepeatingCallback( // task finished (and the callback needs to run on the original thread). template <typename... Args> base::OnceCallback<void(Args...)> CreateSafeOnceCallback( - base::OnceCallback<void(Args...)> callback) { - return base::BindOnce(&internal::PostAsyncTask<Args...>, - base::ThreadTaskRunnerHandle::Get(), - std::move(callback)); + base::OnceCallback<void(Args...)> callback, + const base::Location& location = FROM_HERE) { + return base::BindPostTask(base::ThreadTaskRunnerHandle::Get(), + std::move(callback), location); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc index 5a8319fe9cd..127f4d87257 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc @@ -10,6 +10,7 @@ #include "base/files/platform_file.h" #include "base/logging.h" +#include "base/trace_event/trace_event.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/display/types/display_snapshot.h" @@ -36,7 +37,7 @@ namespace { bool FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, HardwareDisplayController* controller, SkSurface* surface, - uint32_t fourcc_format) { + const std::vector<uint64_t>& modifiers) { DCHECK(!controller->crtc_controllers().empty()); CrtcController* first_crtc = controller->crtc_controllers()[0].get(); ScopedDrmCrtcPtr saved_crtc(drm->GetCrtc(first_crtc->crtc())); @@ -45,7 +46,6 @@ bool FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, return false; } - const auto& modifiers = controller->GetFormatModifiers(fourcc_format); for (const uint64_t modifier : modifiers) { // A value of 0 means DRM_FORMAT_MOD_NONE. If the CRTC has any other // modifier (tiling, compression, etc.) we can't read the fb and assume it's @@ -77,7 +77,7 @@ bool FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, // Copy the source buffer. Do not perform any blending. paint.setBlendMode(SkBlendMode::kSrc); surface->getCanvas()->drawImage(saved_buffer.surface()->makeImageSnapshot(), - 0, 0, &paint); + 0, 0, SkSamplingOptions(), &paint); return true; } @@ -93,19 +93,6 @@ CrtcController* GetCrtcController(HardwareDisplayController* controller, return nullptr; } -std::vector<uint64_t> GetModifiersForPrimaryFormat( - HardwareDisplayController* controller) { - gfx::BufferFormat format = display::DisplaySnapshot::PrimaryFormat(); - uint32_t fourcc_format = ui::GetFourCCFormatForOpaqueFramebuffer(format); - return controller->GetFormatModifiersForModesetting(fourcc_format); -} - -bool AreAllStatusesTrue(base::flat_map<int64_t, bool>& display_statuses) { - auto it = find_if(display_statuses.begin(), display_statuses.end(), - [](const auto status) { return status.second == false; }); - return (it == display_statuses.end()); -} - } // namespace ScreenManager::ScreenManager() = default; @@ -175,6 +162,9 @@ void ScreenManager::AddDisplayController(const scoped_refptr<DrmDevice>& drm, void ScreenManager::RemoveDisplayControllers( const CrtcsWithDrmList& controllers_to_remove) { + TRACE_EVENT1("drm", "ScreenManager::RemoveDisplayControllers", + "display_count", controllers_to_remove.size()); + // Split them to different lists unique to each DRM Device. base::flat_map<scoped_refptr<DrmDevice>, CrtcsWithDrmList> controllers_for_drm_devices; @@ -223,8 +213,10 @@ void ScreenManager::RemoveDisplayControllers( UpdateControllerToWindowMapping(); } -base::flat_map<int64_t, bool> ScreenManager::ConfigureDisplayControllers( +bool ScreenManager::ConfigureDisplayControllers( const ControllerConfigsList& controllers_params) { + TRACE_EVENT0("drm", "ScreenManager::ConfigureDisplayControllers"); + // Split them to different lists unique to each DRM Device. base::flat_map<scoped_refptr<DrmDevice>, ControllerConfigsList> displays_for_drm_devices; @@ -238,33 +230,81 @@ base::flat_map<int64_t, bool> ScreenManager::ConfigureDisplayControllers( displays_for_drm_devices[params.drm].emplace_back(params); } - base::flat_map<int64_t, bool> statuses; + bool config_success = true; // Perform display configurations together for the same DRM only. for (const auto& configs_on_drm : displays_for_drm_devices) { - auto display_statuses = TestAndModeset(configs_on_drm.second); - statuses.insert(display_statuses.begin(), display_statuses.end()); + const ControllerConfigsList& controllers_params = configs_on_drm.second; + bool test_modeset = TestAndSetPreferredModifiers(controllers_params) || + TestAndSetLinearModifier(controllers_params); + config_success &= test_modeset; + if (!test_modeset) + continue; + bool can_modeset_with_overlays = + TestModesetWithOverlays(controllers_params); + config_success &= Modeset(controllers_params, can_modeset_with_overlays); } - if (AreAllStatusesTrue(statuses)) + if (config_success) UpdateControllerToWindowMapping(); - return statuses; + return config_success; } -base::flat_map<int64_t, bool> ScreenManager::TestAndModeset( +bool ScreenManager::TestAndSetPreferredModifiers( const ControllerConfigsList& controllers_params) { - if (!TestModeset(controllers_params)) { - base::flat_map<int64_t, bool> statuses; - for (const auto& params : controllers_params) - statuses.insert(std::make_pair(params.display_id, false)); - return statuses; + TRACE_EVENT1("drm", "ScreenManager::TestAndSetPreferredModifiers", + "display_count", controllers_params.size()); + + CrtcPreferredModifierMap crtcs_preferred_modifier; + CommitRequest commit_request; + auto drm = controllers_params[0].drm; + + for (const auto& params : controllers_params) { + auto it = FindDisplayController(params.drm, params.crtc); + DCHECK(controllers_.end() != it); + HardwareDisplayController* controller = it->get(); + + if (params.mode) { + uint32_t fourcc_format = ui::GetFourCCFormatForOpaqueFramebuffer( + display::DisplaySnapshot::PrimaryFormat()); + std::vector<uint64_t> modifiers = + controller->GetFormatModifiersForTestModeset(fourcc_format); + // Test with no overlays to go for a lower bandwidth usage. + DrmOverlayPlaneList modeset_planes = GetModesetPlanes( + controller, gfx::Rect(params.origin, ModeSize(*params.mode)), + modifiers, /*include_overlays=*/false, /*is_testing=*/true); + if (modeset_planes.empty()) + return false; + + uint64_t primary_modifier = + DrmOverlayPlane::GetPrimaryPlane(modeset_planes) + ->buffer->format_modifier(); + crtcs_preferred_modifier[params.crtc] = + std::make_pair(modifiers.empty(), primary_modifier); + + GetModesetControllerProps(&commit_request, controller, params.origin, + *params.mode, modeset_planes); + } else { + controller->GetDisableProps(&commit_request); + } + } + + if (!drm->plane_manager()->Commit( + std::move(commit_request), + DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET)) { + return false; } - return Modeset(controllers_params); + SetPreferredModifiers(controllers_params, crtcs_preferred_modifier); + return true; } -bool ScreenManager::TestModeset( +bool ScreenManager::TestAndSetLinearModifier( const ControllerConfigsList& controllers_params) { + TRACE_EVENT1("drm", "ScreenManager::TestAndSetLinearModifier", + "display_count", controllers_params.size()); + + CrtcPreferredModifierMap crtcs_preferred_modifier; CommitRequest commit_request; auto drm = controllers_params[0].drm; @@ -273,65 +313,151 @@ bool ScreenManager::TestModeset( DCHECK(controllers_.end() != it); HardwareDisplayController* controller = it->get(); + uint32_t fourcc_format = ui::GetFourCCFormatForOpaqueFramebuffer( + display::DisplaySnapshot::PrimaryFormat()); + std::vector<uint64_t> modifiers = + controller->GetFormatModifiersForTestModeset(fourcc_format); + // Test with an empty list if no preferred modifiers are advertised. + // Platforms might not support gbm_bo_create_with_modifiers(). If the + // platform doesn't expose modifiers, do not attempt to explicitly request + // LINEAR otherwise we might CHECK() when trying to allocate buffers. + if (!modifiers.empty()) + modifiers = std::vector<uint64_t>{DRM_FORMAT_MOD_LINEAR}; + crtcs_preferred_modifier[params.crtc] = + std::make_pair(modifiers.empty(), DRM_FORMAT_MOD_LINEAR); + if (params.mode) { - DrmOverlayPlane primary_plane = GetModesetBuffer( + // Test with no overlays to go for a lower bandwidth usage. + DrmOverlayPlaneList modeset_planes = GetModesetPlanes( controller, gfx::Rect(params.origin, ModeSize(*params.mode)), - GetModifiersForPrimaryFormat(controller)); - if (!primary_plane.buffer) + modifiers, /*include_overlays=*/false, /*is_testing=*/true); + if (modeset_planes.empty()) return false; GetModesetControllerProps(&commit_request, controller, params.origin, - *params.mode, primary_plane); + *params.mode, modeset_planes); + } else { + controller->GetDisableProps(&commit_request); + } + } + + if (!drm->plane_manager()->Commit( + std::move(commit_request), + DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET)) { + return false; + } + + SetPreferredModifiers(controllers_params, crtcs_preferred_modifier); + return true; +} + +void ScreenManager::SetPreferredModifiers( + const ControllerConfigsList& controllers_params, + const CrtcPreferredModifierMap& crtcs_preferred_modifier) { + for (const auto& params : controllers_params) { + if (params.mode) { + bool was_modifiers_list_empty = + crtcs_preferred_modifier.at(params.crtc).first; + // No preferred modifiers should be saved as some platforms might not have + // bo_create_with_modifiers implemented, this will send the preferred + // modifiers list as an empty list. + if (!was_modifiers_list_empty) { + uint64_t picked_modifier = + crtcs_preferred_modifier.at(params.crtc).second; + auto it = FindDisplayController(params.drm, params.crtc); + DCHECK(*it); + it->get()->UpdatePreferredModiferForFormat( + display::DisplaySnapshot::PrimaryFormat(), picked_modifier); + } + } + } +} + +bool ScreenManager::TestModesetWithOverlays( + const ControllerConfigsList& controllers_params) { + TRACE_EVENT1("drm", "ScreenManager::TestModesetWithOverlays", "display_count", + controllers_params.size()); + + bool does_an_overlay_exist = false; + + CommitRequest commit_request; + auto drm = controllers_params[0].drm; + for (const auto& params : controllers_params) { + auto it = FindDisplayController(params.drm, params.crtc); + DCHECK(controllers_.end() != it); + HardwareDisplayController* controller = it->get(); + + if (params.mode) { + uint32_t fourcc_format = GetFourCCFormatForOpaqueFramebuffer( + display::DisplaySnapshot::PrimaryFormat()); + std::vector<uint64_t> modifiers = + controller->GetSupportedModifiers(fourcc_format); + + DrmOverlayPlaneList modeset_planes = GetModesetPlanes( + controller, gfx::Rect(params.origin, ModeSize(*params.mode)), + modifiers, /*include_overlays=*/true, /*is_testing=*/true); + DCHECK(!modeset_planes.empty()); + does_an_overlay_exist |= modeset_planes.size() > 1; + + GetModesetControllerProps(&commit_request, controller, params.origin, + *params.mode, modeset_planes); } else { controller->GetDisableProps(&commit_request); } } + // If we have no overlays, report not modesetting with overlays as we haven't + // tested with overlays. + if (!does_an_overlay_exist) + return false; return drm->plane_manager()->Commit( std::move(commit_request), DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET); } -base::flat_map<int64_t, bool> ScreenManager::Modeset( - const ControllerConfigsList& controllers_params) { - base::flat_map<int64_t, bool> statuses; +bool ScreenManager::Modeset(const ControllerConfigsList& controllers_params, + bool can_modeset_with_overlays) { + TRACE_EVENT2("drm", "ScreenManager::Modeset", "display_count", + controllers_params.size(), "modeset_with_overlays", + can_modeset_with_overlays); + + CommitRequest commit_request; + auto drm = controllers_params[0].drm; for (const auto& params : controllers_params) { - // Commit one controller at a time. - CommitRequest commit_request; - bool status = true; if (params.mode) { auto it = FindDisplayController(params.drm, params.crtc); DCHECK(controllers_.end() != it); HardwareDisplayController* controller = it->get(); - DrmOverlayPlane primary_plane = GetModesetBuffer( - controller, gfx::Rect(params.origin, ModeSize(*params.mode)), - GetModifiersForPrimaryFormat(controller)); - if (primary_plane.buffer) { - SetDisplayControllerForEnableAndGetProps( - &commit_request, params.drm, params.crtc, params.connector, - params.origin, *params.mode, primary_plane); - } else { - status = false; - } + uint32_t fourcc_format = GetFourCCFormatForOpaqueFramebuffer( + display::DisplaySnapshot::PrimaryFormat()); + std::vector<uint64_t> modifiers = + controller->GetSupportedModifiers(fourcc_format); + + gfx::Rect bounds = gfx::Rect(params.origin, ModeSize(*params.mode)); + DrmOverlayPlaneList modeset_planes = + GetModesetPlanes(controller, bounds, modifiers, + can_modeset_with_overlays, /*is_testing=*/false); + + SetDisplayControllerForEnableAndGetProps( + &commit_request, params.drm, params.crtc, params.connector, + params.origin, *params.mode, modeset_planes); } else { - status = SetDisableDisplayControllerForDisableAndGetProps( + bool disable_set = SetDisableDisplayControllerForDisableAndGetProps( &commit_request, params.drm, params.crtc); + if (!disable_set) + return false; } + } - CommitRequest request_for_update = commit_request; - if (status) { - status &= params.drm->plane_manager()->Commit( - std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); - UpdateControllerStateAfterModeset(params, request_for_update, status); - } + bool commit_status = drm->plane_manager()->Commit( + commit_request, DRM_MODE_ATOMIC_ALLOW_MODESET); - statuses.insert(std::make_pair(params.display_id, status)); - } + UpdateControllerStateAfterModeset(drm, commit_request, commit_status); - return statuses; + return commit_status; } void ScreenManager::SetDisplayControllerForEnableAndGetProps( @@ -341,7 +467,7 @@ void ScreenManager::SetDisplayControllerForEnableAndGetProps( uint32_t connector, const gfx::Point& origin, const drmModeModeInfo& mode, - const DrmOverlayPlane& primary) { + const DrmOverlayPlaneList& modeset_planes) { HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc); DCHECK(controllers_.end() != it) << "Display controller (crtc=" << crtc << ") doesn't exist."; @@ -358,10 +484,10 @@ void ScreenManager::SetDisplayControllerForEnableAndGetProps( // Otherwise it could apply a mode with the same resolution and refresh // rate but with different timings to the other CRTC. GetModesetControllerProps(commit_request, controller, - controller->origin(), mode, primary); + controller->origin(), mode, modeset_planes); } else { // Just get props to re-enable the controller re-using the current state. - GetEnableControllerProps(commit_request, controller, primary); + GetEnableControllerProps(commit_request, controller, modeset_planes); } return; } @@ -377,7 +503,8 @@ void ScreenManager::SetDisplayControllerForEnableAndGetProps( controller = it->get(); } - GetModesetControllerProps(commit_request, controller, origin, mode, primary); + GetModesetControllerProps(commit_request, controller, origin, mode, + modeset_planes); } bool ScreenManager::SetDisableDisplayControllerForDisableAndGetProps( @@ -402,31 +529,32 @@ bool ScreenManager::SetDisableDisplayControllerForDisableAndGetProps( } void ScreenManager::UpdateControllerStateAfterModeset( - const ControllerConfigParams& config, + const scoped_refptr<DrmDevice>& drm, const CommitRequest& commit_request, bool did_succeed) { - for (auto& crtc_request : commit_request) { + for (const CrtcCommitRequest& crtc_request : commit_request) { bool was_enabled = (crtc_request.should_enable()); HardwareDisplayControllers::iterator it = - FindDisplayController(config.drm, crtc_request.crtc_id()); + FindDisplayController(drm, crtc_request.crtc_id()); if (it != controllers_.end()) { - it->get()->UpdateState(was_enabled, DrmOverlayPlane::GetPrimaryPlane( - crtc_request.overlays())); + it->get()->UpdateState(crtc_request); // If the CRTC is mirrored, move it to the mirror controller. if (did_succeed && was_enabled) - HandleMirrorIfExists(config, it); + HandleMirrorIfExists(drm, crtc_request, it); } } } void ScreenManager::HandleMirrorIfExists( - const ControllerConfigParams& config, + const scoped_refptr<DrmDevice>& drm, + const CrtcCommitRequest& crtc_request, const HardwareDisplayControllers::iterator& controller) { - gfx::Rect modeset_bounds(config.origin, ModeSize(*config.mode)); + gfx::Rect modeset_bounds(crtc_request.origin(), + ModeSize(crtc_request.mode())); HardwareDisplayControllers::iterator mirror = - FindActiveDisplayControllerByLocation(config.drm, modeset_bounds); + FindActiveDisplayControllerByLocation(drm, modeset_bounds); // TODO(dnicoara): This is hacky, instead the DrmDisplay and // CrtcController should be merged and picking the mode should be done // properly within HardwareDisplayController. @@ -434,7 +562,7 @@ void ScreenManager::HandleMirrorIfExists( // TODO(markyacoub): RemoveCrtc makes a blocking commit to // DisableOverlayPlanes. This should be redesigned and included as part of // the Modeset commit. - (*mirror)->AddCrtc((*controller)->RemoveCrtc(config.drm, config.crtc)); + (*mirror)->AddCrtc((*controller)->RemoveCrtc(drm, crtc_request.crtc_id())); controllers_.erase(controller); } } @@ -541,24 +669,30 @@ void ScreenManager::UpdateControllerToWindowMapping() { // otherwise the controller may be waiting for a page flip while the window // tries to schedule another buffer. if (should_enable) { - DrmOverlayPlane primary_plane = GetModesetBuffer( + uint32_t fourcc_format = ui::GetFourCCFormatForOpaqueFramebuffer( + display::DisplaySnapshot::PrimaryFormat()); + std::vector<uint64_t> modifiers = + controller->GetSupportedModifiers(fourcc_format); + DrmOverlayPlaneList modeset_planes = GetModesetPlanes( controller, - gfx::Rect(controller->origin(), controller->GetModeSize()), - GetModifiersForPrimaryFormat(controller)); - DCHECK(primary_plane.buffer); + gfx::Rect(controller->origin(), controller->GetModeSize()), modifiers, + /*include_overlays=*/true, /*is_testing=*/false); + DCHECK(!modeset_planes.empty()); CommitRequest commit_request; - GetEnableControllerProps(&commit_request, controller, primary_plane); + GetEnableControllerProps(&commit_request, controller, modeset_planes); controller->GetDrmDevice()->plane_manager()->Commit( std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET); } } } -DrmOverlayPlane ScreenManager::GetModesetBuffer( +DrmOverlayPlaneList ScreenManager::GetModesetPlanes( HardwareDisplayController* controller, const gfx::Rect& bounds, - const std::vector<uint64_t>& modifiers) { + const std::vector<uint64_t>& modifiers, + bool include_overlays, + bool is_testing) { scoped_refptr<DrmDevice> drm = controller->GetDrmDevice(); uint32_t fourcc_format = ui::GetFourCCFormatForOpaqueFramebuffer( display::DisplaySnapshot::PrimaryFormat()); @@ -569,19 +703,27 @@ DrmOverlayPlane ScreenManager::GetModesetBuffer( fourcc_format, bounds.size(), GBM_BO_USE_SCANOUT, modifiers); if (!buffer) { LOG(ERROR) << "Failed to create scanout buffer"; - return DrmOverlayPlane::Error(); + return DrmOverlayPlaneList(); } // If the current primary plane matches what we need for the next page flip, - // we can clone it. + // clone all last_submitted_planes (matching primary + overlays). DrmWindow* window = FindWindowAt(bounds); if (window) { - const DrmOverlayPlane* primary = window->GetLastModesetBuffer(); - const DrmDevice* drm = controller->GetDrmDevice().get(); + const DrmOverlayPlaneList& last_submitted_planes = + window->last_submitted_planes(); + const DrmOverlayPlane* primary = + DrmOverlayPlane::GetPrimaryPlane(last_submitted_planes); if (primary && primary->buffer->size() == bounds.size() && - primary->buffer->drm_device() == drm) { - if (primary->buffer->format_modifier() == buffer->GetFormatModifier()) - return primary->Clone(); + primary->buffer->drm_device() == controller->GetDrmDevice().get() && + primary->buffer->format_modifier() == buffer->GetFormatModifier()) { + if (include_overlays) { + return DrmOverlayPlane::Clone(last_submitted_planes); + } else { + DrmOverlayPlaneList modeset_plane; + modeset_plane.push_back(primary->Clone()); + return modeset_plane; + } } } @@ -589,28 +731,32 @@ DrmOverlayPlane ScreenManager::GetModesetBuffer( drm, buffer.get(), buffer->GetSize(), modifiers); if (!framebuffer) { LOG(ERROR) << "Failed to add framebuffer for scanout buffer"; - return DrmOverlayPlane::Error(); + return DrmOverlayPlaneList(); } - sk_sp<SkSurface> surface = buffer->GetSurface(); - if (!surface) { - VLOG(2) << "Can't get a SkSurface from the modeset gbm buffer."; - } else if (!FillModesetBuffer(drm, controller, surface.get(), - buffer->GetFormat())) { - // If we fail to fill the modeset buffer, clear it black to avoid displaying - // an uninitialized framebuffer. - surface->getCanvas()->clear(SK_ColorBLACK); + if (!is_testing) { + sk_sp<SkSurface> surface = buffer->GetSurface(); + if (!surface) { + VLOG(2) << "Can't get a SkSurface from the modeset gbm buffer."; + } else if (!FillModesetBuffer(drm, controller, surface.get(), modifiers)) { + // If we fail to fill the modeset buffer, clear it black to avoid + // displaying an uninitialized framebuffer. + surface->getCanvas()->clear(SK_ColorBLACK); + } } - return DrmOverlayPlane(framebuffer, nullptr); + + DrmOverlayPlaneList modeset_planes; + modeset_planes.emplace_back(framebuffer, nullptr); + return modeset_planes; } void ScreenManager::GetEnableControllerProps( CommitRequest* commit_request, HardwareDisplayController* controller, - const DrmOverlayPlane& primary) { + const DrmOverlayPlaneList& modeset_planes) { DCHECK(!controller->crtc_controllers().empty()); - controller->GetEnableProps(commit_request, primary); + controller->GetEnableProps(commit_request, modeset_planes); } void ScreenManager::GetModesetControllerProps( @@ -618,11 +764,11 @@ void ScreenManager::GetModesetControllerProps( HardwareDisplayController* controller, const gfx::Point& origin, const drmModeModeInfo& mode, - const DrmOverlayPlane& primary) { + const DrmOverlayPlaneList& modeset_planes) { DCHECK(!controller->crtc_controllers().empty()); controller->set_origin(origin); - controller->GetModesetProps(commit_request, primary, mode); + controller->GetModesetProps(commit_request, modeset_planes, mode); } DrmWindow* ScreenManager::FindWindowAt(const gfx::Rect& bounds) const { diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h index d7ac2810b53..c102c52525d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h @@ -68,7 +68,7 @@ class ScreenManager { void RemoveDisplayControllers(const CrtcsWithDrmList& controllers_to_remove); // Enables/Disables the display controller based on if a mode exists. - base::flat_map<int64_t, bool> ConfigureDisplayControllers( + bool ConfigureDisplayControllers( const ControllerConfigsList& controllers_params); // Returns a reference to the display controller configured to display within @@ -98,6 +98,9 @@ class ScreenManager { std::vector<std::unique_ptr<HardwareDisplayController>>; using WidgetToWindowMap = std::unordered_map<gfx::AcceleratedWidget, std::unique_ptr<DrmWindow>>; + using CrtcPreferredModifierMap = base::flat_map< + uint32_t /*crtc_is*/, + std::pair<bool /*modifiers_list.empty()*/, uint64_t /*picked_modifier*/>>; // Returns an iterator into |controllers_| for the controller identified by // (|crtc|, |connector|). @@ -105,13 +108,21 @@ class ScreenManager { const scoped_refptr<DrmDevice>& drm, uint32_t crtc); - base::flat_map<int64_t, bool> TestAndModeset( + bool TestAndSetPreferredModifiers( const ControllerConfigsList& controllers_params); - - bool TestModeset(const ControllerConfigsList& controllers_params); - - base::flat_map<int64_t, bool> Modeset( + bool TestAndSetLinearModifier( const ControllerConfigsList& controllers_params); + // Setting the Preferred modifiers that passed from one of the Modeset Test + // functions. The preferred modifiers are used in Modeset. + void SetPreferredModifiers( + const ControllerConfigsList& controllers_params, + const CrtcPreferredModifierMap& crtcs_preferred_modifier); + // The planes used for modesetting can have overlays beside the primary, test + // if we can modeset with them. If not, return false to indicate that we must + // only use the primary plane. + bool TestModesetWithOverlays(const ControllerConfigsList& controllers_params); + bool Modeset(const ControllerConfigsList& controllers_params, + bool can_modeset_with_overlays); // Configures a display controller to be enabled. The display controller is // identified by (|crtc|, |connector|) and the controller is to be modeset @@ -123,7 +134,7 @@ class ScreenManager { uint32_t connector, const gfx::Point& origin, const drmModeModeInfo& mode, - const DrmOverlayPlane& primary); + const DrmOverlayPlaneList& modeset_planes); // Configures a display controller to be disabled. The display controller is // identified by |crtc|. Controller modeset props are added into @@ -135,12 +146,13 @@ class ScreenManager { const scoped_refptr<DrmDevice>& drm, uint32_t crtc); - void UpdateControllerStateAfterModeset(const ControllerConfigParams& config, + void UpdateControllerStateAfterModeset(const scoped_refptr<DrmDevice>& drm, const CommitRequest& commit_request, bool did_succeed); void HandleMirrorIfExists( - const ControllerConfigParams& config, + const scoped_refptr<DrmDevice>& drm, + const CrtcCommitRequest& crtc_request, const HardwareDisplayControllers::iterator& controller); // Returns an iterator into |controllers_| for the controller located at @@ -154,19 +166,21 @@ class ScreenManager { const scoped_refptr<DrmDevice>& drm, const gfx::Rect& bounds); - DrmOverlayPlane GetModesetBuffer(HardwareDisplayController* controller, - const gfx::Rect& bounds, - const std::vector<uint64_t>& modifiers); + DrmOverlayPlaneList GetModesetPlanes(HardwareDisplayController* controller, + const gfx::Rect& bounds, + const std::vector<uint64_t>& modifiers, + bool include_overlays, + bool is_testing); // Gets props for modesetting the |controller| using |origin| and |mode|. void GetModesetControllerProps(CommitRequest* commit_request, HardwareDisplayController* controller, const gfx::Point& origin, const drmModeModeInfo& mode, - const DrmOverlayPlane& primary); + const DrmOverlayPlaneList& modeset_planes); void GetEnableControllerProps(CommitRequest* commit_request, HardwareDisplayController* controller, - const DrmOverlayPlane& primary); + const DrmOverlayPlaneList& modeset_planes); DrmWindow* FindWindowAt(const gfx::Rect& bounds) const; diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc index d4a1a691a1e..a98d358da52 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc @@ -47,6 +47,7 @@ constexpr uint32_t kSecondaryConnector = kConnectorIdBase + 1; constexpr uint32_t kPlaneIdBase = 300; constexpr uint32_t kInFormatsBlobPropIdBase = 400; +constexpr uint32_t kFbIdPropId = 3005; constexpr uint32_t kTypePropId = 3010; constexpr uint32_t kInFormatsPropId = 3011; @@ -81,7 +82,8 @@ class ScreenManagerTest : public testing::Test { void InitializeDrmState(ui::MockDrmDevice* drm, const std::vector<CrtcState>& crtc_states, - bool is_atomic = true) { + bool is_atomic, + bool use_modifiers_list = false) { std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties( crtc_states.size()); std::map<uint32_t, std::string> crtc_property_names = { @@ -109,7 +111,7 @@ class ScreenManagerTest : public testing::Test { {3002, "CRTC_Y"}, {3003, "CRTC_W"}, {3004, "CRTC_H"}, - {3005, "FB_ID"}, + {kFbIdPropId, "FB_ID"}, {3006, "SRC_X"}, {3007, "SRC_Y"}, {3008, "SRC_W"}, @@ -143,9 +145,17 @@ class ScreenManagerTest : public testing::Test { : DRM_PLANE_TYPE_OVERLAY; } else if (pair.first == kInFormatsPropId) { value = property_id++; + std::vector<drm_format_modifier> drm_format_modifiers; + if (use_modifiers_list) { + for (const auto modifier : supported_modifiers_) { + drm_format_modifiers.push_back( + {/*formats=*/1, /*offset=*/0, /*pad=*/0, modifier}); + } + } + drm->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob( value, crtc_states[crtc_idx].planes[plane_idx].formats, - std::vector<drm_format_modifier>())); + std::move(drm_format_modifiers))); } crtc_plane_properties[plane_idx].properties.push_back( @@ -165,12 +175,15 @@ class ScreenManagerTest : public testing::Test { connector_property_names.end()); property_names.insert(plane_property_names.begin(), plane_property_names.end()); + + drm->SetModifiersOverhead(modifiers_overhead_); drm->InitializeState(crtc_properties, connector_properties, plane_properties, property_names, is_atomic); } void InitializeDrmStateWithDefault(ui::MockDrmDevice* drm, - bool is_atomic = true) { + bool is_atomic, + bool use_modifiers_list = false) { // A Sample of CRTC states. std::vector<CrtcState> crtc_states = { {/* .planes = */ @@ -182,11 +195,12 @@ class ScreenManagerTest : public testing::Test { {/* .formats = */ {DRM_FORMAT_XRGB8888}}, }}, }; - InitializeDrmState(drm, crtc_states, is_atomic); + InitializeDrmState(drm, crtc_states, is_atomic, use_modifiers_list); } void SetUp() override { auto gbm = std::make_unique<ui::MockGbmDevice>(); + supported_modifiers_ = gbm->GetSupportedModifiers(); drm_ = new ui::MockDrmDevice(std::move(gbm)); device_manager_ = std::make_unique<ui::DrmDeviceManager>(nullptr); screen_manager_ = std::make_unique<ui::ScreenManager>(); @@ -218,6 +232,10 @@ class ScreenManagerTest : public testing::Test { scoped_refptr<ui::MockDrmDevice> drm_; std::unique_ptr<ui::DrmDeviceManager> device_manager_; std::unique_ptr<ui::ScreenManager> screen_manager_; + std::vector<uint64_t> supported_modifiers_; + base::flat_map<uint64_t /*modifier*/, int /*overhead*/> modifiers_overhead_{ + {DRM_FORMAT_MOD_LINEAR, 1}, + {I915_FORMAT_MOD_Yf_TILED_CCS, 100}}; private: DISALLOW_COPY_AND_ASSIGN(ScreenManagerTest); @@ -231,7 +249,7 @@ TEST_F(ScreenManagerTest, CheckWithNoControllers) { } TEST_F(ScreenManagerTest, CheckWithValidController) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); @@ -252,7 +270,7 @@ TEST_F(ScreenManagerTest, CheckWithValidController) { } TEST_F(ScreenManagerTest, CheckWithInvalidBounds) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); @@ -268,7 +286,7 @@ TEST_F(ScreenManagerTest, CheckWithInvalidBounds) { } TEST_F(ScreenManagerTest, CheckForSecondValidController) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -287,14 +305,104 @@ TEST_F(ScreenManagerTest, CheckForSecondValidController) { screen_manager_->ConfigureDisplayControllers(controllers_to_enable); EXPECT_EQ(drm_->get_test_modeset_count(), 1); - EXPECT_EQ(drm_->get_commit_modeset_count(), 2); + EXPECT_EQ(drm_->get_commit_modeset_count(), 1); EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds())); } +TEST_F(ScreenManagerTest, CheckMultipleDisplaysWithinModifiersLimit) { + int max_supported_displays_with_modifier = 2; + drm_->SetSystemLimitOfModifiers( + modifiers_overhead_[I915_FORMAT_MOD_Yf_TILED_CCS] * + max_supported_displays_with_modifier); + + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true, + /*use_modifiers_list=*/true); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, + kSecondaryConnector); + + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + EXPECT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + EXPECT_EQ(drm_->get_test_modeset_count(), 1); + EXPECT_EQ(drm_->get_commit_modeset_count(), 1); +} + +TEST_F(ScreenManagerTest, CheckMultipleDisplaysOutsideModifiersLimit) { + int max_supported_displays_with_modifier = 2; + drm_->SetSystemLimitOfModifiers(modifiers_overhead_[DRM_FORMAT_MOD_LINEAR] * + max_supported_displays_with_modifier); + + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true, + /*use_modifiers_list=*/true); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, + kSecondaryConnector); + + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + EXPECT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + // Testing for a failed test-modeset with modifiers + a fallback to Linear + // Modifier and a modeset commit. + EXPECT_EQ(drm_->get_test_modeset_count(), 2); + EXPECT_EQ(drm_->get_commit_modeset_count(), 1); +} + +TEST_F(ScreenManagerTest, CheckDisplaysWith0Limit) { + drm_->SetSystemLimitOfModifiers(0); + + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true, + /*use_modifiers_list=*/true); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, + kSecondaryConnector); + + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + EXPECT_FALSE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + // Testing for a failed test-modeset with modifiers + failed test-modeset with + // Linear Modifier and no modeset due to failed tests. + EXPECT_EQ(drm_->get_test_modeset_count(), 2); + EXPECT_EQ(drm_->get_commit_modeset_count(), 0); +} + TEST_F(ScreenManagerTest, CheckControllerAfterItIsRemoved) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); @@ -314,7 +422,7 @@ TEST_F(ScreenManagerTest, CheckControllerAfterItIsRemoved) { } TEST_F(ScreenManagerTest, CheckControllerAfterDisabled) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); // Enable @@ -344,7 +452,7 @@ TEST_F(ScreenManagerTest, CheckControllerAfterDisabled) { } TEST_F(ScreenManagerTest, CheckMultipleControllersAfterBeingRemoved) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -375,7 +483,7 @@ TEST_F(ScreenManagerTest, CheckMultipleControllersAfterBeingRemoved) { } TEST_F(ScreenManagerTest, CheckMultipleControllersAfterBeingDisabled) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); @@ -407,7 +515,7 @@ TEST_F(ScreenManagerTest, CheckMultipleControllersAfterBeingDisabled) { EXPECT_EQ(drm_->get_test_modeset_count(), test_modeset_count_before_disable + 1); EXPECT_EQ(drm_->get_commit_modeset_count(), - commit_modeset_count_before_disable + 2); + commit_modeset_count_before_disable + 1); EXPECT_FALSE(screen_manager_->GetDisplayController(GetPrimaryBounds())); EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds())); @@ -442,7 +550,7 @@ TEST_F(ScreenManagerTest, CheckDuplicateConfiguration) { } TEST_F(ScreenManagerTest, CheckChangingMode) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); @@ -478,7 +586,7 @@ TEST_F(ScreenManagerTest, CheckChangingMode) { } TEST_F(ScreenManagerTest, CheckForControllersInMirroredMode) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -517,7 +625,7 @@ TEST_F(ScreenManagerTest, CheckMirrorModeTransitions) { }, }, }; - InitializeDrmState(drm_.get(), crtc_states); + InitializeDrmState(drm_.get(), crtc_states, /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -574,7 +682,7 @@ TEST_F(ScreenManagerTest, CheckMirrorModeTransitions) { // Make sure we're using each display's mode when doing mirror mode otherwise // the timings may be off. TEST_F(ScreenManagerTest, CheckMirrorModeModesettingWithDisplaysMode) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -609,7 +717,7 @@ TEST_F(ScreenManagerTest, CheckMirrorModeModesettingWithDisplaysMode) { } TEST_F(ScreenManagerTest, MonitorGoneInMirrorMode) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -641,23 +749,29 @@ TEST_F(ScreenManagerTest, MonitorGoneInMirrorMode) { } TEST_F(ScreenManagerTest, MonitorDisabledInMirrorMode) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, kSecondaryConnector); - ScreenManager::ControllerConfigsList controllers_to_enable; - controllers_to_enable.emplace_back( - kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, - GetPrimaryBounds().origin(), - std::make_unique<drmModeModeInfo>(kDefaultMode)); - drmModeModeInfo secondary_mode = kDefaultMode; - controllers_to_enable.emplace_back( - kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, - GetPrimaryBounds().origin(), - std::make_unique<drmModeModeInfo>(secondary_mode)); + // Enable in Mirror Mode. + { + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + drmModeModeInfo secondary_mode = kDefaultMode; + controllers_to_enable.emplace_back( + kSecondaryDisplayId, drm_, kSecondaryCrtc, kSecondaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(secondary_mode)); + screen_manager_->ConfigureDisplayControllers(controllers_to_enable); + } + // Disable display Controller. + ScreenManager::ControllerConfigsList controllers_to_enable; controllers_to_enable.emplace_back(0, drm_, kSecondaryCrtc, 0, gfx::Point(), nullptr); screen_manager_->ConfigureDisplayControllers(controllers_to_enable); @@ -672,7 +786,7 @@ TEST_F(ScreenManagerTest, MonitorDisabledInMirrorMode) { } TEST_F(ScreenManagerTest, DoNotEnterMirrorModeUnlessSameBounds) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -741,7 +855,7 @@ TEST_F(ScreenManagerTest, ReuseFramebufferIfDisabledThenReEnabled) { } TEST_F(ScreenManagerTest, CheckMirrorModeAfterBeginReEnabled) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm_, kSecondaryCrtc, @@ -839,8 +953,8 @@ TEST_F(ScreenManagerTest, scoped_refptr<ui::MockDrmDevice> drm2 = new ui::MockDrmDevice(std::move(gbm_device)); - InitializeDrmStateWithDefault(drm_.get()); - InitializeDrmStateWithDefault(drm2.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); + InitializeDrmStateWithDefault(drm2.get(), /*is_atomic=*/true); screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); screen_manager_->AddDisplayController(drm2, kPrimaryCrtc, kPrimaryConnector); @@ -868,7 +982,7 @@ TEST_F(ScreenManagerTest, } TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); @@ -891,7 +1005,7 @@ TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithSameBounds) { } TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); std::unique_ptr<ui::DrmWindow> window( new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); @@ -917,7 +1031,7 @@ TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithDifferentBounds) { TEST_F(ScreenManagerTest, CheckControllerToWindowMappingWithOverlappingWindows) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); const size_t kWindowCount = 2; for (size_t i = 1; i < kWindowCount + 1; ++i) { @@ -948,7 +1062,7 @@ TEST_F(ScreenManagerTest, } TEST_F(ScreenManagerTest, ShouldDissociateWindowOnControllerRemoval) { - InitializeDrmStateWithDefault(drm_.get()); + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); gfx::AcceleratedWidget window_id = 1; std::unique_ptr<ui::DrmWindow> window(new ui::DrmWindow( @@ -1105,12 +1219,12 @@ TEST_F(ScreenManagerTest, ShouldNotHardwareMirrorDifferentDrmDevices) { auto gbm_device1 = std::make_unique<MockGbmDevice>(); auto drm_device1 = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device1)); - InitializeDrmStateWithDefault(drm_device1.get()); + InitializeDrmStateWithDefault(drm_device1.get(), /*is_atomic=*/true); auto gbm_device2 = std::make_unique<MockGbmDevice>(); auto drm_device2 = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device2)); - InitializeDrmStateWithDefault(drm_device2.get()); + InitializeDrmStateWithDefault(drm_device2.get(), /*is_atomic=*/true); DrmDeviceManager drm_device_manager(nullptr); ScreenManager screen_manager; @@ -1310,4 +1424,315 @@ TEST_F(ScreenManagerTest, ShouldNotUnbindFramebufferOnJoiningMirror) { screen_manager.RemoveWindow(1)->Shutdown(); } +TEST_F(ScreenManagerTest, DrmFramebufferSequenceIdIncrementingAtModeset) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); + + scoped_refptr<DrmFramebuffer> pre_modeset_buffer = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + CHECK_EQ(pre_modeset_buffer->modeset_sequence_id_at_allocation(), 0); + + // Successful modeset + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + { + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + } + + scoped_refptr<DrmFramebuffer> first_post_modeset_buffer = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + CHECK_EQ(first_post_modeset_buffer->modeset_sequence_id_at_allocation(), 1); + CHECK_EQ(pre_modeset_buffer->modeset_sequence_id_at_allocation(), 0); + + // Unsuccessful modeset + { + drm_->set_set_crtc_expectation(false); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetSecondaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_FALSE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + } + + scoped_refptr<DrmFramebuffer> second_post_modeset_buffer = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + CHECK_EQ(second_post_modeset_buffer->modeset_sequence_id_at_allocation(), 1); + CHECK_EQ(first_post_modeset_buffer->modeset_sequence_id_at_allocation(), 1); + CHECK_EQ(pre_modeset_buffer->modeset_sequence_id_at_allocation(), 0); +} + +TEST_F(ScreenManagerTest, CloningPlanesOnModeset) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); + + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + scoped_refptr<DrmFramebuffer> buffer = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + ui::DrmOverlayPlaneList planes; + planes.push_back(ui::DrmOverlayPlane(buffer, nullptr)); + window->SchedulePageFlip(std::move(planes), base::DoNothing(), + base::DoNothing()); + screen_manager_->AddWindow(1, std::move(window)); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + buffer)); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + +TEST_F(ScreenManagerTest, CloningMultiplePlanesOnModeset) { + std::vector<CrtcState> crtc_states = {{ + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }, + }}; + InitializeDrmState(drm_.get(), crtc_states, /*is_atomic=*/true); + + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + scoped_refptr<DrmFramebuffer> primary = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + scoped_refptr<DrmFramebuffer> overlay = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + ui::DrmOverlayPlaneList planes; + planes.push_back(ui::DrmOverlayPlane(primary, nullptr)); + planes.push_back(ui::DrmOverlayPlane(overlay, nullptr)); + window->SchedulePageFlip(std::move(planes), base::DoNothing(), + base::DoNothing()); + screen_manager_->AddWindow(1, std::move(window)); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + primary)); + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + overlay)); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + +TEST_F(ScreenManagerTest, ModesetWithClonedPlanesNoOverlays) { + InitializeDrmStateWithDefault(drm_.get(), /*is_atomic=*/true); + + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + scoped_refptr<DrmFramebuffer> buffer = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + ui::DrmOverlayPlaneList planes; + planes.push_back(ui::DrmOverlayPlane(buffer, nullptr)); + window->SchedulePageFlip(std::move(planes), base::DoNothing(), + base::DoNothing()); + screen_manager_->AddWindow(1, std::move(window)); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + ASSERT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + buffer)); + + EXPECT_EQ(drm_->get_test_modeset_count(), 1); + EXPECT_EQ(drm_->last_planes_committed_count(), 1); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + +TEST_F(ScreenManagerTest, ModesetWithClonedPlanesWithOverlaySucceeding) { + std::vector<CrtcState> crtc_states = {{ + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }, + }}; + InitializeDrmState(drm_.get(), crtc_states, /*is_atomic=*/true); + + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + scoped_refptr<DrmFramebuffer> primary = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + scoped_refptr<DrmFramebuffer> overlay = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + ui::DrmOverlayPlaneList planes; + planes.push_back(ui::DrmOverlayPlane(primary, nullptr)); + planes.push_back(ui::DrmOverlayPlane(overlay, nullptr)); + window->SchedulePageFlip(std::move(planes), base::DoNothing(), + base::DoNothing()); + screen_manager_->AddWindow(1, std::move(window)); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + primary)); + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + overlay)); + + EXPECT_EQ(drm_->get_test_modeset_count(), 2); + EXPECT_EQ(drm_->last_planes_committed_count(), 2); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + +TEST_F(ScreenManagerTest, ModesetWithClonedPlanesWithOverlayFailing) { + std::vector<CrtcState> crtc_states = {{ + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }, + }}; + InitializeDrmState(drm_.get(), crtc_states, /*is_atomic=*/true); + + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + scoped_refptr<DrmFramebuffer> primary = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + scoped_refptr<DrmFramebuffer> overlay = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + ui::DrmOverlayPlaneList planes; + planes.push_back(ui::DrmOverlayPlane(primary, nullptr)); + planes.push_back(ui::DrmOverlayPlane(overlay, nullptr)); + window->SchedulePageFlip(std::move(planes), base::DoNothing(), + base::DoNothing()); + screen_manager_->AddWindow(1, std::move(window)); + + drm_->set_overlay_modeset_expecation(false); + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + EXPECT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + EXPECT_TRUE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + primary)); + EXPECT_FALSE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + overlay)); + + EXPECT_EQ(drm_->get_test_modeset_count(), 2); + EXPECT_EQ(drm_->last_planes_committed_count(), 1); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + +TEST_F(ScreenManagerTest, ModesetWithNewBuffersOnModifiersChange) { + std::vector<CrtcState> crtc_states = {{ + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }, + }}; + InitializeDrmState(drm_.get(), crtc_states, /*is_atomic=*/true, + /*use_modifiers_list=*/true); + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + + scoped_refptr<DrmFramebuffer> primary = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + scoped_refptr<DrmFramebuffer> overlay = + CreateBuffer(DRM_FORMAT_XRGB8888, GetPrimaryBounds().size()); + ui::DrmOverlayPlaneList planes; + planes.push_back(ui::DrmOverlayPlane(primary, nullptr)); + planes.push_back(ui::DrmOverlayPlane(overlay, nullptr)); + window->SchedulePageFlip(std::move(planes), base::DoNothing(), + base::DoNothing()); + screen_manager_->AddWindow(1, std::move(window)); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + ScreenManager::ControllerConfigsList controllers_to_enable; + controllers_to_enable.emplace_back( + kPrimaryDisplayId, drm_, kPrimaryCrtc, kPrimaryConnector, + GetPrimaryBounds().origin(), + std::make_unique<drmModeModeInfo>(kDefaultMode)); + ASSERT_TRUE( + screen_manager_->ConfigureDisplayControllers(controllers_to_enable)); + + EXPECT_FALSE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + primary)); + EXPECT_FALSE(base::Contains(drm_->plane_manager() + ->GetCrtcStateForCrtcId(kPrimaryCrtc) + .modeset_framebuffers, + overlay)); + + // Testing test modifiers only, no linear or overlays test. + EXPECT_EQ(drm_->get_test_modeset_count(), 1); + EXPECT_EQ(drm_->last_planes_committed_count(), 1); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor.cc b/chromium/ui/ozone/platform/drm/host/drm_cursor.cc index afed5c26d24..5b433602330 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_cursor.cc @@ -8,11 +8,12 @@ #include <utility> #include "base/trace_event/trace_event.h" +#include "build/chromeos_buildflags.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/ozone/platform/drm/host/drm_window_host.h" #include "ui/ozone/platform/drm/host/drm_window_host_manager.h" -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) #include "ui/events/ozone/chromeos/cursor_controller.h" #endif @@ -192,7 +193,7 @@ void DrmCursor::MoveCursor(const gfx::Vector2dF& delta) { if (window_ == gfx::kNullAcceleratedWidget) return; -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) gfx::Vector2dF transformed_delta = delta; ui::CursorController::GetInstance()->ApplyCursorConfigForWindow( window_, &transformed_delta); @@ -232,7 +233,7 @@ void DrmCursor::SetCursorLocationLocked(const gfx::PointF& location) { gfx::PointF(confined_bounds_.right() - 1, confined_bounds_.bottom() - 1)); location_ = clamped_location; -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) ui::CursorController::GetInstance()->SetCursorLocation(location_); #endif } diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc index 975da5c6ab3..32818432bd4 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc @@ -237,15 +237,11 @@ void DrmDisplayHostManager::UpdateDisplays( void DrmDisplayHostManager::ConfigureDisplays( const std::vector<display::DisplayConfigurationParams>& config_requests, display::ConfigureCallback callback) { - base::flat_map<int64_t, bool> dummy_statuses; - bool is_any_dummy = false; for (auto& config : config_requests) { - is_any_dummy |= GetDisplay(config.id)->is_dummy(); - dummy_statuses.insert(std::make_pair(config.id, true)); - } - if (is_any_dummy) { - std::move(callback).Run(dummy_statuses); - return; + if (GetDisplay(config.id)->is_dummy()) { + std::move(callback).Run(true); + return; + } } proxy_->GpuConfigureNativeDisplays(config_requests, std::move(callback)); diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc index 1c6c2b9ac58..9cd757ceeb6 100644 --- a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc @@ -136,16 +136,11 @@ void HostDrmDevice::GpuConfigureNativeDisplays( if (IsConnected()) { drm_device_->ConfigureNativeDisplays(config_requests, std::move(callback)); } else { - // If not connected, report failure to config. - base::flat_map<int64_t, bool> dummy_statuses; - for (const auto& config : config_requests) - dummy_statuses.insert(std::make_pair(config.id, false)); - // Post this task to protect the callstack from accumulating too many // recursive calls to ConfigureDisplaysTask::Run() in cases in which the GPU // process crashes repeatedly. base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), dummy_statuses)); + FROM_HERE, base::BindOnce(std::move(callback), false)); } } diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_drm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_drm.cc index 8a6442257df..aceccb83583 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_drm.cc +++ b/chromium/ui/ozone/platform/drm/ozone_platform_drm.cc @@ -18,6 +18,7 @@ #include "base/notreached.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_task_runner_handle.h" +#include "build/chromeos_buildflags.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "ui/base/buildflags.h" @@ -58,7 +59,7 @@ #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h" #endif -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) #include "ui/base/ime/chromeos/input_method_chromeos.h" #else #include "ui/base/ime/input_method_minimal.h" @@ -174,7 +175,7 @@ class OzonePlatformDrm : public OzonePlatform { std::unique_ptr<InputMethod> CreateInputMethod( internal::InputMethodDelegate* delegate, gfx::AcceleratedWidget) override { -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) return std::make_unique<InputMethodChromeOS>(delegate); #else return std::make_unique<InputMethodMinimal>(delegate); @@ -230,6 +231,12 @@ class OzonePlatformDrm : public OzonePlatform { } void InitializeGPU(const InitParams& args) override { + // Check if buffer bandwidth compression is disabled + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableBufferBWCompression)) { + setenv("MINIGBM_DEBUG", "nocompression", 1); + } + gpu_task_runner_ = base::ThreadTaskRunnerHandle::Get(); // NOTE: Can't start the thread here since this is called before sandbox @@ -239,8 +246,9 @@ class OzonePlatformDrm : public OzonePlatform { surface_factory_ = std::make_unique<GbmSurfaceFactory>(drm_thread_proxy_.get()); - overlay_manager_ = - std::make_unique<DrmOverlayManagerGpu>(drm_thread_proxy_.get()); + overlay_manager_ = std::make_unique<DrmOverlayManagerGpu>( + drm_thread_proxy_.get(), + args.allow_sync_and_real_buffer_page_flip_testing); // If gpu is in a separate process, rest of the initialization happens after // entering the sandbox. diff --git a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc index 01107a50f94..f6e70bb27fa 100644 --- a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc +++ b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc @@ -89,11 +89,7 @@ class OzonePlatformHeadless : public OzonePlatform { std::unique_ptr<InputMethod> CreateInputMethod( internal::InputMethodDelegate* delegate, gfx::AcceleratedWidget widget) override { -#if defined(OS_FUCHSIA) - return std::make_unique<InputMethodFuchsia>(delegate, widget); -#else return std::make_unique<InputMethodMinimal>(delegate); -#endif } void InitializeUI(const InitParams& params) override { diff --git a/chromium/ui/ozone/platform/scenic/overlay_manager_scenic.cc b/chromium/ui/ozone/platform/scenic/overlay_manager_scenic.cc index e8e4b816710..7ec96d5e05a 100644 --- a/chromium/ui/ozone/platform/scenic/overlay_manager_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/overlay_manager_scenic.cc @@ -32,7 +32,10 @@ class OverlayCandidatesScenic : public OverlayCandidatesOzone { } // namespace -OverlayManagerScenic::OverlayManagerScenic() {} +OverlayManagerScenic::OverlayManagerScenic() { + // Fuchsia overlays rely on ShouldUseRealBuffersForPageFlipTest. + allow_sync_and_real_buffer_page_flip_testing_ = true; +} OverlayManagerScenic::~OverlayManagerScenic() = default; diff --git a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc index 35827691a17..5769845402e 100644 --- a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc @@ -131,7 +131,8 @@ class OzonePlatformScenic : public OzonePlatform, std::unique_ptr<InputMethod> CreateInputMethod( internal::InputMethodDelegate* delegate, gfx::AcceleratedWidget widget) override { - return std::make_unique<InputMethodFuchsia>(delegate, widget); + return std::make_unique<InputMethodFuchsia>( + delegate, window_manager_->GetWindow(widget)->CloneViewRef()); } void InitializeUI(const InitParams& params) override { diff --git a/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc b/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc index a9709908586..f7e30451b70 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_overlay_view.cc @@ -50,6 +50,9 @@ ScenicOverlayView::~ScenicOverlayView() { surface->AssertBelongsToCurrentThread(); surface->RemoveOverlayView(buffer_collection_id_); } + + // Releasing |image_pipe_| implicitly also enforces cleanup. + image_pipe_->RemoveBufferCollection(kImagePipeBufferCollectionId); } void ScenicOverlayView::Initialize( diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc index d1e03bbb990..f9066115081 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc @@ -243,6 +243,7 @@ void ScenicSurfaceFactory::CreateNativePixmapAsync( #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> ScenicSurfaceFactory::CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory) { return std::make_unique<ui::VulkanImplementationScenic>( diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h index b0454ac45cb..20f7dca7608 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h @@ -63,6 +63,7 @@ class ScenicSurfaceFactory : public SurfaceFactoryOzone { NativePixmapCallback callback) override; #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory) override; #endif diff --git a/chromium/ui/ozone/platform/scenic/scenic_window.cc b/chromium/ui/ozone/platform/scenic/scenic_window.cc index 330fe7de65f..b25ec452a2b 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_window.cc @@ -29,10 +29,11 @@ ScenicWindow::ScenicWindow(ScenicWindowManager* window_manager, window_id_(manager_->AddWindow(this)), event_dispatcher_(this), scenic_session_(manager_->GetScenic()), + view_ref_(std::move(properties.view_ref_pair.view_ref)), view_(&scenic_session_, std::move(std::move(properties.view_token)), std::move(properties.view_ref_pair.control_ref), - std::move(properties.view_ref_pair.view_ref), + CloneViewRef(), "chromium window"), node_(&scenic_session_), input_node_(&scenic_session_), @@ -245,6 +246,14 @@ void ScenicWindow::UpdateSize() { delegate_->OnBoundsChanged(bounds_); } +fuchsia::ui::views::ViewRef ScenicWindow::CloneViewRef() { + fuchsia::ui::views::ViewRef dup; + zx_status_t status = + view_ref_.reference.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup.reference); + ZX_CHECK(status == ZX_OK, status) << "zx_object_duplicate"; + return dup; +} + void ScenicWindow::OnScenicError(zx_status_t status) { LOG(ERROR) << "scenic::Session failed with code " << status << "."; delegate_->OnClosed(); diff --git a/chromium/ui/ozone/platform/scenic/scenic_window.h b/chromium/ui/ozone/platform/scenic/scenic_window.h index 2981db6cde8..3bf566cdca0 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window.h +++ b/chromium/ui/ozone/platform/scenic/scenic_window.h @@ -49,6 +49,9 @@ class COMPONENT_EXPORT(OZONE) ScenicWindow // causing its contents to be displayed in this window. void AttachSurfaceView(fuchsia::ui::views::ViewHolderToken token); + // Returns a ViewRef associated with this window. + fuchsia::ui::views::ViewRef CloneViewRef(); + // PlatformWindow implementation. gfx::Rect GetBounds() const override; void SetBounds(const gfx::Rect& bounds) override; @@ -107,6 +110,11 @@ class COMPONENT_EXPORT(OZONE) ScenicWindow // Scenic session used for all drawing operations in this View. scenic::Session scenic_session_; + // Handle to a kernel object which identifies this window's View + // across the system. ViewRef consumers can access the handle by + // calling CloneViewRef(). + fuchsia::ui::views::ViewRef view_ref_; + // The view resource in |scenic_session_|. scenic::View view_; diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc index 9487bcaea42..6a3dee239f9 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc @@ -140,8 +140,9 @@ bool SysmemBufferCollection::Initialize( is_protected_ = force_protected; if (register_with_image_pipe) { - scenic_overlay_view_.emplace(scenic_surface_factory->CreateScenicSession(), - scenic_surface_factory); + overlay_view_task_runner_ = base::ThreadTaskRunnerHandle::Get(); + scenic_overlay_view_ = std::make_unique<ScenicOverlayView>( + scenic_surface_factory->CreateScenicSession(), scenic_surface_factory); surface_factory_ = scenic_surface_factory; } @@ -335,6 +336,12 @@ SysmemBufferCollection::~SysmemBufferCollection() { if (on_deleted_) std::move(on_deleted_).Run(); + + if (scenic_overlay_view_ && + !overlay_view_task_runner_->BelongsToCurrentThread()) { + overlay_view_task_runner_->DeleteSoon(FROM_HERE, + std::move(scenic_overlay_view_)); + } } bool SysmemBufferCollection::InitializeInternal( @@ -350,7 +357,7 @@ bool SysmemBufferCollection::InitializeInternal( // overlay. fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> collection_token_for_scenic; - if (scenic_overlay_view_.has_value()) { + if (scenic_overlay_view_) { collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, collection_token_for_scenic.NewRequest()); } @@ -361,7 +368,7 @@ bool SysmemBufferCollection::InitializeInternal( return false; } - if (scenic_overlay_view_.has_value()) { + if (scenic_overlay_view_) { scenic_overlay_view_->Initialize(std::move(collection_token_for_scenic)); } @@ -450,7 +457,7 @@ bool SysmemBufferCollection::InitializeInternal( is_protected_ = buffers_info_.settings.buffer_settings.is_secure; // Add all images to Image pipe for presentation later. - if (scenic_overlay_view_.has_value()) { + if (scenic_overlay_view_) { scenic_overlay_view_->AddImages(buffers_info_.buffer_count, image_size_); } diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h index 873ddbeccce..6a3ce46bd6b 100644 --- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h +++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h @@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/optional.h" +#include "base/single_thread_task_runner.h" #include "base/threading/thread_checker.h" #include "gpu/ipc/common/vulkan_ycbcr_info.h" #include "gpu/vulkan/fuchsia/vulkan_fuchsia_ext.h" @@ -88,8 +89,7 @@ class SysmemBufferCollection return buffers_info_.settings.buffer_settings.size_bytes; } ScenicOverlayView* scenic_overlay_view() { - return scenic_overlay_view_.has_value() ? &scenic_overlay_view_.value() - : nullptr; + return scenic_overlay_view_ ? scenic_overlay_view_.get() : nullptr; } ScenicSurfaceFactory* surface_factory() { return surface_factory_; } @@ -131,10 +131,13 @@ class SysmemBufferCollection // that is referenced by |collection_|. VkBufferCollectionFUCHSIA vk_buffer_collection_ = VK_NULL_HANDLE; + // |scenic_overlay_view_| view should be used and deleted on the same thread + // as creation. + scoped_refptr<base::SingleThreadTaskRunner> overlay_view_task_runner_; // If ScenicOverlayView is created and its ImagePipe is added as a participant // in buffer allocation negotiations, the associated images can be displayed // as overlays. - base::Optional<ScenicOverlayView> scenic_overlay_view_; + std::unique_ptr<ScenicOverlayView> scenic_overlay_view_; ScenicSurfaceFactory* surface_factory_ = nullptr; // Thread checker used to verify that CreateVkImage() is always called from diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn index 46447f33e84..d1aab09a3a6 100644 --- a/chromium/ui/ozone/platform/wayland/BUILD.gn +++ b/chromium/ui/ozone/platform/wayland/BUILD.gn @@ -18,7 +18,6 @@ source_set("wayland") { "client_native_pixmap_factory_wayland.h", "common/data_util.cc", "common/data_util.h", - "common/wayland.cc", "common/wayland.h", "common/wayland_object.cc", "common/wayland_object.h", @@ -51,8 +50,9 @@ source_set("wayland") { "host/shell_object_factory.h", "host/shell_popup_wrapper.cc", "host/shell_popup_wrapper.h", - "host/shell_surface_wrapper.cc", "host/shell_surface_wrapper.h", + "host/shell_toplevel_wrapper.cc", + "host/shell_toplevel_wrapper.h", "host/wayland_auxiliary_window.cc", "host/wayland_auxiliary_window.h", "host/wayland_buffer_manager_connector.cc", @@ -65,6 +65,8 @@ source_set("wayland") { "host/wayland_connection.h", "host/wayland_cursor.cc", "host/wayland_cursor.h", + "host/wayland_cursor_factory.cc", + "host/wayland_cursor_factory.h", "host/wayland_cursor_position.cc", "host/wayland_cursor_position.h", "host/wayland_data_device.cc", @@ -138,6 +140,8 @@ source_set("wayland") { "host/xdg_popup_wrapper_impl.h", "host/xdg_surface_wrapper_impl.cc", "host/xdg_surface_wrapper_impl.h", + "host/xdg_toplevel_wrapper_impl.cc", + "host/xdg_toplevel_wrapper_impl.h", "host/zwp_primary_selection_device.cc", "host/zwp_primary_selection_device.h", "host/zwp_primary_selection_device_manager.cc", @@ -147,6 +151,12 @@ source_set("wayland") { "host/zwp_text_input_wrapper.h", "host/zwp_text_input_wrapper_v1.cc", "host/zwp_text_input_wrapper_v1.h", + "host/zxdg_popup_v6_wrapper_impl.cc", + "host/zxdg_popup_v6_wrapper_impl.h", + "host/zxdg_surface_v6_wrapper_impl.cc", + "host/zxdg_surface_v6_wrapper_impl.h", + "host/zxdg_toplevel_v6_wrapper_impl.cc", + "host/zxdg_toplevel_v6_wrapper_impl.h", "ozone_platform_wayland.cc", "ozone_platform_wayland.h", ] @@ -182,6 +192,7 @@ source_set("wayland") { "//ui/base:buildflags", "//ui/base/cursor", "//ui/base/cursor:cursor_base", + "//ui/base/cursor:theme_manager", "//ui/base/cursor/mojom:cursor_type", "//ui/base/ime/linux", "//ui/events", @@ -202,7 +213,7 @@ source_set("wayland") { "//ui/platform_window/wm", ] - if (is_linux && !is_chromeos) { + if (is_linux || is_chromeos_lacros) { deps += [ "//ui/base/ime/linux" ] } @@ -217,9 +228,12 @@ source_set("wayland") { ] } - # TODO(crbug.com/1052397): Rename chromeos_is_browser_only to is_lacros. - if (chromeos_is_browser_only) { - deps += [ "//chromeos/crosapi/cpp" ] + if (is_chromeos_lacros) { + deps += [ + "//chromeos/crosapi/cpp", + "//chromeos/crosapi/mojom", + "//chromeos/lacros", + ] } defines = [ "OZONE_IMPLEMENTATION" ] @@ -230,6 +244,7 @@ source_set("wayland") { } else { deps += [ "//third_party/wayland:wayland_client", + "//third_party/wayland:wayland_cursor", "//third_party/wayland:wayland_egl", ] } @@ -357,9 +372,11 @@ source_set("wayland_unittests") { testonly = true sources = [ + "common/wayland_util_unittest.cc", "gpu/wayland_overlay_manager_unittest.cc", + "host/wayland_clipboard_unittest.cc", "host/wayland_connection_unittest.cc", - "host/wayland_data_device_unittest.cc", + "host/wayland_cursor_factory_unittest.cc", "host/wayland_data_drag_controller_unittest.cc", "host/wayland_event_source_unittest.cc", "host/wayland_input_method_context_unittest.cc", @@ -370,6 +387,9 @@ source_set("wayland_unittests") { "host/wayland_window_drag_controller_unittest.cc", "host/wayland_window_manager_unittests.cc", "host/wayland_window_unittest.cc", + "host/wayland_zaura_shell_unittest.cc", + "test/wayland_drag_drop_test.cc", + "test/wayland_drag_drop_test.h", "test/wayland_test.cc", "test/wayland_test.h", ] @@ -377,6 +397,7 @@ source_set("wayland_unittests") { deps = [ ":test_support", ":wayland", + "//components/exo/wayland/protocol:aura_shell_protocol", "//testing/gmock", "//testing/gtest", "//third_party/wayland:wayland_server", diff --git a/chromium/ui/ozone/platform/wayland/DEPS b/chromium/ui/ozone/platform/wayland/DEPS index 1eeae79d79e..b7393cb1394 100644 --- a/chromium/ui/ozone/platform/wayland/DEPS +++ b/chromium/ui/ozone/platform/wayland/DEPS @@ -7,7 +7,7 @@ include_rules = [ "+third_party/wayland", "+ui/base/clipboard/clipboard_constants.h", "+ui/base/dragdrop/drag_drop_types.h", - "+ui/base/dragdrop/file_info/file_info.h", + "+ui/base/clipboard/file_info.h", "+ui/base/dragdrop/os_exchange_data.h", "+ui/base/dragdrop/os_exchange_data_provider_non_backed.h", ] diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.cc b/chromium/ui/ozone/platform/wayland/common/data_util.cc index 90773668cde..94c84236a2c 100644 --- a/chromium/ui/ozone/platform/wayland/common/data_util.cc +++ b/chromium/ui/ozone/platform/wayland/common/data_util.cc @@ -12,7 +12,7 @@ #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/clipboard/clipboard_constants.h" -#include "ui/base/dragdrop/file_info/file_info.h" +#include "ui/base/clipboard/file_info.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" #include "ui/ozone/public/platform_clipboard.h" diff --git a/chromium/ui/ozone/platform/wayland/common/wayland.cc b/chromium/ui/ozone/platform/wayland/common/wayland.cc deleted file mode 100644 index 8a20e3a7be3..00000000000 --- a/chromium/ui/ozone/platform/wayland/common/wayland.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/ozone/platform/wayland/common/wayland.h" - -namespace wl { - -void* bind_registry(struct wl_registry* registry, - uint32_t name, - const struct wl_interface* interface, - uint32_t version) { - if (dlsym(RTLD_DEFAULT, "wl_proxy_marshal_constructor_versioned")) { - return wl_registry_bind(registry, name, interface, version); - } else { - return wl_proxy_marshal_constructor(reinterpret_cast<wl_proxy*>(registry), - WL_REGISTRY_BIND, interface, name, - interface->name, version, nullptr); - } - NOTREACHED(); - return nullptr; -} - -} // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland.h b/chromium/ui/ozone/platform/wayland/common/wayland.h index c645346ddcb..fd8ff58bf7d 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland.h +++ b/chromium/ui/ozone/platform/wayland/common/wayland.h @@ -5,26 +5,22 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_H_ #define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_H_ -#include <dlfcn.h> -// This header includes wayland-client-core.h and wayland-client-protocol.h #include <wayland-client.h> -#include "base/notreached.h" - namespace wl { template <typename T> uint32_t get_version_of_object(T* obj) { - if (dlsym(RTLD_DEFAULT, "wl_proxy_get_version")) - return wl_proxy_get_version(reinterpret_cast<wl_proxy*>(obj)); - // Older version of the libwayland-client didn't support version of objects. - return 0; + return wl_proxy_get_version(reinterpret_cast<wl_proxy*>(obj)); } -void* bind_registry(struct wl_registry* registry, - uint32_t name, - const struct wl_interface* interface, - uint32_t version); +template <typename T> +T* bind_registry(struct wl_registry* registry, + uint32_t name, + const struct wl_interface* interface, + uint32_t version) { + return static_cast<T*>(wl_registry_bind(registry, name, interface, version)); +} } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc index 7a6c5e7b849..8827a5e9f49 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc +++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc @@ -15,6 +15,7 @@ #include <primary-selection-unstable-v1-client-protocol.h> #include <text-input-unstable-v1-client-protocol.h> #include <viewporter-client-protocol.h> +#include <wayland-cursor.h> #include <wayland-drm-client-protocol.h> #include <xdg-decoration-unstable-v1-client-protocol.h> #include <xdg-foreign-unstable-v1-client-protocol.h> @@ -131,6 +132,9 @@ const wl_interface* ObjectTraits<wl_compositor>::interface = void (*ObjectTraits<wl_compositor>::deleter)(wl_compositor*) = &wl_compositor_destroy; +void (*ObjectTraits<wl_cursor_theme>::deleter)(wl_cursor_theme*) = + &wl_cursor_theme_destroy; + const wl_interface* ObjectTraits<wl_data_device_manager>::interface = &wl_data_device_manager_interface; void (*ObjectTraits<wl_data_device_manager>::deleter)(wl_data_device_manager*) = diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.h b/chromium/ui/ozone/platform/wayland/common/wayland_object.h index b06cf876f91..d609c6150fa 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_object.h +++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.h @@ -20,6 +20,7 @@ struct zwp_primary_selection_source_v1; struct wl_buffer; struct wl_callback; struct wl_compositor; +struct wl_cursor_theme; struct wl_data_device_manager; struct wl_data_device; struct wl_data_offer; @@ -154,6 +155,11 @@ struct ObjectTraits<wl_compositor> { }; template <> +struct ObjectTraits<wl_cursor_theme> { + static void (*deleter)(wl_cursor_theme*); +}; + +template <> struct ObjectTraits<wl_data_device_manager> { static const wl_interface* interface; static void (*deleter)(wl_data_device_manager*); @@ -462,8 +468,8 @@ class Object : public std::unique_ptr<T, Deleter> { template <typename T> wl::Object<T> Bind(wl_registry* registry, uint32_t name, uint32_t version) { - return wl::Object<T>(static_cast<T*>( - wl::bind_registry(registry, name, ObjectTraits<T>::interface, version))); + return wl::Object<T>(wl::bind_registry<T>( + registry, name, ObjectTraits<T>::interface, version)); } } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util.cc b/chromium/ui/ozone/platform/wayland/common/wayland_util.cc index 042cfc8e96b..6d7b05906af 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_util.cc +++ b/chromium/ui/ozone/platform/wayland/common/wayland_util.cc @@ -7,7 +7,11 @@ #include <xdg-shell-client-protocol.h> #include <xdg-shell-unstable-v6-client-protocol.h> +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRegion.h" #include "ui/base/hit_test.h" +#include "ui/gfx/skia_util.h" +#include "ui/gfx/transform.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h" #include "ui/ozone/platform/wayland/host/wayland_surface.h" @@ -131,7 +135,7 @@ bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer) { // Clear to transparent in case |bitmap| is smaller than the canvas. auto* canvas = sk_surface->getCanvas(); canvas->clear(SK_ColorTRANSPARENT); - canvas->drawBitmapRect(bitmap, damage, nullptr); + canvas->drawImageRect(bitmap.asImage(), damage, SkSamplingOptions()); return true; } @@ -276,4 +280,26 @@ gfx::Rect TranslateWindowBoundsToParentDIP(ui::WaylandWindow* window, 1.0 / window->buffer_scale()); } +std::vector<gfx::Rect> CreateRectsFromSkPath(const SkPath& path) { + SkRegion clip_region; + clip_region.setRect(path.getBounds().round()); + SkRegion region; + region.setPath(path, clip_region); + + std::vector<gfx::Rect> rects; + for (SkRegion::Iterator it(region); !it.done(); it.next()) + rects.push_back(gfx::SkIRectToRect(it.rect())); + + return rects; +} + +SkPath ConvertPathToDIP(const SkPath& path_in_pixels, const int32_t scale) { + SkScalar sk_scale = SkFloatToScalar(1.0f / scale); + gfx::Transform transform; + transform.Scale(sk_scale, sk_scale); + SkPath path_in_dips; + path_in_pixels.transform(SkMatrix(transform.matrix()), &path_in_dips); + return path_in_dips; +} + } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util.h b/chromium/ui/ozone/platform/wayland/common/wayland_util.h index d755de9e0ba..8d8b12f9cc1 100644 --- a/chromium/ui/ozone/platform/wayland/common/wayland_util.h +++ b/chromium/ui/ozone/platform/wayland/common/wayland_util.h @@ -17,6 +17,7 @@ #include "ui/platform_window/platform_window_init_properties.h" class SkBitmap; +class SkPath; namespace ui { class WaylandConnection; @@ -87,6 +88,12 @@ ui::WaylandWindow* RootWindowFromWlSurface(wl_surface* surface); gfx::Rect TranslateWindowBoundsToParentDIP(ui::WaylandWindow* window, ui::WaylandWindow* parent_window); +// Returns rectangles dictated by SkPath. +std::vector<gfx::Rect> CreateRectsFromSkPath(const SkPath& path); + +// Returns converted SkPath in DIPs from the one in pixels. +SkPath ConvertPathToDIP(const SkPath& path_in_pixels, const int32_t scale); + } // namespace wl #endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_ diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util_unittest.cc b/chromium/ui/ozone/platform/wayland/common/wayland_util_unittest.cc new file mode 100644 index 00000000000..57a3669cb30 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/common/wayland_util_unittest.cc @@ -0,0 +1,71 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/common/wayland_util.h" + +#include <memory> + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRect.h" + +namespace ui { + +class WaylandUtilTest : public testing::Test {}; + +TEST_F(WaylandUtilTest, TestCreateRectsFromSkPath) { + constexpr int width = 100; + constexpr int height = 100; + + // Test1 consists of 2 rectangles from SkPath. + std::vector<gfx::Rect> expectedFor2Rects = {gfx::Rect(1, 0, 98, 1), + gfx::Rect(0, 1, 100, 99)}; + SkPath pathFor2Rects; + const SkRect rect = SkRect::MakeIWH(width, height); + constexpr SkScalar corner_radius_scalar = 2.0; + constexpr SkScalar radii[8] = {corner_radius_scalar, + corner_radius_scalar, // top-left + corner_radius_scalar, + corner_radius_scalar, // top-right + 0, + 0, // bottom-right + 0, + 0}; // bottom-left + pathFor2Rects.addRoundRect(rect, radii, SkPathDirection::kCW); + EXPECT_EQ(expectedFor2Rects, wl::CreateRectsFromSkPath(pathFor2Rects)); + + // Test2 consists of 5 rectangles from SkPath. + std::vector<gfx::Rect> expectedFor5Rects = { + gfx::Rect(3, 0, 94, 1), gfx::Rect(1, 1, 98, 2), gfx::Rect(0, 3, 100, 94), + gfx::Rect(1, 97, 98, 2), gfx::Rect(3, 99, 94, 1)}; + SkPath pathFor5Rects; + pathFor5Rects.moveTo(0, 3); + pathFor5Rects.lineTo(1, 3); + pathFor5Rects.lineTo(1, 1); + pathFor5Rects.lineTo(3, 1); + pathFor5Rects.lineTo(3, 0); + + pathFor5Rects.lineTo(width - 3, 0); + pathFor5Rects.lineTo(width - 3, 1); + pathFor5Rects.lineTo(width - 1, 1); + pathFor5Rects.lineTo(width - 1, 3); + pathFor5Rects.lineTo(width, 3); + + pathFor5Rects.lineTo(width, height - 3); + pathFor5Rects.lineTo(width - 1, height - 3); + pathFor5Rects.lineTo(width - 1, height - 1); + pathFor5Rects.lineTo(width - 3, height - 1); + pathFor5Rects.lineTo(width - 3, height); + + pathFor5Rects.lineTo(3, height); + pathFor5Rects.lineTo(3, height - 1); + pathFor5Rects.lineTo(1, height - 1); + pathFor5Rects.lineTo(1, height - 3); + pathFor5Rects.lineTo(0, height - 3); + pathFor5Rects.close(); + + EXPECT_EQ(expectedFor5Rects, wl::CreateRectsFromSkPath(pathFor5Rects)); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index 1b15b1c27f7..827cd1c9b7d 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc @@ -11,6 +11,8 @@ #include "base/task/thread_pool.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/gpu_fence.h" +#include "ui/gfx/gpu_fence_handle.h" +#include "ui/gl/gl_bindings.h" #include "ui/ozone/common/egl_util.h" #include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h" #include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h" @@ -19,12 +21,6 @@ namespace ui { namespace { -void WaitForEGLFence(EGLDisplay display, EGLSyncKHR fence) { - eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, - EGL_FOREVER_KHR); - eglDestroySyncKHR(display, fence); -} - void WaitForGpuFences(std::vector<std::unique_ptr<gfx::GpuFence>> fences) { for (auto& fence : fences) fence->Wait(); @@ -60,9 +56,9 @@ bool GbmSurfacelessWayland::ScheduleOverlayPlane( const gfx::RectF& crop_rect, bool enable_blend, std::unique_ptr<gfx::GpuFence> gpu_fence) { - unsubmitted_frames_.back()->overlays.push_back( - gl::GLSurfaceOverlay(z_order, transform, image, bounds_rect, crop_rect, - enable_blend, std::move(gpu_fence))); + unsubmitted_frames_.back()->overlays.emplace_back( + z_order, transform, image, bounds_rect, crop_rect, enable_blend, + std::move(gpu_fence)); return true; } @@ -115,31 +111,23 @@ void GbmSurfacelessWayland::SwapBuffersAsync( unsubmitted_frames_.push_back(std::make_unique<PendingFrame>()); - if (!use_egl_fence_sync_ || !frame->schedule_planes_succeeded) { + // If Wayland server supports linux_explicit_synchronization_protocol, fences + // should be shipped with buffers. Otherwise, we will wait for fences. + if (buffer_manager_->supports_acquire_fence() || !use_egl_fence_sync_ || + !frame->schedule_planes_succeeded) { frame->ready = true; MaybeSubmitFrames(); return; } + base::OnceClosure fence_wait_task; std::vector<std::unique_ptr<gfx::GpuFence>> fences; - // Uset in-fences provided in the overlays. If there are none, we insert our - // own fence and wait. for (auto& plane : frame->planes) { if (plane.second.gpu_fence) fences.push_back(std::move(plane.second.gpu_fence)); } - base::OnceClosure fence_wait_task; - if (!fences.empty()) { - fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences)); - } else { - // TODO(fangzhoug): the following should be replaced by a per surface flush - // as it gets implemented in GL drivers. - EGLSyncKHR fence = InsertFence(has_implicit_external_sync_); - CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed"; - - fence_wait_task = base::BindOnce(&WaitForEGLFence, GetDisplay(), fence); - } + fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences)); base::OnceClosure fence_retired_callback = base::BindOnce( &GbmSurfacelessWayland::FenceRetired, weak_factory_.GetWeakPtr(), frame); @@ -190,6 +178,14 @@ void GbmSurfacelessWayland::SetRelyOnImplicitSync() { use_egl_fence_sync_ = false; } +bool GbmSurfacelessWayland::SupportsPlaneGpuFences() const { + return true; +} + +bool GbmSurfacelessWayland::SupportsOverridePlatformSize() const { + return true; +} + gfx::SurfaceOrigin GbmSurfacelessWayland::GetOrigin() const { // GbmSurfacelessWayland's y-axis is flipped compare to GL - (0,0) is at top // left corner. @@ -200,9 +196,9 @@ GbmSurfacelessWayland::~GbmSurfacelessWayland() { buffer_manager_->UnregisterSurface(widget_); } -GbmSurfacelessWayland::PendingFrame::PendingFrame() {} +GbmSurfacelessWayland::PendingFrame::PendingFrame() = default; -GbmSurfacelessWayland::PendingFrame::~PendingFrame() {} +GbmSurfacelessWayland::PendingFrame::~PendingFrame() = default; void GbmSurfacelessWayland::PendingFrame::ScheduleOverlayPlanes( gfx::AcceleratedWidget widget) { @@ -238,15 +234,19 @@ void GbmSurfacelessWayland::MaybeSubmitFrames() { } std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs; - for (const auto& plane : submitted_frame->planes) { + for (auto& plane : submitted_frame->planes) { overlay_configs.push_back( ui::ozone::mojom::WaylandOverlayConfig::From(plane.second)); overlay_configs.back()->buffer_id = plane.first; - if (plane.second.z_order == 0) { + if (plane.second.z_order == 0) overlay_configs.back()->damage_region = submitted_frame->damage_region_; - submitted_frame->buffer_id = plane.first; - } +#if DCHECK_IS_ON() + if (plane.second.z_order == INT32_MIN) + background_buffer_id_ = plane.first; +#endif + plane.second.gpu_fence.reset(); } + buffer_manager_->CommitOverlays(widget_, std::move(overlay_configs)); submitted_frames_.push_back(std::move(submitted_frame)); } @@ -273,7 +273,7 @@ void GbmSurfacelessWayland::OnSubmission(BufferId buffer_id, const gfx::SwapResult& swap_result) { // submitted_frames_ may temporarily have more than one buffer in it if // buffers are released out of order by the Wayland server. - DCHECK(!submitted_frames_.empty()); + DCHECK(!submitted_frames_.empty() || background_buffer_id_ == buffer_id); size_t erased = 0; for (auto& submitted_frame : submitted_frames_) { @@ -320,7 +320,8 @@ void GbmSurfacelessWayland::OnSubmission(BufferId buffer_id, void GbmSurfacelessWayland::OnPresentation( BufferId buffer_id, const gfx::PresentationFeedback& feedback) { - DCHECK(!submitted_frames_.empty() || !pending_presentation_frames_.empty()); + DCHECK(!submitted_frames_.empty() || !pending_presentation_frames_.empty() || + background_buffer_id_ == buffer_id); size_t erased = 0; for (auto& frame : pending_presentation_frames_) { diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index 910b9a7056e..8be5c32391f 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h @@ -60,6 +60,8 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, PresentationCallback presentation_callback) override; EGLConfig GetConfig() override; void SetRelyOnImplicitSync() override; + bool SupportsPlaneGpuFences() const override; + bool SupportsOverridePlatformSize() const override; gfx::SurfaceOrigin GetOrigin() const override; private: @@ -88,9 +90,6 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, bool ready = false; - // The id of the buffer, which represents this frame. - BufferId buffer_id = 0; - // A region of the updated content in a corresponding frame. It's used to // advice Wayland which part of a buffer is going to be updated. Passing {0, // 0, 0, 0} results in a whole buffer update on the Wayland compositor side. @@ -120,6 +119,12 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL, WaylandBufferManagerGpu* const buffer_manager_; + // |background_buffer_id| is sent to WaylandBufferManagerHost once per + // background_buffer allocation. However WaylandBufferManagerHost may commit + // this buffer more often b/c buffers needs to be re-attached when wl_surface + // is reshown. + BufferId background_buffer_id_; + // The native surface. Deleting this is allowed to free the EGLNativeWindow. gfx::AcceleratedWidget widget_; std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_; diff --git a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc index 195d061926c..5383fb8209c 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc @@ -116,8 +116,9 @@ void GLSurfaceEglReadbackWayland::SwapBuffersAsync( CHECK(next_buffer->shm_mapping_.memory()); ReadPixels(next_buffer->shm_mapping_.memory()); - buffer_manager_->CommitBuffer(widget_, next_buffer->buffer_id_, - {{0, 0}, GetSize()}); + const auto bounds = gfx::Rect(GetSize()); + buffer_manager_->CommitBuffer(widget_, next_buffer->buffer_id_, bounds, + bounds); } gfx::SurfaceOrigin GLSurfaceEglReadbackWayland::GetOrigin() const { diff --git a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc index e5cdaf12895..b430a061c26 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc @@ -46,21 +46,21 @@ bool GLSurfaceWayland::Resize(const gfx::Size& size, EGLConfig GLSurfaceWayland::GetConfig() { if (!config_) { - GLint config_attribs[] = {EGL_BUFFER_SIZE, - 32, - EGL_ALPHA_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_RED_SIZE, - 8, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_NONE}; + EGLint config_attribs[] = {EGL_BUFFER_SIZE, + 32, + EGL_ALPHA_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_NONE}; config_ = ChooseEGLConfig(GetDisplay(), config_attribs); } return config_; diff --git a/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.cc index 3a2be4275ef..1a3335da4e2 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.cc @@ -34,8 +34,8 @@ bool InitializeVulkanFunctionPointers( } // namespace -VulkanImplementationWayland::VulkanImplementationWayland() - : gpu::VulkanImplementation(false /* use_swiftshader */) {} +VulkanImplementationWayland::VulkanImplementationWayland(bool use_swiftshader) + : gpu::VulkanImplementation(use_swiftshader) {} VulkanImplementationWayland::~VulkanImplementationWayland() {} @@ -47,14 +47,20 @@ bool VulkanImplementationWayland::InitializeVulkanInstance(bool using_surface) { auto* vulkan_function_pointers = gpu::GetVulkanFunctionPointers(); - base::FilePath path("libvulkan.so.1"); + base::FilePath path; + if (use_swiftshader()) { + if (!base::PathService::Get(base::DIR_MODULE, &path)) + return false; + + path = path.Append("libvk_swiftshader.so"); + } else { + path = base::FilePath("libvulkan.so.1"); + } if (!InitializeVulkanFunctionPointers(path, vulkan_function_pointers)) return false; - if (!vulkan_instance_.Initialize(required_extensions, {})) - return false; - return true; + return vulkan_instance_.Initialize(required_extensions, {}); } gpu::VulkanInstance* VulkanImplementationWayland::GetVulkanInstance() { @@ -77,13 +83,16 @@ bool VulkanImplementationWayland::GetPhysicalDevicePresentationSupport( std::vector<const char*> VulkanImplementationWayland::GetRequiredDeviceExtensions() { - return { - VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, + std::vector<const char*> result{ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, }; + + if (!use_swiftshader()) + result.push_back(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME); + return result; } std::vector<const char*> diff --git a/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.h index e504ac313ef..5acd1e07945 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.h +++ b/chromium/ui/ozone/platform/wayland/gpu/vulkan_implementation_wayland.h @@ -14,7 +14,7 @@ namespace ui { class VulkanImplementationWayland : public gpu::VulkanImplementation { public: - VulkanImplementationWayland(); + explicit VulkanImplementationWayland(bool use_swiftshader = false); ~VulkanImplementationWayland() override; VulkanImplementationWayland(const VulkanImplementationWayland&) = delete; @@ -56,4 +56,4 @@ class VulkanImplementationWayland : public gpu::VulkanImplementation { } // namespace ui -#endif // UI_OZONE_PLATFORM_WAYLAND_GPU_VULKAN_IMPLEMENTATION_WAYLAND_H_
\ No newline at end of file +#endif // UI_OZONE_PLATFORM_WAYLAND_GPU_VULKAN_IMPLEMENTATION_WAYLAND_H_ diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc index 5c14a1c918f..da7ce23565e 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc @@ -44,7 +44,8 @@ void WaylandBufferManagerGpu::Initialize( mojo::PendingRemote<ozone::mojom::WaylandBufferManagerHost> remote_host, const base::flat_map<::gfx::BufferFormat, std::vector<uint64_t>>& buffer_formats_with_modifiers, - bool supports_dma_buf) { + bool supports_dma_buf, + bool supports_acquire_fence) { DCHECK(supported_buffer_formats_with_modifiers_.empty()); supported_buffer_formats_with_modifiers_ = buffer_formats_with_modifiers; @@ -52,6 +53,7 @@ void WaylandBufferManagerGpu::Initialize( if (!supports_dma_buf) set_gbm_device(nullptr); #endif + supports_acquire_fence_ = supports_acquire_fence; BindHostInterface(std::move(remote_host)); @@ -182,18 +184,16 @@ void WaylandBufferManagerGpu::CreateShmBasedBuffer( void WaylandBufferManagerGpu::CommitBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id, + const gfx::Rect& bounds_rect, const gfx::Rect& damage_region) { - if (!remote_host_) { - LOG(ERROR) << "Interface is not bound. Can't request " - "WaylandBufferManagerHost to create/commit/destroy buffers."; - return; - } - - // Do the mojo call on the IO child thread. - io_thread_runner_->PostTask( - FROM_HERE, - base::BindOnce(&WaylandBufferManagerGpu::CommitBufferInternal, - base::Unretained(this), widget, buffer_id, damage_region)); + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs; + // This surface only commits one buffer per frame, use INT32_MIN to attach + // the buffer to root_surface of wayland window. + overlay_configs.push_back(ui::ozone::mojom::WaylandOverlayConfig::New( + INT32_MIN, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, buffer_id, + bounds_rect, gfx::RectF(), damage_region, false, gfx::GpuFenceHandle())); + + CommitOverlays(widget, std::move(overlay_configs)); } void WaylandBufferManagerGpu::CommitOverlays( @@ -271,14 +271,6 @@ void WaylandBufferManagerGpu::CreateShmBasedBufferInternal( length, size, buffer_id); } -void WaylandBufferManagerGpu::CommitBufferInternal( - gfx::AcceleratedWidget widget, - uint32_t buffer_id, - const gfx::Rect& damage_region) { - DCHECK(io_thread_runner_->BelongsToCurrentThread()); - remote_host_->CommitBuffer(widget, buffer_id, damage_region); -} - void WaylandBufferManagerGpu::CommitOverlaysInternal( gfx::AcceleratedWidget widget, std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays) { diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h index 1f451202652..8123ade0306 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h @@ -26,7 +26,6 @@ namespace gfx { enum class SwapResult; -class Rect; } // namespace gfx namespace ui { @@ -51,7 +50,8 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu { mojo::PendingRemote<ozone::mojom::WaylandBufferManagerHost> remote_host, const base::flat_map<::gfx::BufferFormat, std::vector<uint64_t>>& buffer_formats_with_modifiers, - bool supports_dma_buf) override; + bool supports_dma_buf, + bool supports_acquire_fence) override; // These two calls get the surface, which backs the |widget| and notifies it // about the submission and the presentation. After the surface receives the @@ -103,10 +103,13 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu { // logic as well. This call must not be done twice for the same |widget| until // the OnSubmission is called (which actually means the client can continue // sending buffer swap requests). + // + // CommitBuffer() calls CommitOverlays() to commit only a primary plane + // buffer. void CommitBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id, + const gfx::Rect& bounds_rect, const gfx::Rect& damage_region); - // Send overlay configurations for a frame to a WaylandWindow identified by // |widget|. void CommitOverlays( @@ -124,6 +127,8 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu { } #endif + bool supports_acquire_fence() const { return supports_acquire_fence_; } + // Adds a WaylandBufferManagerGpu binding. void AddBindingWaylandBufferManagerGpu( mojo::PendingReceiver<ozone::mojom::WaylandBufferManagerGpu> receiver); @@ -148,9 +153,6 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu { size_t length, gfx::Size size, uint32_t buffer_id); - void CommitBufferInternal(gfx::AcceleratedWidget widget, - uint32_t buffer_id, - const gfx::Rect& damage_region); void CommitOverlaysInternal( gfx::AcceleratedWidget widget, std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays); @@ -178,6 +180,8 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu { // A DRM render node based gbm device. std::unique_ptr<GbmDevice> gbm_device_; #endif + // Whether Wayland server allows buffer submission with acquire fence. + bool supports_acquire_fence_ = false; mojo::Receiver<ozone::mojom::WaylandBufferManagerGpu> receiver_{this}; diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc index deb43b013e4..bb7029b7d29 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc @@ -58,6 +58,7 @@ class WaylandCanvasSurface::SharedMemoryBuffer { // based wl_buffer, which can be attached to a surface and have its contents // shown on a screen. bool Initialize(const gfx::Size& size) { + size_ = size; base::CheckedNumeric<size_t> checked_length(size.width()); checked_length *= size.height(); checked_length *= 4; @@ -92,7 +93,8 @@ class WaylandCanvasSurface::SharedMemoryBuffer { } void CommitBuffer(const gfx::Rect& damage) { - buffer_manager_->CommitBuffer(widget_, buffer_id_, damage); + buffer_manager_->CommitBuffer(widget_, buffer_id_, gfx::Rect(size_), + damage); } void OnUse() { @@ -134,6 +136,9 @@ class WaylandCanvasSurface::SharedMemoryBuffer { } private: + // The size of the buffer. + gfx::Size size_; + // The id of the buffer this surface is backed. const uint32_t buffer_id_; diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc index 0f20a7c0c39..18f555b2076 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc @@ -164,9 +164,10 @@ GLOzone* WaylandSurfaceFactory::GetGLOzone( #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> WaylandSurfaceFactory::CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory) { - return std::make_unique<VulkanImplementationWayland>(); + return std::make_unique<VulkanImplementationWayland>(use_swiftshader); } #endif diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h index 1de11ed0104..18a0319c1d9 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h @@ -31,6 +31,7 @@ class WaylandSurfaceFactory : public SurfaceFactoryOzone { GLOzone* GetGLOzone(gl::GLImplementation implementation) override; #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory) override; #endif diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc index cc71dc65c2b..310d4a9be16 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc @@ -178,7 +178,7 @@ class WaylandSurfaceFactoryTest : public WaylandTest { WaylandTest::SetUp(); auto manager_ptr = connection_->buffer_manager_host()->BindInterface(); - buffer_manager_gpu_->Initialize(std::move(manager_ptr), {}, false); + buffer_manager_gpu_->Initialize(std::move(manager_ptr), {}, false, false); // Wait until initialization and mojo calls go through. base::RunLoop().RunUntilIdle(); @@ -204,221 +204,10 @@ class WaylandSurfaceFactoryTest : public WaylandTest { }; TEST_P(WaylandSurfaceFactoryTest, - GbmSurfacelessWaylandCheckOrderOfCallbacksTest) { - gl::SetGLImplementation(gl::kGLImplementationEGLGLES2); - - buffer_manager_gpu_->set_gbm_device(std::make_unique<MockGbmDevice>()); - - auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2); - auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_); - EXPECT_TRUE(gl_surface); - gl_surface->SetRelyOnImplicitSync(); - static_cast<ui::GbmSurfacelessWayland*>(gl_surface.get()) - ->SetNoGLFlushForTests(); - - // Expect to create 3 buffers. - EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3); - - // Create buffers and FakeGlImageNativePixmap. - std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image; - for (int i = 0; i < 3; ++i) { - auto native_pixmap = surface_factory_->CreateNativePixmap( - widget_, nullptr, window_->GetBounds().size(), - gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT); - fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>( - native_pixmap, window_->GetBounds().size())); - - Sync(); - - // Create one buffer at a time. - auto params_vector = server_.zwp_linux_dmabuf_v1()->buffer_params(); - DCHECK_EQ(params_vector.size(), 1u); - zwp_linux_buffer_params_v1_send_created( - params_vector.front()->resource(), - params_vector.front()->buffer_resource()); - - Sync(); - } - - // Now, schedule 3 buffers for swap. - auto* mock_surface = server_.GetObject<wl::MockSurface>( - window_->root_surface()->GetSurfaceId()); - - CallbacksHelper cbs_helper; - // Submit all the available buffers. - for (const auto& gl_image : fake_gl_image) { - // Associate each image with swap id so that we could track released - // buffers. - auto swap_id = cbs_helper.GetNextLocalSwapId(); - // Associate the image with the next swap id so that we can easily track if - // it became free to reuse. - gl_image->AssociateWithSwapId(swap_id); - // And set it to be busy... - gl_image->SetBusy(true); - - // Prepare overlay plane. - gl_surface->ScheduleOverlayPlane( - INT32_MIN, gfx::OverlayTransform::OVERLAY_TRANSFORM_FLIP_VERTICAL, - gl_image.get(), window_->GetBounds(), {}, false, nullptr); - - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(gl_image); - - // And submit each image. They will be executed in FIFO manner. - gl_surface->SwapBuffersAsync( - base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), - base::BindOnce(&CallbacksHelper::BufferPresented, - base::Unretained(&cbs_helper), swap_id)); - } - - // Let's sync so that 1) GbmSurfacelessWayland submits the buffer according to - // internal queue and fake server processes the request. - - // Also, we expect only one buffer to be committed. - EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_surface, Frame(_)).Times(1); - EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1); - EXPECT_CALL(*mock_surface, Commit()).Times(1); - - Sync(); - - testing::Mock::VerifyAndClearExpectations(&mock_surface); - - // Give mojo the chance to pass the callbacks. - base::RunLoop().RunUntilIdle(); - - // We have just received Attach/DamageBuffer/Commit for buffer with swap - // id=0u. The SwapCompletionCallback must be executed automatically as long as - // we didn't have any buffers attached to the surface before. - EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 0u); - - cbs_helper.ResetLastFinishedSwapId(); - - for (const auto& gl_image : fake_gl_image) { - // All the images except the first one, which was associated with swap - // id=0u, must be busy and not displayed. The first one must be displayed. - if (gl_image->GetAssociateWithSwapId() == 0u) { - EXPECT_FALSE(gl_image->busy()); - EXPECT_TRUE(gl_image->displayed()); - } else { - EXPECT_TRUE(gl_image->busy()); - EXPECT_FALSE(gl_image->displayed()); - } - } - - // Expect buffer for swap with id=1u to be committed. - EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_surface, Frame(_)).Times(1); - EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1); - EXPECT_CALL(*mock_surface, Commit()).Times(1); - - // Send the frame callback so that pending buffer for swap id=1u is processed - // and swapped. - mock_surface->SendFrameCallback(); - - Sync(); - - // Give mojo the chance to pass the callbacks. - base::RunLoop().RunUntilIdle(); - - // Even though the second buffer was submitted, we mustn't receive - // SwapCompletionCallback until the previous buffer is released. - EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), - std::numeric_limits<uint32_t>::max()); - - // This will result in Wayland server releasing previously attached buffer for - // swap id=0u and calling OnSubmission for buffer with swap id=1u. - mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer()); - - Sync(); - - // Give mojo the chance to pass the callbacks. - base::RunLoop().RunUntilIdle(); - - // We expect only one buffer to be released. Thus, the last swap id must be - // 0 as we waited until next buffer was attached to the surface. - EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 1u); - - // Reset to test further swap ids. - cbs_helper.ResetLastFinishedSwapId(); - - for (const auto& gl_image : fake_gl_image) { - // The first image is not displayed and not busy, the second is displayed - // and not busy. And others are not display and busy. - if (gl_image->GetAssociateWithSwapId() == 0u) { - EXPECT_FALSE(gl_image->busy()); - EXPECT_FALSE(gl_image->displayed()); - } else if (gl_image->GetAssociateWithSwapId() == 1u) { - EXPECT_FALSE(gl_image->busy()); - EXPECT_TRUE(gl_image->displayed()); - } else { - EXPECT_TRUE(gl_image->busy()); - EXPECT_FALSE(gl_image->displayed()); - } - } - - EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_surface, Frame(_)).Times(1); - EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(1); - EXPECT_CALL(*mock_surface, Commit()).Times(1); - - // Send the frame callback, so that the pending buffer with swap id=2u can - // be processed. - mock_surface->SendFrameCallback(); - - Sync(); - - // Give mojo the chance to pass the callbacks. - base::RunLoop().RunUntilIdle(); - - // Even though the second buffer was submitted, we mustn't receive - // SwapCompletionCallback until the previous buffer is released. - EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), - std::numeric_limits<uint32_t>::max()); - - // This will result in Wayland server releasing previously attached buffer for - // swap id=1u and calling OnSubmission for buffer with swap id=2u. - mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer()); - - Sync(); - - // Give mojo the chance to pass the callbacks. - base::RunLoop().RunUntilIdle(); - - // We should receive next callbacks for the next swap id. - EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 2u); - - cbs_helper.ResetLastFinishedSwapId(); - - // All images must be free now and the last one is displayed. - for (const auto& gl_image : fake_gl_image) { - if (gl_image->GetAssociateWithSwapId() == 2u) { - EXPECT_TRUE(gl_image->displayed()); - EXPECT_FALSE(gl_image->busy()); - } else { - EXPECT_FALSE(gl_image->displayed()); - EXPECT_FALSE(gl_image->busy()); - } - } - - // There are no buffers left. Send last frame callback and verify that. - EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0); - EXPECT_CALL(*mock_surface, Frame(_)).Times(0); - EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(0); - EXPECT_CALL(*mock_surface, Commit()).Times(0); - - // Send a frame callback so that the WaylandBufferManagerHost processes the - // pending buffers if any exist. - mock_surface->SendFrameCallback(); -} - -TEST_P(WaylandSurfaceFactoryTest, GbmSurfacelessWaylandCommitOverlaysCallbacksTest) { - // GbmSurfacelessWaylandCheckOrderOfCallbacksTest tests with one buffer per - // frame. This tests multiple buffers per-frame and order of - // SwapCompletionCallbacks. Even when all OnSubmission from later frames are - // called, their SwapCompletionCallbacks should not run until previous frames' + // This tests multiple buffers per-frame and order of SwapCompletionCallbacks. + // Even when all OnSubmission from later frames are called, their + // SwapCompletionCallbacks should not run until previous frames' // SwapCompletionCallbacks run. gl::SetGLImplementation(gl::kGLImplementationEGLGLES2); @@ -493,10 +282,10 @@ TEST_P(WaylandSurfaceFactoryTest, // Also, we expect only one buffer to be committed. EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); - EXPECT_CALL(*root_surface, Frame(_)).Times(1); + EXPECT_CALL(*root_surface, Frame(_)).Times(0); EXPECT_CALL(*root_surface, Commit()).Times(1); Sync(); @@ -553,15 +342,15 @@ TEST_P(WaylandSurfaceFactoryTest, // Expect one buffer to be committed. EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); - EXPECT_CALL(*root_surface, Frame(_)).Times(1); + EXPECT_CALL(*root_surface, Frame(_)).Times(0); EXPECT_CALL(*root_surface, Commit()).Times(1); // Send the frame callback so that pending buffer for swap id=1u is processed // and swapped. - root_surface->SendFrameCallback(); + mock_primary_surface->SendFrameCallback(); Sync(); @@ -620,12 +409,12 @@ TEST_P(WaylandSurfaceFactoryTest, EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(0); EXPECT_CALL(*mock_primary_surface, Commit()).Times(0); // Expect root surface to be committed. - EXPECT_CALL(*root_surface, Frame(_)).Times(1); + EXPECT_CALL(*root_surface, Frame(_)).Times(0); EXPECT_CALL(*root_surface, Commit()).Times(1); // Send the frame callback so that pending buffer for swap id=2u is processed // and swapped. - root_surface->SendFrameCallback(); + mock_primary_surface->SendFrameCallback(); Sync(); @@ -754,10 +543,10 @@ TEST_P(WaylandSurfaceFactoryTest, // Also, we expect primary buffer to be committed. EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); - EXPECT_CALL(*root_surface, Frame(_)).Times(1); + EXPECT_CALL(*root_surface, Frame(_)).Times(0); EXPECT_CALL(*root_surface, Commit()).Times(1); Sync(); @@ -829,23 +618,24 @@ TEST_P(WaylandSurfaceFactoryTest, // Expect primary buffer to be committed. EXPECT_CALL(*mock_primary_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_primary_surface, Frame(_)).Times(1); EXPECT_CALL(*mock_primary_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_primary_surface, Commit()).Times(1); // Expect overlay buffer to be committed. EXPECT_CALL(*mock_overlay_surface, Attach(_, _, _)).Times(1); - EXPECT_CALL(*mock_overlay_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_overlay_surface, Frame(_)).Times(1); EXPECT_CALL(*mock_overlay_surface, DamageBuffer(_, _, _, _)).Times(1); EXPECT_CALL(*mock_overlay_surface, Commit()).Times(1); // Expect root surface to be committed without buffer. - EXPECT_CALL(*root_surface, Frame(_)).Times(1); + EXPECT_CALL(*root_surface, Frame(_)).Times(0); EXPECT_CALL(*root_surface, Commit()).Times(1); // Send the frame callback so that pending buffer for swap id=1u is processed // and swapped. - root_surface->SendFrameCallback(); + mock_overlay_surface->SendFrameCallback(); + mock_primary_surface->SendFrameCallback(); Sync(); diff --git a/chromium/ui/ozone/platform/wayland/host/DEPS b/chromium/ui/ozone/platform/wayland/host/DEPS index 3ed58be77cd..b9371fab272 100644 --- a/chromium/ui/ozone/platform/wayland/host/DEPS +++ b/chromium/ui/ozone/platform/wayland/host/DEPS @@ -1,5 +1,7 @@ include_rules = [ # For Lacros. + "+chromeos/crosapi/mojom", "+chromeos/crosapi/cpp/crosapi_constants.h", + "+chromeos/lacros/lacros_chrome_service_impl.h", "+chromeos/ui/base", ] diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc index 45540640759..ed4dc91240c 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc @@ -41,10 +41,6 @@ void GtkPrimarySelectionDevice::OnDataOffer( gtk_primary_selection_offer* offer) { auto* self = static_cast<GtkPrimarySelectionDevice*>(data); DCHECK(self); - - self->connection()->clipboard()->UpdateSequenceNumber( - ClipboardBuffer::kSelection); - self->set_data_offer(std::make_unique<GtkPrimarySelectionOffer>(offer)); } @@ -57,17 +53,16 @@ void GtkPrimarySelectionDevice::OnSelection( DCHECK(self); // 'offer' will be null to indicate that the selection is no longer valid, - // i.e. there is no longer clipboard data available to paste. + // i.e. there is no longer selection data available to be fetched. if (!offer) { self->ResetDataOffer(); - - // Clear Clipboard cache. - self->connection()->clipboard()->SetData({}, {}); - return; + } else { + DCHECK(self->data_offer()); + self->data_offer()->EnsureTextMimeTypeIfNeeded(); } - DCHECK(self->data_offer()); - self->data_offer()->EnsureTextMimeTypeIfNeeded(); + if (self->selection_delegate()) + self->selection_delegate()->OnSelectionOffer(self->data_offer()); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc index 0a0e4712910..cb501cbd8f0 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc @@ -4,24 +4,31 @@ #include "ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h" -#include <gdk/gdkwayland.h> #include <gtk/gtk.h> #include <memory> #include "base/bind.h" +#include "base/environment.h" #include "base/logging.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_surface.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_window_manager.h" #include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h" #include "ui/platform_window/platform_window_init_properties.h" +#if GTK_CHECK_VERSION(3, 90, 0) +#include <gdk/wayland/gdkwayland.h> +#else +#include <gdk/gdkwayland.h> + #define WEAK_GTK_FN(x) extern "C" __attribute__((weak)) decltype(x) x WEAK_GTK_FN(gdk_wayland_window_set_transient_for_exported); +#endif namespace ui { @@ -29,6 +36,9 @@ GtkUiDelegateWayland::GtkUiDelegateWayland(WaylandConnection* connection) : connection_(connection) { DCHECK(connection_); gdk_set_allowed_backends("wayland"); + // GDK_BACKEND takes precedence over gdk_set_allowed_backends(), so override + // it to ensure we get the wayland backend. + base::Environment::Create()->SetVar("GDK_BACKEND", "wayland"); } GtkUiDelegateWayland::~GtkUiDelegateWayland() = default; @@ -51,12 +61,14 @@ GdkWindow* GtkUiDelegateWayland::GetGdkWindow( bool GtkUiDelegateWayland::SetGdkWindowTransientFor( GdkWindow* window, gfx::AcceleratedWidget parent) { +#if !GTK_CHECK_VERSION(3, 90, 0) if (!gdk_wayland_window_set_transient_for_exported) { LOG(WARNING) << "set_transient_for_exported not supported in GTK version " << GTK_MAJOR_VERSION << '.' << GTK_MINOR_VERSION << '.' << GTK_MICRO_VERSION; return false; } +#endif auto* parent_window = connection_->wayland_window_manager()->GetWindow(parent); @@ -82,10 +94,19 @@ void GtkUiDelegateWayland::ShowGtkWindow(GtkWindow* window) { gtk_window_present(window); } +int GtkUiDelegateWayland::GetGdkKeyState() { + // TODO(crbug/1159460): Test fcitx unikey IME on ozone/wayland. + return connection_->event_source()->keyboard_modifiers(); +} + void GtkUiDelegateWayland::OnHandle(GdkWindow* window, const std::string& handle) { - gdk_wayland_window_set_transient_for_exported( - window, const_cast<char*>(handle.c_str())); + char* parent = const_cast<char*>(handle.c_str()); +#if GTK_CHECK_VERSION(3, 90, 0) + gdk_wayland_toplevel_set_transient_for_exported(GDK_TOPLEVEL(window), parent); +#else + gdk_wayland_window_set_transient_for_exported(window, parent); +#endif } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h index e5e23aeddac..d198057b8ca 100644 --- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h +++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h @@ -30,6 +30,7 @@ class GtkUiDelegateWayland : public GtkUiDelegate { gfx::AcceleratedWidget parent) override; void ClearTransientFor(gfx::AcceleratedWidget parent) override; void ShowGtkWindow(GtkWindow* window) override; + int GetGdkKeyState() override; private: // Called when xdg-foreign exports a parent window passed in diff --git a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc index dea71480eb6..ed7842b76c2 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc +++ b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc @@ -8,20 +8,37 @@ #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h" #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" +#include "ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h" +#include "ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.h" +#include "ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h" +#include "ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h" namespace ui { ShellObjectFactory::ShellObjectFactory() = default; ShellObjectFactory::~ShellObjectFactory() = default; -std::unique_ptr<ShellSurfaceWrapper> -ShellObjectFactory::CreateShellSurfaceWrapper(WaylandConnection* connection, - WaylandWindow* wayland_window) { - if (connection->shell() || connection->shell_v6()) { +std::unique_ptr<ShellToplevelWrapper> +ShellObjectFactory::CreateShellToplevelWrapper(WaylandConnection* connection, + WaylandWindow* wayland_window) { + if (connection->shell()) { auto surface = std::make_unique<XDGSurfaceWrapperImpl>(wayland_window, connection); - return surface->Initialize(true /* with_top_level */) ? std::move(surface) - : nullptr; + if (!surface->Initialize()) + return nullptr; + + auto toplevel = std::make_unique<XDGToplevelWrapperImpl>( + std::move(surface), wayland_window, connection); + return toplevel->Initialize() ? std::move(toplevel) : nullptr; + } else if (connection->shell_v6()) { + auto surface = + std::make_unique<ZXDGSurfaceV6WrapperImpl>(wayland_window, connection); + if (!surface->Initialize()) + return nullptr; + + auto toplevel = std::make_unique<ZXDGToplevelV6WrapperImpl>( + std::move(surface), wayland_window, connection); + return toplevel->Initialize() ? std::move(toplevel) : nullptr; } LOG(WARNING) << "Shell protocol is not available."; return nullptr; @@ -31,15 +48,24 @@ std::unique_ptr<ShellPopupWrapper> ShellObjectFactory::CreateShellPopupWrapper( WaylandConnection* connection, WaylandWindow* wayland_window, const gfx::Rect& bounds) { - if (connection->shell() || connection->shell_v6()) { + if (connection->shell()) { auto surface = std::make_unique<XDGSurfaceWrapperImpl>(wayland_window, connection); - if (!surface->Initialize(false /* with_top_level */)) + if (!surface->Initialize()) return nullptr; auto popup = std::make_unique<XDGPopupWrapperImpl>(std::move(surface), wayland_window); return popup->Initialize(connection, bounds) ? std::move(popup) : nullptr; + } else if (connection->shell_v6()) { + auto surface = + std::make_unique<ZXDGSurfaceV6WrapperImpl>(wayland_window, connection); + if (!surface->Initialize()) + return nullptr; + + auto popup = std::make_unique<ZXDGPopupV6WrapperImpl>(std::move(surface), + wayland_window); + return popup->Initialize(connection, bounds) ? std::move(popup) : nullptr; } LOG(WARNING) << "Shell protocol is not available."; return nullptr; diff --git a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.h b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.h index a915326c23f..da3472a5018 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.h +++ b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.h @@ -13,7 +13,7 @@ class Rect; namespace ui { -class ShellSurfaceWrapper; +class ShellToplevelWrapper; class ShellPopupWrapper; class WaylandConnection; class WaylandWindow; @@ -27,8 +27,8 @@ class ShellObjectFactory { ShellObjectFactory(); ~ShellObjectFactory(); - // Creates and initializes a ShellSurfaceWrapper. - std::unique_ptr<ShellSurfaceWrapper> CreateShellSurfaceWrapper( + // Creates and initializes a ShellToplevelWrapper. + std::unique_ptr<ShellToplevelWrapper> CreateShellToplevelWrapper( WaylandConnection* connection, WaylandWindow* wayland_window); @@ -41,4 +41,4 @@ class ShellObjectFactory { } // namespace ui -#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_OBJECT_FACTORY_H_
\ No newline at end of file +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_OBJECT_FACTORY_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc index f9f5aaa0825..bcf8fc25847 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc +++ b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc @@ -6,6 +6,10 @@ #include "base/check_op.h" #include "base/notreached.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_event_source.h" +#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" namespace ui { @@ -79,4 +83,119 @@ gfx::Rect GetAnchorRect(MenuType menu_type, return anchor_rect; } +WlAnchor GetAnchor(MenuType menu_type, const gfx::Rect& bounds) { + WlAnchor anchor = WlAnchor::None; + switch (menu_type) { + case MenuType::TYPE_RIGHT_CLICK: + anchor = WlAnchor::TopLeft; + break; + case MenuType::TYPE_3DOT_PARENT_MENU: + anchor = WlAnchor::BottomRight; + break; + case MenuType::TYPE_3DOT_CHILD_MENU: + // Chromium may want to manually position a child menu on the left side of + // its parent menu. Thus, react accordingly. Positive x means the child is + // located on the right side of the parent and negative - on the left + // side. + if (bounds.x() >= 0) + anchor = WlAnchor::TopRight; + else + anchor = WlAnchor::TopLeft; + break; + case MenuType::TYPE_UNKNOWN: + NOTREACHED() << "Unsupported menu type"; + break; + } + + return anchor; +} + +WlGravity GetGravity(MenuType menu_type, const gfx::Rect& bounds) { + WlGravity gravity = WlGravity::None; + switch (menu_type) { + case MenuType::TYPE_RIGHT_CLICK: + gravity = WlGravity::BottomRight; + break; + case MenuType::TYPE_3DOT_PARENT_MENU: + gravity = WlGravity::BottomRight; + break; + case MenuType::TYPE_3DOT_CHILD_MENU: + // Chromium may want to manually position a child menu on the left side of + // its parent menu. Thus, react accordingly. Positive x means the child is + // located on the right side of the parent and negative - on the left + // side. + if (bounds.x() >= 0) + gravity = WlGravity::BottomRight; + else + gravity = WlGravity::BottomLeft; + break; + case MenuType::TYPE_UNKNOWN: + NOTREACHED() << "Unsupported menu type"; + break; + } + + return gravity; +} + +WlConstraintAdjustment GetConstraintAdjustment(MenuType menu_type) { + WlConstraintAdjustment constraint = WlConstraintAdjustment::None; + + switch (menu_type) { + case MenuType::TYPE_RIGHT_CLICK: + constraint = + WlConstraintAdjustment::SlideX | WlConstraintAdjustment::SlideY | + WlConstraintAdjustment::FlipY | WlConstraintAdjustment::ResizeY; + break; + case MenuType::TYPE_3DOT_PARENT_MENU: + constraint = WlConstraintAdjustment::SlideX | + WlConstraintAdjustment::FlipY | + WlConstraintAdjustment::ResizeY; + break; + case MenuType::TYPE_3DOT_CHILD_MENU: + constraint = WlConstraintAdjustment::SlideY | + WlConstraintAdjustment::FlipX | + WlConstraintAdjustment::ResizeY; + break; + case MenuType::TYPE_UNKNOWN: + NOTREACHED() << "Unsupported menu type"; + break; + } + + return constraint; +} + +MenuType ShellPopupWrapper::GetMenuTypeForPositioner( + WaylandConnection* connection, + WaylandWindow* parent_window) const { + bool is_right_click_menu = + connection->event_source()->last_pointer_button_pressed() & + EF_RIGHT_MOUSE_BUTTON; + + // Different types of menu require different anchors, constraint adjustments, + // gravity and etc. + if (is_right_click_menu) + return MenuType::TYPE_RIGHT_CLICK; + else if (!wl::IsMenuType(parent_window->type())) + return MenuType::TYPE_3DOT_PARENT_MENU; + else + return MenuType::TYPE_3DOT_CHILD_MENU; +} + +bool ShellPopupWrapper::CanGrabPopup(WaylandConnection* connection) const { + // When drag process starts, as described the protocol - + // https://goo.gl/1Mskq3, the client must have an active implicit grab. If + // we try to create a popup and grab it, it will be immediately dismissed. + // Thus, do not take explicit grab during drag process. + if (connection->IsDragInProgress() || !connection->seat()) + return false; + + // According to the definition of the xdg protocol, the grab request must be + // used in response to some sort of user action like a button press, key + // press, or touch down event. + EventType last_event_type = connection->event_serial().event_type; + return last_event_type == ET_TOUCH_PRESSED || + last_event_type == ET_KEY_PRESSED || + last_event_type == ET_MOUSE_PRESSED; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.h b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.h index 2970549dd09..77eaa71794d 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.h +++ b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.h @@ -11,6 +11,7 @@ namespace ui { class WaylandConnection; +class WaylandWindow; enum class MenuType { TYPE_RIGHT_CLICK, @@ -73,11 +74,21 @@ class ShellPopupWrapper { // Initializes the popup surface. virtual bool Initialize(WaylandConnection* connection, const gfx::Rect& bounds) = 0; + + // Sends acknowledge configure event back to wayland. + virtual void AckConfigure(uint32_t serial) = 0; + + MenuType GetMenuTypeForPositioner(WaylandConnection* connection, + WaylandWindow* parent_window) const; + bool CanGrabPopup(WaylandConnection* connection) const; }; gfx::Rect GetAnchorRect(MenuType menu_type, const gfx::Rect& menu_bounds, const gfx::Rect& parent_window_bounds); +WlAnchor GetAnchor(MenuType menu_type, const gfx::Rect& bounds); +WlGravity GetGravity(MenuType menu_type, const gfx::Rect& bounds); +WlConstraintAdjustment GetConstraintAdjustment(MenuType menu_type); } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/shell_surface_wrapper.h b/chromium/ui/ozone/platform/wayland/host/shell_surface_wrapper.h index c75e87a627b..d89aacbb46f 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_surface_wrapper.h +++ b/chromium/ui/ozone/platform/wayland/host/shell_surface_wrapper.h @@ -5,8 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_SURFACE_WRAPPER_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_SURFACE_WRAPPER_H_ -#include "base/strings/string16.h" -#include "ui/ozone/platform/wayland/common/wayland_object.h" +#include <cstdint> namespace gfx { class Rect; @@ -14,63 +13,21 @@ class Rect; namespace ui { -class WaylandConnection; - -// Wrapper interface for different wayland shells shell versions. +// Wrapper interface for different wayland xdg-shell surface versions. class ShellSurfaceWrapper { public: virtual ~ShellSurfaceWrapper() {} - // Initializes the ShellSurface. Some protocols may require to create shell - // surface without toplevel role and assign a popup role to it later. - virtual bool Initialize(bool with_toplevel) = 0; - - // Sets a native window to maximized state. - virtual void SetMaximized() = 0; - - // Unsets a native window from maximized state. - virtual void UnSetMaximized() = 0; - - // Sets a native window to fullscreen state. - virtual void SetFullscreen() = 0; - - // Unsets a native window from fullscreen state. - virtual void UnSetFullscreen() = 0; - - // Sets a native window to minimized state. - virtual void SetMinimized() = 0; - - // Tells wayland to start interactive window drag. - virtual void SurfaceMove(WaylandConnection* connection) = 0; - - // Tells wayland to start interactive window resize. - virtual void SurfaceResize(WaylandConnection* connection, - uint32_t hittest) = 0; - - // Sets a title of a native window. - virtual void SetTitle(const base::string16& title) = 0; + // Initializes the ShellSurface. + virtual bool Initialize() = 0; // Sends acknowledge configure event back to wayland. - virtual void AckConfigure() = 0; + virtual void AckConfigure(uint32_t serial) = 0; // Sets a desired window geometry once wayland requests client to do so. virtual void SetWindowGeometry(const gfx::Rect& bounds) = 0; - - // Sets the minimum size for the top level. - virtual void SetMinSize(int32_t width, int32_t height) = 0; - - // Sets the maximum size for the top level. - virtual void SetMaxSize(int32_t width, int32_t height) = 0; - - // Sets an app id of the native window that is shown as an application name - // and hints the compositor that it can group application surfaces together by - // their app id. This also helps the compositor to identify application's - // .desktop file and use the icon set there. - virtual void SetAppId(const std::string& app_id) = 0; }; -bool CheckIfWlArrayHasValue(struct wl_array* wl_array, uint32_t value); - } // namespace ui #endif // UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_SURFACE_WRAPPER_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/shell_surface_wrapper.cc b/chromium/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.cc index ea7d3ca8c94..e84d7ca63bb 100644 --- a/chromium/ui/ozone/platform/wayland/host/shell_surface_wrapper.cc +++ b/chromium/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" +#include "ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h" namespace ui { diff --git a/chromium/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h b/chromium/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h new file mode 100644 index 00000000000..8767166a13d --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h @@ -0,0 +1,98 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_TOPLEVEL_WRAPPER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_TOPLEVEL_WRAPPER_H_ + +#include "base/strings/string16.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" + +namespace gfx { +class Rect; +} + +namespace ui { + +class WaylandConnection; + +// A wrapper around different versions of xdg toplevels. Allows +// WaylandToplevelWindow to set window-like properties such as maximize, +// fullscreen, and minimize, set application-specific metadata like title and +// id, as well as trigger user interactive operations such as interactive resize +// and move. +class ShellToplevelWrapper { + public: + enum class DecorationMode { + // Client-side decoration for a window. + // In this case, the client is responsible for drawing decorations + // for a window (e.g. caption bar, close button). This is suitable for + // windows using custom frame. + kClientSide = 1, + // Server-side decoration for a window. + // In this case, the ash window manager is responsible for drawing + // decorations. This is suitable for windows using native frame. + // e.g. taskmanager. + kServerSide + }; + + virtual ~ShellToplevelWrapper() = default; + + // Initializes the ShellToplevel. + virtual bool Initialize() = 0; + + // Sets a native window to maximized state. + virtual void SetMaximized() = 0; + + // Unsets a native window from maximized state. + virtual void UnSetMaximized() = 0; + + // Sets a native window to fullscreen state. + virtual void SetFullscreen() = 0; + + // Unsets a native window from fullscreen state. + virtual void UnSetFullscreen() = 0; + + // Sets a native window to minimized state. + virtual void SetMinimized() = 0; + + // Tells wayland to start interactive window drag. + virtual void SurfaceMove(WaylandConnection* connection) = 0; + + // Tells wayland to start interactive window resize. + virtual void SurfaceResize(WaylandConnection* connection, + uint32_t hittest) = 0; + + // Sets a title of a native window. + virtual void SetTitle(const base::string16& title) = 0; + + // Sends acknowledge configure event back to wayland. + virtual void AckConfigure(uint32_t serial) = 0; + + // Sets a desired window geometry once wayland requests client to do so. + virtual void SetWindowGeometry(const gfx::Rect& bounds) = 0; + + // Sets the minimum size for the top level. + virtual void SetMinSize(int32_t width, int32_t height) = 0; + + // Sets the maximum size for the top level. + virtual void SetMaxSize(int32_t width, int32_t height) = 0; + + // Sets an app id of the native window that is shown as an application name + // and hints the compositor that it can group application surfaces together by + // their app id. This also helps the compositor to identify application's + // .desktop file and use the icon set there. + virtual void SetAppId(const std::string& app_id) = 0; + + // In case of kClientSide or kServerSide, this function sends a request to the + // wayland compositor to update the decoration mode for a surface associated + // with this top level window. + virtual void SetDecoration(DecorationMode decoration) = 0; +}; + +// Look for |value| in |wl_array| in C++ style. +bool CheckIfWlArrayHasValue(struct wl_array* wl_array, uint32_t value); + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_SHELL_TOPLEVEL_WRAPPER_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc index 5139572e51b..7d8cb1086f5 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc @@ -46,7 +46,7 @@ void WaylandAuxiliaryWindow::SetBounds(const gfx::Rect& bounds) { auto old_bounds = GetBounds(); WaylandWindow::SetBounds(bounds); - if (old_bounds == bounds || !parent_window()) + if (old_bounds == bounds || !parent_window() || !subsurface_) return; auto subsurface_bounds_dip = @@ -58,22 +58,11 @@ void WaylandAuxiliaryWindow::SetBounds(const gfx::Rect& bounds) { } void WaylandAuxiliaryWindow::CreateSubsurface() { - auto* parent = parent_window(); - if (!parent) { - // wl_subsurface can be used for several purposes: tooltips and drag arrow - // windows. If we are in a drag process, use the entered window. Otherwise, - // it must be a tooltip. - if (connection()->IsDragInProgress()) { - parent = connection()->data_drag_controller()->entered_window(); - set_parent_window(parent); - } else { - // If Aura does not not provide a reference parent window, needed by - // Wayland, we get the current focused window to place and show the - // tooltips. - parent = - connection()->wayland_window_manager()->GetCurrentFocusedWindow(); - } - } + auto* parent = FindParentWindow(); + set_parent_window(parent); + + // We need to make sure that buffer scale matches the parent window. + UpdateBufferScale(true); // Tooltip and drag arrow creation is an async operation. By the time Aura // actually creates them, it is possible that the user has already moved the @@ -101,14 +90,30 @@ void WaylandAuxiliaryWindow::CreateSubsurface() { connection()->wayland_window_manager()->NotifyWindowConfigured(this); } +WaylandWindow* WaylandAuxiliaryWindow::FindParentWindow() { + // wl_subsurface can be used for several purposes: tooltips and drag arrow + // windows. If we are in a drag process, use the entered window. Otherwise, + // it must be a tooltip. + if (connection()->IsDragInProgress()) + return connection()->data_drag_controller()->entered_window(); + + // We get the current focused window to place and show the + // tooltips. + return connection()->wayland_window_manager()->GetCurrentFocusedWindow(); +} + bool WaylandAuxiliaryWindow::OnInitialize( PlatformWindowInitProperties properties) { DCHECK(!parent_window()); // If we do not have parent window provided, we must always use a focused // window or a window that entered drag whenever the subsurface is created. - if (properties.parent_widget == gfx::kNullAcceleratedWidget) + if (properties.parent_widget == gfx::kNullAcceleratedWidget) { + // Need to set the possible parent window here, so the initial scale will be + // calculated correctly. + set_parent_window(FindParentWindow()); return true; + } set_parent_window( connection()->wayland_window_manager()->FindParentForNewWindow( diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h index 0db19755696..0fb87289e10 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h @@ -31,6 +31,9 @@ class WaylandAuxiliaryWindow : public WaylandWindow { // Creates (if necessary) and shows a subsurface window. void CreateSubsurface(); + // Finds an appropriate parent window. + WaylandWindow* FindParentWindow(); + wl::Object<wl_subsurface> subsurface_; }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_connector.cc b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_connector.cc index da4fdd5fa93..0a6e6d5b9d6 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_connector.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_connector.cc @@ -84,9 +84,9 @@ void WaylandBufferManagerConnector::OnBufferManagerHostPtrBinded( #if defined(WAYLAND_GBM) supports_dma_buf = buffer_manager_host_->SupportsDmabuf(); #endif - buffer_manager_gpu_remote->Initialize(std::move(buffer_manager_host), - buffer_formats_with_modifiers, - supports_dma_buf); + buffer_manager_gpu_remote->Initialize( + std::move(buffer_manager_host), buffer_formats_with_modifiers, + supports_dma_buf, buffer_manager_host_->SupportsAcquireFence()); } void WaylandBufferManagerConnector::OnTerminateGpuProcess(std::string message) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc index fc9e374dc63..831babb51b4 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc @@ -7,11 +7,14 @@ #include <presentation-time-client-protocol.h> #include <memory> +#include "base/bind.h" #include "base/i18n/number_formatting.h" +#include "base/stl_util.h" #include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" #include "base/task/current_thread.h" #include "base/trace_event/trace_event.h" +#include "ui/gfx/gpu_fence.h" #include "ui/gfx/linux/drm_util_linux.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_drm.h" @@ -58,6 +61,46 @@ std::string NumberToString(uint32_t number) { } // namespace +struct WaylandBufferManagerHost::Frame { + explicit Frame(WaylandSurface* root_surface, + WaylandBufferManagerHost* manager) + : root_surface(root_surface), + buffer_manager_(manager), + weak_factory(this) {} + void IncrementPendingActions() { ++pending_actions; } + void PendingActionComplete() { + DCHECK(base::CurrentUIThread::IsSet()); + CHECK_GT(pending_actions, 0u); + if (!--pending_actions) + if (!std::move(frame_commit_cb).Run()) { + buffer_manager_->error_message_ = + base::StrCat({"Buffer with ", NumberToString(buffer_id), + " id does not exist or failed to be created."}); + buffer_manager_->TerminateGpuProcess(); + } + } + + // |root_surface| and |buffer_id| are saved so this Frame can be destroyed to + // prevent running |frame_commit_cb| in case the corresponding surface/buffer + // is removed. + WaylandSurface* root_surface; + uint32_t buffer_id = 0u; + + // Calls TerminateGpuProcess() if buffer does not exist. + WaylandBufferManagerHost* const buffer_manager_; + + // Number of actions to be completed before |frame_commit_cb| is run + // automatically. Such actions include: + // 1) End Frame; + // 2) Commit of a subsurface in this frame; + size_t pending_actions = 0u; + + // This runs WaylandBufferManagerHost::Surface::CommitBuffer() of + // |root_surface| with |buffer_id|. + base::OnceCallback<bool()> frame_commit_cb; + base::WeakPtrFactory<WaylandBufferManagerHost::Frame> weak_factory; +}; + class WaylandBufferManagerHost::Surface { public: Surface(WaylandSurface* wayland_surface, @@ -68,16 +111,22 @@ class WaylandBufferManagerHost::Surface { buffer_manager_(buffer_manager) {} ~Surface() = default; - bool CommitBuffer(uint32_t buffer_id, - const gfx::Rect& damage_region, - bool wait_for_frame_callback) { + bool CommitBuffer( + uint32_t buffer_id, + const gfx::Rect& damage_region, + bool wait_for_frame_callback, + base::OnceClosure post_commit_cb, + gfx::GpuFenceHandle access_fence_handle = gfx::GpuFenceHandle()) { + DCHECK(!post_commit_cb.is_null()); // The window has already been destroyed. if (!wayland_surface_) return true; // This is a buffer-less commit, do not lookup buffers. if (buffer_id == kInvalidBufferId) { - pending_commits_.push_back({nullptr, wait_for_frame_callback}); + DCHECK(access_fence_handle.is_null()); + pending_commits_.push_back({nullptr, wait_for_frame_callback, nullptr, + std::move(post_commit_cb)}); MaybeProcessPendingBuffer(); return true; } @@ -107,7 +156,10 @@ class WaylandBufferManagerHost::Surface { if (buffer->attached && !buffer->wl_buffer) return false; - pending_commits_.push_back({buffer, wait_for_frame_callback}); + pending_commits_.push_back( + {buffer, wait_for_frame_callback, + std::make_unique<gfx::GpuFence>(std::move(access_fence_handle)), + std::move(post_commit_cb)}); MaybeProcessPendingBuffer(); return true; } @@ -122,8 +174,10 @@ class WaylandBufferManagerHost::Surface { MaybeProcessSubmittedBuffers(); for (auto it = pending_commits_.begin(); it != pending_commits_.end(); ++it) { - if (it->buffer == buffer) + if (it->buffer == buffer) { + std::move(it->post_commit_cb).Run(); pending_commits_.erase(it++); + } } } @@ -155,6 +209,8 @@ class WaylandBufferManagerHost::Surface { ResetSurfaceContents(); submitted_buffers_.clear(); + for (auto& pending_commit : pending_commits_) + std::move(pending_commit.post_commit_cb).Run(); pending_commits_.clear(); connection_->ScheduleFlush(); @@ -227,9 +283,16 @@ class WaylandBufferManagerHost::Surface { // Whether this commit must wait for a wl_frame_callback and setup another // wl_frame_callback. bool wait_for_callback = false; + // Fence to wait on before the |buffer| content is available to read by + // Wayland host. + std::unique_ptr<gfx::GpuFence> access_fence; + // Callback to run once this commit is applied. + base::OnceClosure post_commit_cb; }; - bool CommitBufferInternal(WaylandBuffer* buffer, bool wait_for_callback) { + bool CommitBufferInternal(WaylandBuffer* buffer, + bool wait_for_callback, + const gfx::GpuFenceHandle& access_fence_handle) { DCHECK(buffer && wayland_surface_); // If the same buffer has been submitted again right after the client @@ -242,7 +305,7 @@ class WaylandBufferManagerHost::Surface { // Once the BufferRelease is called, the buffer will be released. DCHECK(buffer->released); buffer->released = false; - AttachBuffer(buffer); + AttachBuffer(buffer, access_fence_handle); } // If the client submits the same buffer twice, we need to store it twice, @@ -278,8 +341,11 @@ class WaylandBufferManagerHost::Surface { buffer->size); } - void AttachBuffer(WaylandBuffer* buffer) { + void AttachBuffer(WaylandBuffer* buffer, + const gfx::GpuFenceHandle& access_fence_handle) { DCHECK(wayland_surface_ && configured_); + if (!access_fence_handle.is_null()) + wayland_surface_->SetAcquireFence(access_fence_handle); wayland_surface_->AttachBuffer(buffer->wl_buffer.get()); } @@ -552,27 +618,30 @@ class WaylandBufferManagerHost::Surface { // // The third case happens if the window hasn't been configured until a // request to attach a buffer to its surface is sent. - auto pending_commit = std::move(pending_commits_.front()); + const auto& pending_commit = pending_commits_.front(); if ((pending_commit.buffer && !pending_commit.buffer->wl_buffer) || (wl_frame_callback_ && pending_commit.wait_for_callback) || !configured_) { return; } + auto commit = std::move(pending_commits_.front()); + pending_commits_.erase(pending_commits_.begin()); + // A Commit without attaching buffers only needs to setup wl_frame_callback. - if (!pending_commit.buffer) { - pending_commits_.erase(pending_commits_.begin()); - if (pending_commit.wait_for_callback) + if (!commit.buffer) { + if (commit.wait_for_callback) SetupFrameCallback(); CommitSurface(); + std::move(commit.post_commit_cb).Run(); connection_->ScheduleFlush(); MaybeProcessSubmittedBuffers(); return; } - pending_commits_.erase(pending_commits_.begin()); - CommitBufferInternal(pending_commit.buffer, - pending_commit.wait_for_callback); + CommitBufferInternal(commit.buffer, commit.wait_for_callback, + commit.access_fence->GetGpuFenceHandle()); + std::move(commit.post_commit_cb).Run(); } // Widget this helper surface backs and has 1:1 relationship with the @@ -645,6 +714,8 @@ void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) { surface_graveyard_.emplace_back(std::move(it->second)); } surfaces_.erase(it); + + RemovePendingFrames(window->root_surface(), 0u); } void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) { @@ -675,6 +746,8 @@ void WaylandBufferManagerHost::OnSubsurfaceRemoved( surface_graveyard_.emplace_back(std::move(it->second)); } surfaces_.erase(it); + + RemovePendingFrames(subsurface->wayland_surface(), 0u); } void WaylandBufferManagerHost::SetSurfaceConfigured(WaylandSurface* surface) { @@ -709,6 +782,7 @@ void WaylandBufferManagerHost::OnChannelDestroyed() { surface_pair.second->ClearState(); anonymous_buffers_.clear(); + pending_frames_.clear(); } wl::BufferFormatsWithModifiersMap @@ -727,6 +801,10 @@ bool WaylandBufferManagerHost::SupportsDmabuf() const { (connection_->drm() && connection_->drm()->SupportsDrmPrime()); } +bool WaylandBufferManagerHost::SupportsAcquireFence() const { + return !!connection_->linux_explicit_synchronization_v1(); +} + void WaylandBufferManagerHost::SetWaylandBufferManagerGpu( mojo::PendingAssociatedRemote<ozone::mojom::WaylandBufferManagerGpu> buffer_manager_gpu_associated) { @@ -803,19 +881,72 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd, connection_->ScheduleFlush(); } +void WaylandBufferManagerHost::StartFrame(WaylandSurface* root_surface) { + RemovePendingFrames(nullptr, 0u); + DCHECK_LE(pending_frames_.size(), 10u); + pending_frames_.push_back( + std::make_unique<WaylandBufferManagerHost::Frame>(root_surface, this)); + pending_frames_.back()->IncrementPendingActions(); +} + +void WaylandBufferManagerHost::EndFrame(uint32_t buffer_id, + const gfx::Rect& damage_region) { + DCHECK(base::CurrentUIThread::IsSet()); + + // If TerminateGpuProcess() is called, pending_frames_ would be cleared. + if (pending_frames_.empty()) + return; + + pending_frames_.back()->buffer_id = buffer_id; + Surface* surface = GetSurface(pending_frames_.back()->root_surface); + if (!surface) { + pending_frames_.erase(--pending_frames_.end()); + return; + } + + base::OnceClosure post_commit_cb = base::DoNothing(); + pending_frames_.back()->frame_commit_cb = base::BindOnce( + &WaylandBufferManagerHost::Surface::CommitBuffer, + base::Unretained(surface), buffer_id, damage_region, !!buffer_id, + std::move(post_commit_cb), gfx::GpuFenceHandle()); + + pending_frames_.back()->PendingActionComplete(); +} + +void WaylandBufferManagerHost::RemovePendingFrames(WaylandSurface* root_surface, + uint32_t buffer_id) { + base::EraseIf(pending_frames_, + [buffer_id, root_surface](const std::unique_ptr<Frame>& frame) { + return !frame->pending_actions || + (frame->buffer_id == buffer_id && buffer_id) || + (frame->root_surface == root_surface && root_surface); + }); +} + bool WaylandBufferManagerHost::CommitBufferInternal( WaylandSurface* wayland_surface, uint32_t buffer_id, const gfx::Rect& damage_region, - bool wait_for_frame_callback) { + bool wait_for_frame_callback, + bool commit_synced_subsurface, + gfx::GpuFenceHandle access_fence_handle) { DCHECK(base::CurrentUIThread::IsSet()); Surface* surface = GetSurface(wayland_surface); if (!surface || !ValidateBufferIdFromGpu(buffer_id)) return false; - if (!surface->CommitBuffer(buffer_id, damage_region, - wait_for_frame_callback)) { + base::OnceClosure subsurface_committed_cb = base::DoNothing(); + if (!pending_frames_.empty() && commit_synced_subsurface) { + pending_frames_.back()->IncrementPendingActions(); + subsurface_committed_cb.Reset(); + subsurface_committed_cb = + base::BindOnce(&WaylandBufferManagerHost::Frame::PendingActionComplete, + pending_frames_.back()->weak_factory.GetWeakPtr()); + } + if (!surface->CommitBuffer(buffer_id, damage_region, wait_for_frame_callback, + std::move(subsurface_committed_cb), + std::move(access_fence_handle))) { error_message_ = base::StrCat({"Buffer with ", NumberToString(buffer_id), " id does not exist or failed to be created."}); @@ -826,45 +957,6 @@ bool WaylandBufferManagerHost::CommitBufferInternal( return true; } -bool WaylandBufferManagerHost::CommitWithoutBufferInternal( - WaylandSurface* wayland_surface, - bool wait_for_frame_callback) { - DCHECK(base::CurrentUIThread::IsSet()); - - Surface* surface = GetSurface(wayland_surface); - if (!surface) - return false; - - bool result = surface->CommitBuffer(kInvalidBufferId, gfx::Rect(), - wait_for_frame_callback); - DCHECK(result); - - if (!error_message_.empty()) - TerminateGpuProcess(); - return true; -} - -void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget, - uint32_t buffer_id, - const gfx::Rect& damage_region) { - DCHECK(base::CurrentUIThread::IsSet()); - - TRACE_EVENT1("wayland", "WaylandBufferManagerHost::CommitBuffer", "Buffer id", - buffer_id); - - DCHECK(error_message_.empty()); - - if (widget == gfx::kNullAcceleratedWidget) { - error_message_ = "Invalid widget."; - TerminateGpuProcess(); - } else { - auto* window = connection_->wayland_window_manager()->GetWindow(widget); - if (!window) - return; - CommitBufferInternal(window->root_surface(), buffer_id, damage_region); - } -} - void WaylandBufferManagerHost::CommitOverlays( gfx::AcceleratedWidget widget, std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) { @@ -954,6 +1046,8 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget, } } + RemovePendingFrames(nullptr, buffer_id); + // Ensure that we can't destroy more than 1 buffer. This can be 0 as well // if no buffers are destroyed. DCHECK_LE(destroyed_count, 1u); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h index 28dc77dc8d0..8137f562f07 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h @@ -5,7 +5,6 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_BUFFER_MANAGER_HOST_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_BUFFER_MANAGER_HOST_H_ -#include <map> #include <memory> #include <vector> @@ -18,6 +17,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/gpu_fence_handle.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/presentation_feedback.h" #include "ui/gfx/swap_result.h" @@ -109,6 +109,7 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, wl::BufferFormatsWithModifiersMap GetSupportedBufferFormats() const; bool SupportsDmabuf() const; + bool SupportsAcquireFence() const; // ozone::mojom::WaylandBufferManagerHost overrides: // @@ -139,13 +140,6 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, // Called by the GPU to destroy the imported wl_buffer with a |buffer_id|. void DestroyBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id) override; - // Called by the GPU and asks to attach a wl_buffer with a |buffer_id| to a - // WaylandWindow with the specified |widget|. - // Calls OnSubmission and OnPresentation on successful swap and pixels - // presented. - void CommitBuffer(gfx::AcceleratedWidget widget, - uint32_t buffer_id, - const gfx::Rect& damage_region) override; // Called by the GPU and asks to configure the surface/subsurfaces and attach // wl_buffers to WaylandWindow with the specified |widget|. Calls OnSubmission // and OnPresentation on successful swap and pixels presented. @@ -153,6 +147,15 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, gfx::AcceleratedWidget widget, std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) override; + // Called by WaylandWindow to start recording a frame. This helps record the + // number of subsurface commits needed to finish for this frame before + // |root_surface| can be committed. + // This pairs with an EndCommitFrame(). Every CommitBufferInternal() in + // between increases the number of needed pending commits by 1. + void StartFrame(WaylandSurface* root_surface); + void EndFrame(uint32_t buffer_id = 0u, + const gfx::Rect& damage_region = gfx::Rect()); + // Called by the WaylandWindow and asks to attach a wl_buffer with a // |buffer_id| to a WaylandSurface. // Calls OnSubmission and OnPresentation on successful swap and pixels @@ -163,16 +166,16 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, // commit will move an entire wl_surface tree from pending state to ready // state. This root_surface commit must wait for wl_frame_callback, such that // in effect all other surface updates wait for this wl_frame_callback, too. - bool CommitBufferInternal(WaylandSurface* wayland_surface, - uint32_t buffer_id, - const gfx::Rect& damage_region, - bool wait_for_frame_callback = true); - - // Does a wl_surface commit without attaching any buffers. This commit will - // still wait for previous wl_frame_callback. Similar to above but for - // commits that do not change the root_surface. - bool CommitWithoutBufferInternal(WaylandSurface* wayland_surface, - bool wait_for_frame_callback = true); + // |access_fence_handle| specifies a gpu fence created by the gpu process. + // It's to be waited on before content of the buffer is ready to be read by + // Wayland host. + bool CommitBufferInternal( + WaylandSurface* wayland_surface, + uint32_t buffer_id, + const gfx::Rect& damage_region, + bool wait_for_frame_callback = true, + bool commit_synced_subsurface = false, + gfx::GpuFenceHandle access_fence_handle = gfx::GpuFenceHandle()); // When a surface is hidden, the client may want to detach the buffer attached // to the surface to ensure Wayland does not present those contents and do not @@ -189,10 +192,17 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, // presentation callbacks for that surface. class Surface; + // This represents a frame that consists of state changes to multiple + // synchronized wl_surfaces that are in the same hierarchy. It defers + // committing the root surface until all child surfaces' states are ready. + struct Frame; + bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id); Surface* GetSurface(WaylandSurface* wayland_surface) const; + void RemovePendingFrames(WaylandSurface* root_surface, uint32_t buffer_id); + // Validates data sent from GPU. If invalid, returns false and sets an error // message to |error_message_|. bool ValidateDataFromGpu(const base::ScopedFD& file, @@ -230,6 +240,10 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost, base::flat_map<WaylandSurface*, std::unique_ptr<Surface>> surfaces_; + // When StartCommitFrame() is called, a Frame is pushed to + // |pending_frames_|. See StartCommitFrame(). + std::vector<std::unique_ptr<Frame>> pending_frames_; + // When a WaylandWindow/WaylandSubsurface is removed, its corresponding // Surface may still have an un-released buffer and un-acked presentation. // Thus, we keep removed surfaces in the graveyard. It's safe to delete them diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc index 43d14126367..f6984d4ff54 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc @@ -8,18 +8,22 @@ #include <string> #include "base/check.h" +#include "base/memory/scoped_refptr.h" #include "base/notreached.h" +#include "base/optional.h" #include "ui/base/clipboard/clipboard_buffer.h" #include "ui/base/clipboard/clipboard_constants.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h" #include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h" -#include "ui/ozone/platform/wayland/host/zwp_primary_selection_device.h" -#include "ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_data_device.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device_base.h" #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" +#include "ui/ozone/platform/wayland/host/wayland_data_offer_base.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/platform/wayland/host/zwp_primary_selection_device.h" +#include "ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h" #include "ui/ozone/public/platform_clipboard.h" namespace wl { @@ -36,15 +40,20 @@ class Clipboard { virtual std::vector<std::string> ReadMimeTypes() = 0; // Asynchronously reads clipboard content with |mime_type| format. The result - // data is expected to arrive through WaylandClipboard::SetData(). - // TODO(nickdiego): Decouple DataDevice impls from WaylandClipboard. - virtual bool Read(const std::string& mime_type) = 0; + // data is expected to arrive through OnSelectionDataReceived() callback. + virtual bool Read(const std::string& mime_type, + ui::PlatformClipboard::DataMap* data_map, + ui::PlatformClipboard::RequestDataClosure callback) = 0; // Synchronously stores and announces |data| as available from this clipboard. virtual void Write(const ui::PlatformClipboard::DataMap* data) = 0; // Tells if this clipboard instance is the current selection owner. virtual bool IsSelectionOwner() const = 0; + + // Sets the callback in charge of updating the clipboard sequence number. + virtual void SetSequenceNumberUpdateCb( + ui::PlatformClipboard::SequenceNumberUpdateCb callback) = 0; }; // Templated wl::Clipboard implementation. Whereas DataSource is the data source @@ -55,41 +64,70 @@ class Clipboard { template <typename Manager, typename DataSource = typename Manager::DataSource, typename DataDevice = typename Manager::DataDevice> -class ClipboardImpl final : public Clipboard, public DataSource::Delegate { +class ClipboardImpl final : public Clipboard, + public DataSource::Delegate, + public DataDevice::SelectionDelegate { public: - explicit ClipboardImpl(Manager* manager) : manager_(manager) {} + explicit ClipboardImpl(Manager* manager, ui::ClipboardBuffer buffer) + : manager_(manager), buffer_(buffer) { + GetDevice()->set_selection_delegate(this); + } + ~ClipboardImpl() final { GetDevice()->set_selection_delegate(nullptr); } + ClipboardImpl(const ClipboardImpl&) = delete; ClipboardImpl& operator=(const ClipboardImpl&) = delete; - virtual ~ClipboardImpl() = default; - virtual bool Read(const std::string& mime_type) override { - return GetDevice()->RequestSelectionData(mime_type); + // TODO(crbug.com/1165466): Support nested clipboard requests. + bool Read(const std::string& mime_type, + ui::PlatformClipboard::DataMap* data_map, + ui::PlatformClipboard::RequestDataClosure callback) final { + DCHECK(data_map); + received_data_ = data_map; + read_clipboard_closure_ = std::move(callback); + + if (GetDevice()->RequestSelectionData(mime_type)) + return true; + SetData(base::MakeRefCounted<base::RefCountedBytes>(), mime_type); + return false; } - std::vector<std::string> ReadMimeTypes() override { + std::vector<std::string> ReadMimeTypes() final { return GetDevice()->GetAvailableMimeTypes(); } - virtual void Write(const ui::PlatformClipboard::DataMap* data) override { + // Once this client sends wl_data_source::offer, it is responsible for holding + // onto its clipboard contents. At future points in time, the wayland server + // may send a wl_data_source::send event, in which case this client is + // responsible for writing the clipboard contents into the supplied fd. This + // client can only drop the clipboard contents when it receives a + // wl_data_source::cancelled event. + void Write(const ui::PlatformClipboard::DataMap* data) final { if (!data || data->empty()) { - data_.clear(); + offered_data_.clear(); source_.reset(); } else { - data_ = *data; + offered_data_ = *data; source_ = manager_->CreateSource(this); source_->Offer(GetMimeTypes()); GetDevice()->SetSelectionSource(source_.get()); } } - bool IsSelectionOwner() const override { return !!source_; } + bool IsSelectionOwner() const final { return !!source_; } + + void SetSequenceNumberUpdateCb( + ui::PlatformClipboard::SequenceNumberUpdateCb callback) final { + CHECK(update_sequence_cb_.is_null()) + << "The callback can be installed only once."; + update_sequence_cb_ = callback; + } private: DataDevice* GetDevice() { return manager_->GetDevice(); } std::vector<std::string> GetMimeTypes() { std::vector<std::string> mime_types; - for (const auto& data : data_) { + for (const auto& data : offered_data_) { mime_types.push_back(data.first); if (data.first == ui::kMimeTypeText) mime_types.push_back(ui::kMimeTypeTextUtf8); @@ -97,6 +135,36 @@ class ClipboardImpl final : public Clipboard, public DataSource::Delegate { return mime_types; } + void SetData(ui::PlatformClipboard::Data contents, + const std::string& mime_type) { + if (!received_data_) + return; + + DCHECK(contents); + (*received_data_)[mime_type] = contents; + + if (!read_clipboard_closure_.is_null()) { + auto it = received_data_->find(mime_type); + DCHECK(it != received_data_->end()); + std::move(read_clipboard_closure_).Run(it->second); + } + received_data_ = nullptr; + } + + // WaylandDataDeviceBase::SelectionDelegate: + void OnSelectionOffer(ui::WaylandDataOfferBase* offer) final { + if (!update_sequence_cb_.is_null()) + update_sequence_cb_.Run(buffer_); + + if (!offer) + SetData({}, {}); + } + + void OnSelectionDataReceived(const std::string& mime_type, + ui::PlatformClipboard::Data contents) final { + SetData(contents, mime_type); + } + // WaylandDataSource::Delegate: void OnDataSourceFinish(bool completed) override { if (!completed) @@ -106,21 +174,34 @@ class ClipboardImpl final : public Clipboard, public DataSource::Delegate { void OnDataSourceSend(const std::string& mime_type, std::string* contents) override { DCHECK(contents); - auto it = data_.find(mime_type); - if (it == data_.end() && mime_type == ui::kMimeTypeTextUtf8) - it = data_.find(ui::kMimeTypeText); - if (it != data_.end()) + auto it = offered_data_.find(mime_type); + if (it == offered_data_.end() && mime_type == ui::kMimeTypeTextUtf8) + it = offered_data_.find(ui::kMimeTypeText); + if (it != offered_data_.end()) contents->assign(it->second->data().begin(), it->second->data().end()); } // The device manager used to access data device and create data sources. Manager* const manager_; + // The clipboard buffer managed by this |this|. + const ui::ClipboardBuffer buffer_; + // The current data source used to offer clipboard data. std::unique_ptr<DataSource> source_; // The data currently stored in a given clipboard buffer. - ui::PlatformClipboard::DataMap data_; + ui::PlatformClipboard::DataMap offered_data_; + + // Holds a temporary instance of the client's clipboard content + // so that we can asynchronously write to it. + ui::PlatformClipboard::DataMap* received_data_ = nullptr; + + // Stores the callback to be invoked upon data reading from clipboard. + ui::PlatformClipboard::RequestDataClosure read_clipboard_closure_; + + // Notifies when clipboard sequence must change. Can be empty if not set. + ui::PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_; }; } // namespace wl @@ -132,7 +213,8 @@ WaylandClipboard::WaylandClipboard(WaylandConnection* connection, : connection_(connection), copypaste_clipboard_( std::make_unique<wl::ClipboardImpl<WaylandDataDeviceManager>>( - manager)) { + manager, + ClipboardBuffer::kCopyPaste)) { DCHECK(manager); DCHECK(connection_); DCHECK(copypaste_clipboard_); @@ -149,19 +231,16 @@ void WaylandClipboard::OfferClipboardData( std::move(callback).Run(); } +// TODO(crbug.com/1165466): Support nested clipboard requests. void WaylandClipboard::RequestClipboardData( ClipboardBuffer buffer, const std::string& mime_type, PlatformClipboard::DataMap* data_map, PlatformClipboard::RequestDataClosure callback) { - DCHECK(data_map); - data_map_ = data_map; - read_clipboard_closure_ = std::move(callback); - auto* clipboard = GetClipboard(buffer); - if (!clipboard || !clipboard->Read(mime_type)) { - SetData(scoped_refptr<base::RefCountedBytes>(new base::RefCountedBytes()), - mime_type); - } + if (auto* clipboard = GetClipboard(buffer)) + clipboard->Read(mime_type, data_map, std::move(callback)); + else + std::move(callback).Run(base::nullopt); } bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) { @@ -172,9 +251,9 @@ bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) { void WaylandClipboard::SetSequenceNumberUpdateCb( PlatformClipboard::SequenceNumberUpdateCb cb) { - CHECK(update_sequence_cb_.is_null()) - << " The callback can be installed only once."; - update_sequence_cb_ = std::move(cb); + copypaste_clipboard_->SetSequenceNumberUpdateCb(cb); + if (auto* selection_clipboard = GetClipboard(ClipboardBuffer::kSelection)) + selection_clipboard->SetSequenceNumberUpdateCb(cb); } void WaylandClipboard::GetAvailableMimeTypes( @@ -191,27 +270,6 @@ bool WaylandClipboard::IsSelectionBufferAvailable() const { (connection_->gtk_primary_selection_device_manager() != nullptr); } -void WaylandClipboard::SetData(PlatformClipboard::Data contents, - const std::string& mime_type) { - if (!data_map_) - return; - - DCHECK(contents); - (*data_map_)[mime_type] = contents; - - if (!read_clipboard_closure_.is_null()) { - auto it = data_map_->find(mime_type); - DCHECK(it != data_map_->end()); - std::move(read_clipboard_closure_).Run(it->second); - } - data_map_ = nullptr; -} - -void WaylandClipboard::UpdateSequenceNumber(ClipboardBuffer buffer) { - if (!update_sequence_cb_.is_null()) - update_sequence_cb_.Run(buffer); -} - wl::Clipboard* WaylandClipboard::GetClipboard(ClipboardBuffer buffer) { if (buffer == ClipboardBuffer::kCopyPaste) return copypaste_clipboard_.get(); @@ -219,16 +277,17 @@ wl::Clipboard* WaylandClipboard::GetClipboard(ClipboardBuffer buffer) { if (buffer == ClipboardBuffer::kSelection) { if (auto* manager = connection_->zwp_primary_selection_device_manager()) { if (!primary_selection_clipboard_) { - primary_selection_clipboard_ = - std::make_unique<wl::ClipboardImpl<ZwpPrimarySelectionDeviceManager>>( - manager); + primary_selection_clipboard_ = std::make_unique< + wl::ClipboardImpl<ZwpPrimarySelectionDeviceManager>>( + manager, ClipboardBuffer::kSelection); } return primary_selection_clipboard_.get(); } else if (auto* manager = connection_->gtk_primary_selection_device_manager()) { if (!primary_selection_clipboard_) { primary_selection_clipboard_ = std::make_unique< - wl::ClipboardImpl<GtkPrimarySelectionDeviceManager>>(manager); + wl::ClipboardImpl<GtkPrimarySelectionDeviceManager>>( + manager, ClipboardBuffer::kSelection); } return primary_selection_clipboard_.get(); } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h index 515c85ca6c3..1b15f4f488e 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h @@ -7,13 +7,10 @@ #include <memory> #include <string> -#include <vector> #include "base/callback.h" -#include "base/containers/flat_map.h" -#include "base/macros.h" -#include "base/optional.h" #include "ui/base/clipboard/clipboard_buffer.h" +#include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/public/platform_clipboard.h" namespace wl { @@ -25,7 +22,12 @@ namespace ui { class WaylandConnection; class WaylandDataDeviceManager; -// Handles clipboard operations. +// This class is a wrapper around Wayland data_device protocols that simulates +// typical clipboard operations. Unlike some other platforms, data-transfer is +// an async, lazy operation. This means that even after "writing" data to the +// system clipboard, this class must still hold on to a local cache of the +// clipboard contents, since it may be read (repeatedly) by other Wayland +// clients. // // WaylandDataDeviceManager singleton is required to be up and running for // WaylandClipboard to be minimally functional. @@ -55,11 +57,6 @@ class WaylandClipboard : public PlatformClipboard { PlatformClipboard::SequenceNumberUpdateCb cb) override; bool IsSelectionBufferAvailable() const override; - // TODO(nickdiego): Get rid of these methods once DataDevice implementations - // are decoupled from WaylandClipboard. - void SetData(PlatformClipboard::Data contents, const std::string& mime_type); - void UpdateSequenceNumber(ClipboardBuffer buffer); - private: // Get the wl::Clipboard instance owning a given |buffer|. Can return null in // case |buffer| is unsupported. E.g: primary selection is not available. @@ -69,17 +66,6 @@ class WaylandClipboard : public PlatformClipboard { // primary selection. WaylandConnection* const connection_; - // Holds a temporary instance of the client's clipboard content - // so that we can asynchronously write to it. - PlatformClipboard::DataMap* data_map_ = nullptr; - - // Notifies whenever clipboard sequence number is changed. Can be empty if - // not set. - PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_; - - // Stores the callback to be invoked upon data reading from clipboard. - PlatformClipboard::RequestDataClosure read_clipboard_closure_; - const std::unique_ptr<wl::Clipboard> copypaste_clipboard_; std::unique_ptr<wl::Clipboard> primary_selection_clipboard_; }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard_unittest.cc new file mode 100644 index 00000000000..9318149af79 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard_unittest.cc @@ -0,0 +1,232 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <wayland-server.h> + +#include <cstring> +#include <memory> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/callback_forward.h" +#include "base/containers/contains.h" +#include "base/containers/flat_set.h" +#include "base/location.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/mock_callback.h" +#include "base/threading/thread_task_runner_handle.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/clipboard/clipboard_buffer.h" +#include "ui/base/clipboard/clipboard_constants.h" +#include "ui/events/base_event_utils.h" +#include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/test_data_device.h" +#include "ui/ozone/platform/wayland/test/test_data_device_manager.h" +#include "ui/ozone/platform/wayland/test/test_data_offer.h" +#include "ui/ozone/platform/wayland/test/test_data_source.h" +#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" +#include "ui/ozone/platform/wayland/test/wayland_test.h" +#include "ui/ozone/public/platform_clipboard.h" + +using testing::_; +using testing::Mock; + +namespace ui { + +namespace { + +constexpr char kSampleClipboardText[] = "This is a sample text for clipboard."; + +template <typename StringType> +ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) { + std::vector<uint8_t> data_vector; + data_vector.assign(data_string.begin(), data_string.end()); + return base::RefCountedBytes::TakeVector(&data_vector); +} + +} // namespace + +class WaylandClipboardTest : public WaylandTest { + public: + WaylandClipboardTest() = default; + + void SetUp() override { + WaylandTest::SetUp(); + + Sync(); + + data_device_manager_ = server_.data_device_manager(); + ASSERT_TRUE(data_device_manager_); + + clipboard_ = connection_->clipboard(); + ASSERT_TRUE(clipboard_); + + offered_data_.clear(); + } + + protected: + // Fill the clipboard backing store with sample data. + void OfferData(ClipboardBuffer buffer, + const char* data, + const std::string& mime_type) { + std::vector<uint8_t> data_vector(data, data + std::strlen(data)); + offered_data_[mime_type] = base::RefCountedBytes::TakeVector(&data_vector); + + base::MockCallback<PlatformClipboard::OfferDataClosure> offer_callback; + EXPECT_CALL(offer_callback, Run()).Times(1); + clipboard_->OfferClipboardData(buffer, offered_data_, offer_callback.Get()); + } + + wl::TestDataDeviceManager* data_device_manager_; + PlatformClipboard* clipboard_; + PlatformClipboard::DataMap offered_data_; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandClipboardTest); +}; + +TEST_P(WaylandClipboardTest, WriteToClipboard) { + // The client writes data to the clipboard ... + OfferData(ClipboardBuffer::kCopyPaste, kSampleClipboardText, + {kMimeTypeTextUtf8}); + Sync(); + + // ... and the server reads it. + base::RunLoop run_loop; + auto callback = base::BindOnce( + [](base::RunLoop* loop, std::vector<uint8_t>&& data) { + std::string string_data(data.begin(), data.end()); + EXPECT_EQ(kSampleClipboardText, string_data); + loop->Quit(); + }, + &run_loop); + + data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8, + std::move(callback)); + run_loop.Run(); +} + +TEST_P(WaylandClipboardTest, ReadFromClipboard) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(kMimeTypeTextUtf8, + ToClipboardData(std::string(kSampleClipboardText))); + data_device_manager_->data_device()->OnSelection(data_offer); + Sync(); + + // The client requests to read the clipboard data from the server. The Server + // writes in some sample data, and we check it matches expectation. + base::RunLoop run_loop; + auto callback = base::BindOnce( + [](base::OnceClosure quit_closure, + const base::Optional<PlatformClipboard::Data>& data) { + auto& bytes = data->get()->data(); + std::string string_data = std::string(bytes.begin(), bytes.end()); + EXPECT_EQ(kSampleClipboardText, string_data); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure()); + + PlatformClipboard::DataMap read_data_; + clipboard_->RequestClipboardData(ClipboardBuffer::kCopyPaste, + kMimeTypeTextUtf8, &read_data_, + std::move(callback)); + Sync(); + run_loop.Run(); +} + +TEST_P(WaylandClipboardTest, ReadFromClipboardWithoutOffer) { + // When no data offer is advertised and client requests clipboard data + // from the server, the response callback should be gracefully called with + // an empty string. + auto callback = + base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { + auto& bytes = data->get()->data(); + std::string string_data = std::string(bytes.begin(), bytes.end()); + EXPECT_EQ("", string_data); + }); + PlatformClipboard::DataMap read_data_; + clipboard_->RequestClipboardData(ClipboardBuffer::kCopyPaste, + kMimeTypeTextUtf8, &read_data_, + std::move(callback)); +} + +TEST_P(WaylandClipboardTest, IsSelectionOwner) { + OfferData(ClipboardBuffer::kCopyPaste, kSampleClipboardText, + {kMimeTypeTextUtf8}); + Sync(); + ASSERT_TRUE(clipboard_->IsSelectionOwner(ClipboardBuffer::kCopyPaste)); + + // The compositor sends OnCancelled whenever another application + // on the system sets a new selection. It means we are not the application + // that owns the current selection data. + data_device_manager_->data_source()->OnCancelled(); + Sync(); + + ASSERT_FALSE(clipboard_->IsSelectionOwner(ClipboardBuffer::kCopyPaste)); +} + +// Ensures WaylandClipboard correctly handles overlapping read requests for +// different clipboard buffers. +TEST_P(WaylandClipboardTest, OverlapReadingFromDifferentBuffers) { + // Offer a piece of text in kCopyPaste clipboard buffer. + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(kMimeTypeTextUtf8, + ToClipboardData(std::string(kSampleClipboardText))); + data_device_manager_->data_device()->OnSelection(data_offer); + Sync(); + + // Post a read request for kSelection buffer, which will start its execution + // after kCopyPaste request (below) starts. + PlatformClipboard::DataMap selection_data_; + base::MockCallback<PlatformClipboard::RequestDataClosure> selection_callback; + EXPECT_CALL(selection_callback, Run(_)).Times(1); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&PlatformClipboard::RequestClipboardData, + base::Unretained(clipboard_), ClipboardBuffer::kSelection, + kMimeTypeTextUtf8, base::Unretained(&selection_data_), + selection_callback.Get())); + + // Instantly start a clipboard read request for kCopyPaste buffer (the actual + // data transfer will take place asynchronously. See WaylandDataDevice impl) + // and ensure read callback is called and |copy_paste_data_| is filled as + // expected, regardless any other request that may arrive in the meantime. + PlatformClipboard::DataMap copy_paste_data_; + base::RunLoop run_loop; + clipboard_->RequestClipboardData( + ClipboardBuffer::kCopyPaste, kMimeTypeTextUtf8, ©_paste_data_, + base::BindOnce( + [](base::OnceClosure quit_closure, + const base::Optional<PlatformClipboard::Data>& data) { + ASSERT_TRUE(data.has_value()); + EXPECT_EQ(kSampleClipboardText, + std::string(data->get()->front_as<const char>(), + data->get()->size())); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure())); + + Sync(); + run_loop.Run(); + + EXPECT_TRUE(selection_data_.empty()); + + EXPECT_EQ(1u, copy_paste_data_.size()); + EXPECT_TRUE(base::Contains(copy_paste_data_, kMimeTypeTextUtf8)); + auto contents = copy_paste_data_[kMimeTypeTextUtf8]; + EXPECT_EQ(kSampleClipboardText, + std::string(contents->front_as<const char>(), contents->size())); +} + +INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, + WaylandClipboardTest, + ::testing::Values(kXdgShellStable)); + +INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, + WaylandClipboardTest, + ::testing::Values(kXdgShellV6)); + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc index 73b76474df1..702328d0e9f 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc @@ -53,7 +53,10 @@ namespace ui { namespace { -constexpr uint32_t kMaxAuraShellVersion = 11; +// The maximum supported versions for a given interface. +// The version bound will be the minimum of the value and the version +// advertised by the server. +constexpr uint32_t kMaxAuraShellVersion = 16; constexpr uint32_t kMaxCompositorVersion = 4; constexpr uint32_t kMaxCursorShapesVersion = 1; constexpr uint32_t kMaxGtkPrimarySelectionDeviceManagerVersion = 1; @@ -67,10 +70,13 @@ constexpr uint32_t kMaxWpPresentationVersion = 1; constexpr uint32_t kMaxWpViewporterVersion = 1; constexpr uint32_t kMaxTextInputManagerVersion = 1; constexpr uint32_t kMaxExplicitSyncVersion = 2; -constexpr uint32_t kMinWlDrmVersion = 2; -constexpr uint32_t kMinWlOutputVersion = 2; constexpr uint32_t kMaxXdgDecorationVersion = 1; constexpr uint32_t kMaxExtendedDragVersion = 1; +// The minimum required version for a given interface. +// Ensures that the version bound (advertised by server) is higher than this +// value. +constexpr uint32_t kMinWlDrmVersion = 2; +constexpr uint32_t kMinWlOutputVersion = 2; } // namespace WaylandConnection::WaylandConnection() = default; @@ -89,8 +95,22 @@ bool WaylandConnection::Initialize() { LOG(ERROR) << "Failed to load wayland client libraries."; return false; } + if (void* libwayland_egl = dlopen("libwayland-egl.so.1", dlopen_flags)) third_party_wayland::InitializeLibwaylandegl(libwayland_egl); + + // TODO(crbug.com/1081784): consider handling this in more flexible way. + // libwayland-cursor is said to be part of the standard shipment of Wayland, + // and it seems unlikely (although possible) that it would be unavailable + // while libwayland-client was present. To handle that gracefully, chrome can + // fall back to the generic Ozone behaviour. + if (void* libwayland_cursor = + dlopen("libwayland-cursor.so.0", dlopen_flags)) { + third_party_wayland::InitializeLibwaylandcursor(libwayland_cursor); + } else { + LOG(ERROR) << "Failed to load libwayland-cursor.so.0."; + return false; + } #endif static const wl_registry_listener registry_listener = { @@ -163,6 +183,21 @@ void WaylandConnection::SetShutdownCb(base::OnceCallback<void()> shutdown_cb) { event_source()->SetShutdownCb(std::move(shutdown_cb)); } +void WaylandConnection::SetPlatformCursor(wl_cursor* cursor_data, + int buffer_scale) { + if (!cursor_) + return; + cursor_->SetPlatformShape(cursor_data, serial(), buffer_scale); +} + +void WaylandConnection::SetCursorBufferListener( + WaylandCursorBufferListener* listener) { + listener_ = listener; + if (!cursor_) + return; + cursor_->set_listener(listener_); +} + void WaylandConnection::SetCursorBitmap(const std::vector<SkBitmap>& bitmaps, const gfx::Point& hotspot_in_dips, int buffer_scale) { @@ -205,6 +240,7 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat, pointer_ = std::make_unique<WaylandPointer>(pointer, this, event_source()); cursor_ = std::make_unique<WaylandCursor>(pointer_.get(), this); + cursor_->set_listener(listener_); wayland_cursor_position_ = std::make_unique<WaylandCursorPosition>(); } else { LOG(ERROR) << "Failed to get wl_pointer from seat"; @@ -362,7 +398,8 @@ void WaylandConnection::Global(void* data, strcmp(interface, "gtk_primary_selection_device_manager") == 0) { wl::Object<::gtk_primary_selection_device_manager> manager = wl::Bind<::gtk_primary_selection_device_manager>( - registry, name, kMaxGtkPrimarySelectionDeviceManagerVersion); + registry, name, + std::min(version, kMaxGtkPrimarySelectionDeviceManagerVersion)); connection->gtk_primary_selection_device_manager_ = std::make_unique<GtkPrimarySelectionDeviceManager>(manager.release(), connection); @@ -371,7 +408,8 @@ void WaylandConnection::Global(void* data, 0) { wl::Object<zwp_primary_selection_device_manager_v1> manager = wl::Bind<zwp_primary_selection_device_manager_v1>( - registry, name, kMaxGtkPrimarySelectionDeviceManagerVersion); + registry, name, + std::min(version, kMaxGtkPrimarySelectionDeviceManagerVersion)); connection->zwp_primary_selection_device_manager_ = std::make_unique<ZwpPrimarySelectionDeviceManager>(manager.release(), connection); @@ -390,16 +428,16 @@ void WaylandConnection::Global(void* data, zwp_linux_dmabuf.release(), connection); } else if (!connection->presentation_ && (strcmp(interface, "wp_presentation") == 0)) { - connection->presentation_ = - wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion); + connection->presentation_ = wl::Bind<wp_presentation>( + registry, name, std::min(version, kMaxWpPresentationVersion)); } else if (!connection->viewporter_ && (strcmp(interface, "wp_viewporter") == 0)) { - connection->viewporter_ = - wl::Bind<wp_viewporter>(registry, name, kMaxWpViewporterVersion); + connection->viewporter_ = wl::Bind<wp_viewporter>( + registry, name, std::min(version, kMaxWpViewporterVersion)); } else if (!connection->zcr_cursor_shapes_ && strcmp(interface, "zcr_cursor_shapes_v1") == 0) { - auto zcr_cursor_shapes = - wl::Bind<zcr_cursor_shapes_v1>(registry, name, kMaxCursorShapesVersion); + auto zcr_cursor_shapes = wl::Bind<zcr_cursor_shapes_v1>( + registry, name, std::min(version, kMaxCursorShapesVersion)); if (!zcr_cursor_shapes) { LOG(ERROR) << "Failed to bind zcr_cursor_shapes_v1"; return; @@ -409,7 +447,7 @@ void WaylandConnection::Global(void* data, } else if (!connection->keyboard_extension_v1_ && strcmp(interface, "zcr_keyboard_extension_v1") == 0) { connection->keyboard_extension_v1_ = wl::Bind<zcr_keyboard_extension_v1>( - registry, name, kMaxKeyboardExtensionVersion); + registry, name, std::min(version, kMaxKeyboardExtensionVersion)); if (!connection->keyboard_extension_v1_) { LOG(ERROR) << "Failed to bind zcr_keyboard_extension_v1"; return; @@ -447,12 +485,12 @@ void WaylandConnection::Global(void* data, } else if (!connection->xdg_decoration_manager_ && strcmp(interface, "zxdg_decoration_manager_v1") == 0) { connection->xdg_decoration_manager_ = - wl::Bind<struct zxdg_decoration_manager_v1>(registry, name, - kMaxXdgDecorationVersion); + wl::Bind<struct zxdg_decoration_manager_v1>( + registry, name, std::min(version, kMaxXdgDecorationVersion)); } else if (!connection->extended_drag_v1_ && strcmp(interface, "zcr_extended_drag_v1") == 0) { - connection->extended_drag_v1_ = - wl::Bind<zcr_extended_drag_v1>(registry, name, kMaxExtendedDragVersion); + connection->extended_drag_v1_ = wl::Bind<zcr_extended_drag_v1>( + registry, name, std::min(version, kMaxExtendedDragVersion)); if (!connection->extended_drag_v1_) { LOG(ERROR) << "Failed to bind to zcr_extended_drag_v1 global"; return; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h index b7f2981a074..7624abc23d6 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h @@ -16,6 +16,8 @@ #include "ui/ozone/platform/wayland/host/wayland_data_source.h" #include "ui/ozone/platform/wayland/host/wayland_window_manager.h" +struct wl_cursor; + namespace gfx { class Point; } @@ -24,6 +26,7 @@ namespace ui { class WaylandBufferManagerHost; class WaylandCursor; +class WaylandCursorBufferListener; class WaylandDrm; class WaylandEventSource; class WaylandKeyboard; @@ -65,6 +68,8 @@ class WaylandConnection { wl_display* display() const { return display_.get(); } wl_compositor* compositor() const { return compositor_.get(); } + // The server version of the compositor interface (might be higher than the + // version binded). uint32_t compositor_version() const { return compositor_version_; } wl_subcompositor* subcompositor() const { return subcompositor_.get(); } wp_viewporter* viewporter() const { return viewporter_.get(); } @@ -92,6 +97,10 @@ class WaylandConnection { uint32_t serial() const { return serial_.serial; } EventSerial event_serial() const { return serial_; } + void SetPlatformCursor(wl_cursor* cursor_data, int buffer_scale); + + void SetCursorBufferListener(WaylandCursorBufferListener* listener); + void SetCursorBitmap(const std::vector<SkBitmap>& bitmaps, const gfx::Point& hotspot_in_dips, int buffer_scale); @@ -250,6 +259,8 @@ class WaylandConnection { // Manages Wayland windows. WaylandWindowManager wayland_window_manager_; + WaylandCursorBufferListener* listener_ = nullptr; + bool scheduled_flush_ = false; EventSerial serial_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection_unittest.cc index cfce7dba945..4d6cabb6a2e 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_connection_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection_unittest.cc @@ -8,8 +8,10 @@ #include "base/run_loop.h" #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/ozone/platform/wayland/common/wayland.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_event_source.h" +#include "ui/ozone/platform/wayland/test/test_compositor.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" namespace ui { @@ -30,6 +32,9 @@ TEST(WaylandConnectionTest, Ping) { base::RunLoop().RunUntilIdle(); server.Pause(); + EXPECT_EQ(wl::TestCompositor::kVersion, + wl::get_version_of_object(connection.compositor())); + xdg_wm_base_send_ping(server.xdg_shell()->resource(), 1234); EXPECT_CALL(*server.xdg_shell(), Pong(1234)); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc index 44218ecc612..bd32821c243 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc @@ -4,6 +4,7 @@ #include "ui/ozone/platform/wayland/host/wayland_cursor.h" +#include <wayland-cursor.h> #include <memory> #include <vector> @@ -74,6 +75,30 @@ void WaylandCursor::UpdateBitmap(const std::vector<SkBitmap>& cursor_image, auto* address = buffer.get(); buffers_.emplace(address, std::move(buffer)); + + if (listener_) + listener_->OnCursorBufferAttached(nullptr); +} + +void WaylandCursor::SetPlatformShape(wl_cursor* cursor_data, + uint32_t serial, + int buffer_scale) { + if (!pointer_) + return; + + wl_cursor_image* cursor_image = cursor_data->images[0]; + wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(cursor_image); + + wl_pointer_set_cursor(pointer_->wl_object(), serial, pointer_surface_.get(), + cursor_image->hotspot_x, cursor_image->hotspot_y); + wl_surface_set_buffer_scale(pointer_surface_.get(), buffer_scale); + wl_surface_damage(pointer_surface_.get(), 0, 0, cursor_image->width, + cursor_image->height); + wl_surface_attach(pointer_surface_.get(), cursor_buffer, 0, 0); + wl_surface_commit(pointer_surface_.get()); + + if (listener_) + listener_->OnCursorBufferAttached(cursor_data); } void WaylandCursor::HideCursor(uint32_t serial) { @@ -84,6 +109,9 @@ void WaylandCursor::HideCursor(uint32_t serial) { wl_surface_commit(pointer_surface_.get()); connection_->ScheduleFlush(); + + if (listener_) + listener_->OnCursorBufferAttached(nullptr); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h index e60191eced4..16af372b4c0 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h @@ -15,6 +15,8 @@ class SkBitmap; +struct wl_cursor; + namespace gfx { class Point; } @@ -24,12 +26,23 @@ namespace ui { class WaylandConnection; class WaylandPointer; +// Interface through which WaylandCursor notifies the listener that it has +// attached another buffer to the pointer surface. The listener may free the +// previous buffer if it was holding it. +class WaylandCursorBufferListener { + public: + // Tells the listener that a new buffer is attached. |cursor_data| may be + // non-nullptr if the platform shape is used, or nullptr if the cursor has + // been hidden, or a custom bitmap has been set. + virtual void OnCursorBufferAttached(wl_cursor* cursor_data) = 0; + + protected: + virtual ~WaylandCursorBufferListener() = default; +}; + // Manages the actual visual representation (what users see drawn) of the // 'pointer' (which is the Wayland term for mouse/mice). // -// An instance of this class is aggregated by an instance of WaylandPointer -// and is exposed for updating the pointer bitmap with the single method call. -// // Encapsulates the low-level job such as surface and buffer management and // Wayland protocol calls. class WaylandCursor { @@ -48,6 +61,15 @@ class WaylandCursor { uint32_t serial, int buffer_scale); + // Takes data managed by the platform (without taking ownership). + void SetPlatformShape(wl_cursor* cursor_data, + uint32_t serial, + int buffer_scale); + + void set_listener(WaylandCursorBufferListener* listener) { + listener_ = listener; + } + private: // wl_buffer_listener: static void OnBufferRelease(void* data, wl_buffer* wl_buffer); @@ -57,6 +79,8 @@ class WaylandCursor { WaylandPointer* const pointer_; WaylandConnection* const connection_; + WaylandCursorBufferListener* listener_ = nullptr; + // Holds the buffers and their memory until the compositor releases them. base::flat_map<wl_buffer*, WaylandShmBuffer> buffers_; const wl::Object<wl_surface> pointer_surface_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc new file mode 100644 index 00000000000..5c0a72ebe2a --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc @@ -0,0 +1,156 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/wayland_cursor_factory.h" + +#include <wayland-cursor.h> + +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "base/task/thread_pool/thread_pool_instance.h" +#include "base/task_runner_util.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_shm.h" + +namespace ui { + +namespace { + +wl_cursor_theme* LoadCursorTheme(const std::string& name, + int size, + wl_shm* shm) { + // wl_cursor_theme_load() can return nullptr. We don't check that here but + // have to be cautious when we actually load the shape. + return wl_cursor_theme_load((name.empty() ? nullptr : name.c_str()), size, + shm); +} + +} // namespace + +WaylandCursorFactory::ThemeData::ThemeData() = default; + +WaylandCursorFactory::ThemeData::~ThemeData() = default; + +WaylandCursorFactory::WaylandCursorFactory(WaylandConnection* connection) + : connection_(connection) { + connection_->SetCursorBufferListener(this); + ReloadThemeCursors(); +} + +WaylandCursorFactory::~WaylandCursorFactory() = default; + +void WaylandCursorFactory::ObserveThemeChanges() { + auto* cursor_theme_manager = CursorThemeManager::GetInstance(); + DCHECK(cursor_theme_manager); + cursor_theme_observer_.Observe(cursor_theme_manager); +} + +base::Optional<PlatformCursor> WaylandCursorFactory::GetDefaultCursor( + mojom::CursorType type) { + if (type == mojom::CursorType::kNone) + return nullptr; // nullptr is used for the hidden cursor. + + if (current_theme_->cache.count(type) == 0) { + for (const std::string& name : CursorNamesFromType(type)) { + wl_cursor* cursor = GetCursorFromTheme(name); + if (!cursor) + continue; + + current_theme_->cache[type] = + base::MakeRefCounted<BitmapCursorOzone>(type, cursor); + break; + } + } + if (current_theme_->cache.count(type) == 0) + current_theme_->cache[type] = nullptr; + + // Fall back to the base class implementation if the theme has't provided + // a shape for the requested type. + if (current_theme_->cache[type].get() == nullptr) + return BitmapCursorFactoryOzone::GetDefaultCursor(type); + + return static_cast<PlatformCursor>(current_theme_->cache[type].get()); +} + +wl_cursor* WaylandCursorFactory::GetCursorFromTheme(const std::string& name) { + // Possible if the theme could not be loaded. + if (!current_theme_->theme) + return nullptr; + + return wl_cursor_theme_get_cursor(current_theme_->theme.get(), name.c_str()); +} + +void WaylandCursorFactory::OnCursorThemeNameChanged( + const std::string& cursor_theme_name) { + if (name_ == cursor_theme_name) + return; + + name_ = cursor_theme_name; + ReloadThemeCursors(); +} + +void WaylandCursorFactory::OnCursorThemeSizeChanged(int cursor_theme_size) { + if (size_ == cursor_theme_size) + return; + + size_ = cursor_theme_size; + ReloadThemeCursors(); +} + +void WaylandCursorFactory::OnCursorBufferAttached(wl_cursor* cursor_data) { + if (!unloaded_theme_) + return; + if (!cursor_data) { + unloaded_theme_.reset(); + return; + } + for (auto& item : current_theme_->cache) { + if (item.second->platform_data() == cursor_data) { + // The cursor that has been just attached is from the current theme. That + // means that the theme that has been unloaded earlier can now be deleted. + unloaded_theme_.reset(); + return; + } + } +} + +void WaylandCursorFactory::ReloadThemeCursors() { + // If we use any cursor when the theme is reloaded, the one can be only from + // the theme that is currently used. As soon as we take the next cursor from + // the next theme, we will destroy it (see OnCursorBufferAttached() above). + // If more than one theme has been changed but we didn't take any cursors from + // them (which is possible if the user played with settings but didn't switch + // into Chromium), we don't need to track them all. + if (!unloaded_theme_ && current_theme_ && current_theme_->cache.size() > 0) + unloaded_theme_ = std::move(current_theme_); + + current_theme_ = std::make_unique<ThemeData>(); + + // The task environment is normally not created in tests. As this factory is + // part of the platform that is created always and early, posting a task to + // the pool would fail in many many tests. + if (!base::ThreadPoolInstance::Get()) + return; + + base::PostTaskAndReplyWithResult( + FROM_HERE, + {base::ThreadPool(), base::MayBlock(), + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, + base::BindOnce(LoadCursorTheme, name_, size_, connection_->shm()->get()), + base::BindOnce(&WaylandCursorFactory::OnThemeLoaded, + weak_factory_.GetWeakPtr(), name_, size_)); +} + +void WaylandCursorFactory::OnThemeLoaded(const std::string& loaded_theme_name, + int loaded_theme_size, + wl_cursor_theme* loaded_theme) { + if (loaded_theme_name == name_ && loaded_theme_size == size_) { + // wl_cursor_theme_load() can return nullptr. We don't check that here but + // have to be cautious when we actually load the shape. + current_theme_->theme.reset(loaded_theme); + } +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.h b/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.h new file mode 100644 index 00000000000..0be03bc1b43 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory.h @@ -0,0 +1,90 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CURSOR_FACTORY_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CURSOR_FACTORY_H_ + +#include <string> + +#include "base/containers/flat_map.h" +#include "base/memory/scoped_refptr.h" +#include "base/optional.h" +#include "base/scoped_observation.h" +#include "ui/base/cursor/cursor_theme_manager.h" +#include "ui/base/cursor/cursor_theme_manager_observer.h" +#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" +#include "ui/ozone/platform/wayland/host/wayland_cursor.h" + +struct wl_cursor_theme; + +namespace ui { + +class WaylandConnection; + +// CursorFactory implementation for Wayland. +class WaylandCursorFactory : public BitmapCursorFactoryOzone, + public CursorThemeManagerObserver, + public WaylandCursorBufferListener { + public: + explicit WaylandCursorFactory(WaylandConnection* connection); + WaylandCursorFactory(const WaylandCursorFactory&) = delete; + WaylandCursorFactory& operator=(const WaylandCursorFactory&) = delete; + ~WaylandCursorFactory() override; + + // CursorFactory: + void ObserveThemeChanges() override; + + // CursorFactoryOzone: + base::Optional<PlatformCursor> GetDefaultCursor( + mojom::CursorType type) override; + + protected: + // Returns the actual wl_cursor record from the currently loaded theme. + // Virtual for tests where themes can be empty. + virtual wl_cursor* GetCursorFromTheme(const std::string& name); + + private: + FRIEND_TEST_ALL_PREFIXES(WaylandCursorFactoryTest, + RetainOldThemeUntilNewBufferIsAttached); + + struct ThemeData { + ThemeData(); + ~ThemeData(); + wl::Object<wl_cursor_theme> theme; + base::flat_map<mojom::CursorType, scoped_refptr<BitmapCursorOzone>> cache; + }; + + // CusorThemeManagerObserver: + void OnCursorThemeNameChanged(const std::string& cursor_theme_name) override; + void OnCursorThemeSizeChanged(int cursor_theme_size) override; + + // WaylandCursorBufferListener: + void OnCursorBufferAttached(wl_cursor* cursor_data) override; + + void ReloadThemeCursors(); + void OnThemeLoaded(const std::string& loaded_theme_name, + int loaded_theme_size, + wl_cursor_theme* loaded_theme); + + WaylandConnection* const connection_; + + base::ScopedObservation<CursorThemeManager, CursorThemeManagerObserver> + cursor_theme_observer_{this}; + + // Name of the current theme. + std::string name_; + // Current size of cursors + int size_ = 24; + + std::unique_ptr<ThemeData> current_theme_; + // Holds the reference on the unloaded theme until the cursor is released. + std::unique_ptr<ThemeData> unloaded_theme_; + + base::WeakPtrFactory<WaylandCursorFactory> weak_factory_{this}; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CURSOR_FACTORY_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc new file mode 100644 index 00000000000..1ea0e04b6cc --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc @@ -0,0 +1,134 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/wayland_cursor_factory.h" + +#include <wayland-cursor.h> + +#include "base/containers/flat_map.h" +#include "base/gtest_prod_util.h" +#include "ui/ozone/platform/wayland/test/wayland_test.h" + +namespace ui { + +namespace { + +// Overrides WaylandCursorFactory::GetCursorFromTheme() to pretend that cursors +// are really loaded. +class DryRunningWaylandCursorFactory : public WaylandCursorFactory { + public: + explicit DryRunningWaylandCursorFactory(WaylandConnection* connection) + : WaylandCursorFactory(connection) {} + DryRunningWaylandCursorFactory(const DryRunningWaylandCursorFactory&) = + delete; + DryRunningWaylandCursorFactory& operator=( + const DryRunningWaylandCursorFactory&) = delete; + ~DryRunningWaylandCursorFactory() override = default; + + protected: + // Pretends to load a cursor by creating an empty wl_cursor. + wl_cursor* GetCursorFromTheme(const std::string& name) override { + if (cursors_.count(name) == 0) { + cursors_[name] = std::make_unique<wl_cursor>(); + cursors_[name]->image_count = 0; + cursors_[name]->images = nullptr; + cursors_[name]->name = nullptr; + } + return cursors_[name].get(); + } + + private: + base::flat_map<std::string, std::unique_ptr<wl_cursor>> cursors_; +}; + +} // namespace + +class WaylandCursorFactoryTest : public WaylandTest { + public: + WaylandCursorFactoryTest() = default; + + void SetUp() override { + WaylandTest::SetUp(); + + cursor_factory_ = + std::make_unique<DryRunningWaylandCursorFactory>(connection_.get()); + } + + protected: + std::unique_ptr<WaylandCursorFactory> cursor_factory_; +}; + +// Tests that the factory holds the cursor theme until a buffer taken from it +// released. +TEST_P(WaylandCursorFactoryTest, RetainOldThemeUntilNewBufferIsAttached) { + // The default theme should be loaded right away. The unloaded theme should + // not be set. + EXPECT_NE(cursor_factory_->current_theme_, nullptr); + EXPECT_EQ(cursor_factory_->unloaded_theme_, nullptr); + + // Trigger theme reload and ensure that the theme instance has changed. + // As we didn't request any buffers, the unloaded theme should not be held. + { + auto* const current_theme = cursor_factory_->current_theme_.get(); + cursor_factory_->OnCursorThemeNameChanged("Theme1"); + EXPECT_NE(cursor_factory_->current_theme_, nullptr); + EXPECT_NE(cursor_factory_->current_theme_.get(), current_theme); + EXPECT_EQ(cursor_factory_->unloaded_theme_, nullptr); + } + + // Now request some buffer, and while "holding" it (i.e., not notifying the + // factory about attaching any other buffer), reload the theme a couple times. + // This time the unloaded theme should be set and survive these reloads. + // In the end, tell the factory that we have attached a buffer belonging to + // the cursor from the "unloaded" theme. This must not trigger unloading of + // that theme. + { + auto* const current_theme = cursor_factory_->current_theme_.get(); + auto const cursor = + cursor_factory_->GetDefaultCursor(mojom::CursorType::kPointer); + EXPECT_NE(cursor, nullptr); + EXPECT_GT(cursor_factory_->current_theme_->cache.size(), 0U); + + cursor_factory_->OnCursorThemeNameChanged("Theme2"); + + EXPECT_EQ(cursor_factory_->current_theme_->cache.size(), 0U); + EXPECT_NE(cursor_factory_->current_theme_, nullptr); + EXPECT_NE(cursor_factory_->current_theme_.get(), current_theme); + EXPECT_EQ(cursor_factory_->unloaded_theme_.get(), current_theme); + + cursor_factory_->OnCursorThemeNameChanged("Theme3"); + + EXPECT_EQ(cursor_factory_->current_theme_->cache.size(), 0U); + EXPECT_NE(cursor_factory_->current_theme_, nullptr); + EXPECT_NE(cursor_factory_->current_theme_.get(), current_theme); + EXPECT_EQ(cursor_factory_->unloaded_theme_.get(), current_theme); + + cursor_factory_->OnCursorBufferAttached(reinterpret_cast<wl_cursor*>( + reinterpret_cast<BitmapCursorOzone*>(*cursor)->platform_data())); + EXPECT_EQ(cursor_factory_->unloaded_theme_.get(), current_theme); + } + + // Finally, tell the factory that we have attached a buffer from the current + // theme. This time the old theme held since a while ago should be freed. + { + auto const cursor = + cursor_factory_->GetDefaultCursor(mojom::CursorType::kPointer); + EXPECT_NE(cursor, nullptr); + + cursor_factory_->OnCursorBufferAttached(reinterpret_cast<wl_cursor*>( + reinterpret_cast<BitmapCursorOzone*>(*cursor)->platform_data())); + + EXPECT_EQ(cursor_factory_->unloaded_theme_.get(), nullptr); + } +} + +INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, + WaylandCursorFactoryTest, + ::testing::Values(kXdgShellStable)); + +INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, + WaylandCursorFactoryTest, + ::testing::Values(kXdgShellV6)); + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc index 8f4b62fa68e..01f32113363 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/files/scoped_file.h" -#include "ui/base/clipboard/clipboard_buffer.h" #include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/common/data_util.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" @@ -94,10 +93,7 @@ void WaylandDataDevice::OnOffer(void* data, wl_data_device* data_device, wl_data_offer* offer) { auto* self = static_cast<WaylandDataDevice*>(data); - - self->connection()->clipboard()->UpdateSequenceNumber( - ClipboardBuffer::kCopyPaste); - + DCHECK(self); DCHECK(!self->new_offer_); self->new_offer_ = std::make_unique<WaylandDataOffer>(offer); } @@ -163,7 +159,7 @@ void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) { // potential use-after-free. Above call to OnDragLeave() may result in // |drag_delegate_| being reset, so it must be checked here as well. if (self->drag_delegate_ && !self->drag_delegate_->IsDragSource()) - self->drag_delegate_ = nullptr; + self->ResetDragDelegate(); } void WaylandDataDevice::OnSelection(void* data, @@ -173,19 +169,17 @@ void WaylandDataDevice::OnSelection(void* data, DCHECK(self); // 'offer' will be null to indicate that the selection is no longer valid, - // i.e. there is no longer clipboard data available to paste. + // i.e. there is no longer selection data available to fetch. if (!offer) { self->ResetDataOffer(); - - // Clear Clipboard cache. - self->connection()->clipboard()->SetData({}, {}); - return; + } else { + DCHECK(self->new_offer_); + self->set_data_offer(std::move(self->new_offer_)); + self->data_offer()->EnsureTextMimeTypeIfNeeded(); } - DCHECK(self->new_offer_); - self->set_data_offer(std::move(self->new_offer_)); - - self->data_offer()->EnsureTextMimeTypeIfNeeded(); + if (self->selection_delegate()) + self->selection_delegate()->OnSelectionOffer(self->data_offer()); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h index f43ba2e8dfa..b13551eaf66 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h @@ -74,6 +74,15 @@ class WaylandDataDevice : public WaylandDataDeviceBase { // Returns the underlying wl_data_device singleton object. wl_data_device* data_device() const { return data_device_.get(); } + // wl_data_device::set_selection makes the corresponding wl_data_source the + // target of future wl_data_device::data_offer events. In non-Wayland terms, + // this is equivalent to "writing" to the clipboard or DnD, although the + // actual transfer of data happens asynchronously, on-demand-only. + // + // The API relies on the assumption that the Wayland client is responding to a + // keyboard or mouse event with a serial number. This is cached in + // WaylandConnection. However, this may not exist or be set properly in tests, + // resulting in the Wayland server ignoring the set_selection() request. void SetSelectionSource(WaylandDataSource* source); private: diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc index 7df5d9ec709..50be3ad35bc 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc @@ -56,10 +56,10 @@ void WaylandDataDeviceBase::ReadClipboardDataFromFD( const std::string& mime_type) { std::vector<uint8_t> contents; wl::ReadDataFromFD(std::move(fd), &contents); - connection_->clipboard()->SetData( - scoped_refptr<base::RefCountedBytes>( - base::RefCountedBytes::TakeVector(&contents)), - mime_type); + if (!selection_delegate_) + return; + selection_delegate_->OnSelectionDataReceived( + mime_type, base::RefCountedBytes::TakeVector(&contents)); } void WaylandDataDeviceBase::RegisterDeferredReadCallback() { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h index e331243fc5e..1775790bef2 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h @@ -12,6 +12,7 @@ #include "base/macros.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/wayland_data_offer_base.h" +#include "ui/ozone/public/platform_clipboard.h" namespace ui { @@ -20,9 +21,25 @@ class WaylandConnection; // Implements high level (protocol-agnostic) interface to a Wayland data device. class WaylandDataDeviceBase { public: + class SelectionDelegate { + public: + virtual void OnSelectionOffer(WaylandDataOfferBase* offer) = 0; + virtual void OnSelectionDataReceived(const std::string& mime_type, + PlatformClipboard::Data contents) = 0; + + protected: + virtual ~SelectionDelegate() = default; + }; + explicit WaylandDataDeviceBase(WaylandConnection* connection); virtual ~WaylandDataDeviceBase(); + // Sets the delegate instance responsible for handling section events. + void set_selection_delegate(SelectionDelegate* selection_delegate) { + DCHECK(!selection_delegate_ || !selection_delegate); + selection_delegate_ = selection_delegate; + } + // Returns MIME types given by the current data offer. const std::vector<std::string>& GetAvailableMimeTypes() const; @@ -50,6 +67,8 @@ class WaylandDataDeviceBase { void RegisterDeferredReadClosure(base::OnceClosure closure); + SelectionDelegate* selection_delegate() { return selection_delegate_; } + private: // wl_callback_listener callback static void DeferredReadCallback(void* data, @@ -58,9 +77,11 @@ class WaylandDataDeviceBase { void DeferredReadCallbackInternal(struct wl_callback* cb, uint32_t time); - // Used to call out to WaylandConnection once clipboard data - // has been successfully read. - WaylandConnection* const connection_ = nullptr; + SelectionDelegate* selection_delegate_ = nullptr; + + // Used to call out to WaylandConnection once clipboard data has been + // successfully read. + WaylandConnection* const connection_; // Offer that holds the most-recent clipboard selection, or null if no // clipboard data is available. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc deleted file mode 100644 index 855982cf567..00000000000 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <wayland-server.h> - -#include <memory> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/containers/flat_set.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/clipboard/clipboard_constants.h" -#include "ui/events/base_event_utils.h" -#include "ui/ozone/platform/wayland/test/mock_surface.h" -#include "ui/ozone/platform/wayland/test/test_data_device.h" -#include "ui/ozone/platform/wayland/test/test_data_device_manager.h" -#include "ui/ozone/platform/wayland/test/test_data_offer.h" -#include "ui/ozone/platform/wayland/test/test_data_source.h" -#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" -#include "ui/ozone/platform/wayland/test/wayland_test.h" -#include "ui/ozone/public/platform_clipboard.h" - -using testing::_; -using testing::Mock; - -namespace ui { - -namespace { - -constexpr char kSampleClipboardText[] = "This is a sample text for clipboard."; - -template <typename StringType> -ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) { - std::vector<uint8_t> data_vector; - data_vector.assign(data_string.begin(), data_string.end()); - return scoped_refptr<base::RefCountedBytes>( - base::RefCountedBytes::TakeVector(&data_vector)); -} - -} // namespace - -// This class mocks how a real clipboard/ozone client would -// hook to PlatformClipboard, with one difference: real clients -// have no access to the WaylandConnection instance like this -// MockClipboardClient impl does. Instead, clients and ozone gets -// plumbbed up by calling the appropriated Ozone API, -// OzonePlatform::GetPlatformClipboard. -class MockClipboardClient { - public: - explicit MockClipboardClient(WaylandConnection* connection) { - DCHECK(connection); - // See comment above for reasoning to access the WaylandConnection - // directly from here. - delegate_ = connection->clipboard(); - - DCHECK(delegate_); - } - ~MockClipboardClient() = default; - - // Fill the clipboard backing store with sample data. - void SetData(PlatformClipboard::Data data, - const std::string& mime_type, - PlatformClipboard::OfferDataClosure callback) { - data_types_[mime_type] = data; - delegate_->OfferClipboardData(ClipboardBuffer::kCopyPaste, data_types_, - std::move(callback)); - } - - void ReadData(const std::string& mime_type, - PlatformClipboard::RequestDataClosure callback) { - delegate_->RequestClipboardData(ClipboardBuffer::kCopyPaste, mime_type, - &data_types_, std::move(callback)); - } - - bool IsSelectionOwner() { - return delegate_->IsSelectionOwner(ClipboardBuffer::kCopyPaste); - } - - private: - PlatformClipboard* delegate_ = nullptr; - PlatformClipboard::DataMap data_types_; - - DISALLOW_COPY_AND_ASSIGN(MockClipboardClient); -}; - -class WaylandDataDeviceManagerTest : public WaylandTest { - public: - WaylandDataDeviceManagerTest() {} - - void SetUp() override { - WaylandTest::SetUp(); - - Sync(); - - data_device_manager_ = server_.data_device_manager(); - DCHECK(data_device_manager_); - - clipboard_client_ = - std::make_unique<MockClipboardClient>(connection_.get()); - } - - protected: - wl::TestDataDeviceManager* data_device_manager_; - std::unique_ptr<MockClipboardClient> clipboard_client_; - - private: - DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManagerTest); -}; - -TEST_P(WaylandDataDeviceManagerTest, WriteToClipboard) { - // The client writes data to the clipboard ... - std::vector<uint8_t> data_vector( - kSampleClipboardText, - kSampleClipboardText + strlen(kSampleClipboardText)); - clipboard_client_->SetData( - scoped_refptr<base::RefCountedBytes>( - base::RefCountedBytes::TakeVector(&data_vector)), - {kMimeTypeTextUtf8}, base::BindOnce([]() {})); - Sync(); - - // ... and the server reads it. - base::RunLoop run_loop; - auto callback = base::BindOnce( - [](base::RunLoop* loop, std::vector<uint8_t>&& data) { - std::string string_data(data.begin(), data.end()); - EXPECT_EQ(kSampleClipboardText, string_data); - loop->Quit(); - }, - &run_loop); - data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8, - std::move(callback)); - run_loop.Run(); -} - -TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboard) { - // TODO(nickdiego): implement this in terms of an actual wl_surface that - // gets focused and compositor sends data_device data to it. - auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer(kMimeTypeTextUtf8, - ToClipboardData(std::string(kSampleClipboardText))); - data_device_manager_->data_device()->OnSelection(data_offer); - Sync(); - - // The client requests to reading clipboard data from the server. - // The Server writes in some sample data, and we check it matches - // expectation. - auto callback = - base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { - auto& bytes = data->get()->data(); - std::string string_data = std::string(bytes.begin(), bytes.end()); - EXPECT_EQ(kSampleClipboardText, string_data); - }); - clipboard_client_->ReadData(kMimeTypeTextUtf8, std::move(callback)); - Sync(); -} - -TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboardWithoutOffer) { - // When no data offer is advertised and client requests clipboard data - // from the server, the response callback should be gracefully called with - // an empty string. - auto callback = - base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { - auto& bytes = data->get()->data(); - std::string string_data = std::string(bytes.begin(), bytes.end()); - EXPECT_EQ("", string_data); - }); - clipboard_client_->ReadData(kMimeTypeTextUtf8, std::move(callback)); -} - -TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) { - auto callback = base::BindOnce([]() {}); - std::vector<uint8_t> data_vector( - kSampleClipboardText, - kSampleClipboardText + strlen(kSampleClipboardText)); - clipboard_client_->SetData( - scoped_refptr<base::RefCountedBytes>( - base::RefCountedBytes::TakeVector(&data_vector)), - {kMimeTypeTextUtf8}, std::move(callback)); - Sync(); - ASSERT_TRUE(clipboard_client_->IsSelectionOwner()); - - // The compositor sends OnCancelled whenever another application - // on the system sets a new selection. It means we are not the application - // that owns the current selection data. - data_device_manager_->data_source()->OnCancelled(); - Sync(); - - ASSERT_FALSE(clipboard_client_->IsSelectionOwner()); -} - -INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, - WaylandDataDeviceManagerTest, - ::testing::Values(kXdgShellStable)); - -INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test, - WaylandDataDeviceManagerTest, - ::testing::Values(kXdgShellV6)); - -} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc index 736c40d3cbe..d81ce6155af 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc @@ -7,13 +7,13 @@ #include <cstdint> #include "base/check.h" +#include "base/metrics/histogram_macros.h" #include "base/notreached.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_constants.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" -#include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/common/data_util.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" @@ -28,31 +28,22 @@ namespace ui { namespace { -// Returns actions possible with the given source and drag'n'drop actions. -// Also converts enums: input params are wl_data_device_manager_dnd_action but -// the result is ui::DragDropTypes. -int GetPossibleActions(uint32_t source_actions, uint32_t dnd_action) { - // If drag'n'drop action is set, use it but check for ASK action (see below). - uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE - ? dnd_action - : source_actions; - - // We accept any action except ASK (see below). - int operation = DragDropTypes::DRAG_NONE; - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) - operation |= DragDropTypes::DRAG_COPY; - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) - operation |= DragDropTypes::DRAG_MOVE; - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { - // This is very rare and non-standard. Chromium doesn't set this when - // anything is dragged from it, neither it provides any UI for asking - // the user about the desired drag'n'drop action when data is dragged - // from an external source. - // We are safe with not adding anything here. However, keep NOTIMPLEMENTED - // for an (unlikely) event of this being hit in distant future. - NOTIMPLEMENTED_LOG_ONCE(); - } - return operation; +int DndActionsToDragOperations(uint32_t actions) { + int operations = DragDropTypes::DRAG_NONE; + if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + operations |= DragDropTypes::DRAG_COPY; + if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + operations |= DragDropTypes::DRAG_MOVE; + return operations; +} + +uint32_t DragOperationsToDndActions(int operations) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (operations & DragDropTypes::DRAG_COPY) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (operations & DragDropTypes::DRAG_MOVE) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + return dnd_actions; } const SkBitmap* GetDragImage(const OSExchangeData& data) { @@ -94,21 +85,27 @@ void WaylandDataDragController::StartSession(const OSExchangeData& data, Offer(data, operation); // Create drag icon surface (if any) and store the data to be exchanged. - CreateIconSurfaceIfNeeded(data); + icon_bitmap_ = GetDragImage(data); + if (icon_bitmap_) { + icon_surface_ = connection_->CreateSurface(); + wl_surface_set_buffer_scale(icon_surface_.get(), + origin_window_->buffer_scale()); + } data_ = std::make_unique<OSExchangeData>(data.provider().Clone()); // Starts the wayland drag session setting |this| object as delegate. state_ = State::kStarted; data_device_->StartDrag(*data_source_, *origin_window_, icon_surface_.get(), this); + + window_manager_->AddObserver(this); } -// Sessions initiated from Chromium, will have |origin_window_| pointing to the -// window where the drag started in. In such cases, |data_| is expected to be -// non-null, which can be used to save some IO cycles. +// Sessions initiated from Chromium, will have |data_source_| set. In which +// case, |data_| is expected to be non-null as well. bool WaylandDataDragController::IsDragSource() const { - DCHECK(!origin_window_ || data_); - return !!origin_window_; + DCHECK(!data_source_ || data_); + return !!data_source_; } void WaylandDataDragController::DrawIcon() { @@ -125,7 +122,6 @@ void WaylandDataDragController::DrawIcon() { return; } } - // TODO(crbug.com/1085418): Fix drag icon scaling wl::DrawBitmap(*icon_bitmap_, shm_buffer_.get()); wl_surface_attach(icon_surface_.get(), shm_buffer_->get(), 0, 0); wl_surface_damage(icon_surface_.get(), 0, 0, size.width(), size.height()); @@ -142,6 +138,7 @@ void WaylandDataDragController::OnDragEnter(WaylandWindow* window, const gfx::PointF& location, uint32_t serial) { DCHECK(window); + DCHECK(data_offer_); window_ = window; // TODO(crbug.com/1004715): Set mime type the client can accept. Now it sets @@ -153,39 +150,53 @@ void WaylandDataDragController::OnDragEnter(WaylandWindow* window, data_offer_->Accept(serial, mime); } - std::unique_ptr<OSExchangeData> dragged_data; - // If the DND session was initiated from a Chromium window, |data_| already - // holds the data to be exchanged, so no needed to read it through Wayland, - // thus just copy it here. - if (IsDragSource()) - dragged_data = std::make_unique<OSExchangeData>(data_->provider().Clone()); - window_->OnDragEnter(location, std::move(dragged_data), - GetPossibleActions(data_offer_->source_actions(), - data_offer_->dnd_action())); + if (IsDragSource()) { + // If the DND session was initiated from a Chromium window, |data_| already + // holds the data to be exchanged, so we don't need to read it through + // Wayland and can just copy it here. + DCHECK(data_); + PropagateOnDragEnter( + location, std::make_unique<OSExchangeData>(data_->provider().Clone())); + } else { + // Otherwise, we are about to accept data dragged from another application. + // Reading the data may take some time so set |state_| to |kTrasferring|, + // which will defer sending OnDragEnter to the client until the data + // is ready. + state_ = State::kTransferring; + received_data_ = std::make_unique<OSExchangeData>( + std::make_unique<OSExchangeDataProviderNonBacked>()); + last_drag_location_ = location; + HandleUnprocessedMimeTypes(base::TimeTicks::Now()); + } } void WaylandDataDragController::OnDragMotion(const gfx::PointF& location) { if (!window_) return; - int client_operation = window_->OnDragMotion( - location, GetPossibleActions(data_offer_->source_actions(), - data_offer_->dnd_action())); - SetOperation(client_operation); + if (state_ == State::kTransferring) { + last_drag_location_ = location; + return; + } + + DCHECK(data_offer_); + int available_operations = + DndActionsToDragOperations(data_offer_->source_actions()); + int client_operations = window_->OnDragMotion(location, available_operations); + + data_offer_->SetActions(DragOperationsToDndActions(client_operations)); } void WaylandDataDragController::OnDragLeave() { - if (!window_) - return; - - // Leave event can arrive while data is being transferred. As it cannot be - // handled right away, just mark it to be processed when the data is ready. if (state_ == State::kTransferring) { + // We cannot leave until the transfer is finished. Postponing. is_leave_pending_ = true; return; } - window_->OnDragLeave(); + if (window_) + window_->OnDragLeave(); + window_ = nullptr; data_offer_.reset(); is_leave_pending_ = false; @@ -195,40 +206,32 @@ void WaylandDataDragController::OnDragDrop() { if (!window_) return; - if (IsDragSource()) { - // This means the data is being exchanged between Chromium windows. In this - // case, data is supposed to have already been sent to the drop handler - // before (see OnDragEnter()), expecting to receive null at this stage. - OnDataTransferFinished(nullptr); - return; - } + window_->OnDragDrop(); - // Otherwise, we are about to accept data dragged from another application. - // Reading the data may take some time so set |state_| to |kTrasfering|, which - // will make final "leave" event handling to be postponed until data is ready. - state_ = State::kTransferring; - received_data_ = std::make_unique<OSExchangeData>( - std::make_unique<OSExchangeDataProviderNonBacked>()); - HandleUnprocessedMimeTypes(); + // Offer must be finished and destroyed here as some compositors may delay to + // send wl_data_source::finished|cancelled until owning client destroys the + // drag offer. e.g: Exosphere. + data_offer_->FinishOffer(); + data_offer_.reset(); } void WaylandDataDragController::OnDataSourceFinish(bool completed) { DCHECK(data_source_); - DCHECK(origin_window_); - - origin_window_->OnDragSessionClose(data_source_->dnd_action()); - // DnD handlers expect DragLeave to be sent for drag sessions that end up - // with no data transfer (wl_data_source::cancelled event). - if (!completed) - origin_window_->OnDragLeave(); + if (origin_window_) { + origin_window_->OnDragSessionClose(data_source_->dnd_action()); + // DnD handlers expect DragLeave to be sent for drag sessions that end up + // with no data transfer (wl_data_source::cancelled event). + if (!completed) + origin_window_->OnDragLeave(); + origin_window_ = nullptr; + } - origin_window_ = nullptr; + window_manager_->RemoveObserver(this); data_source_.reset(); data_offer_.reset(); data_.reset(); data_device_->ResetDragDelegate(); - state_ = State::kIdle; } @@ -243,6 +246,14 @@ void WaylandDataDragController::OnDataSourceSend(const std::string& mime_type, } } +void WaylandDataDragController::OnWindowRemoved(WaylandWindow* window) { + if (window == window_) + window_ = nullptr; + + if (window == origin_window_) + origin_window_ = nullptr; +} + void WaylandDataDragController::Offer(const OSExchangeData& data, int operation) { DCHECK(data_source_); @@ -271,36 +282,29 @@ void WaylandDataDragController::Offer(const OSExchangeData& data, data_source_->SetAction(operation); } -void WaylandDataDragController::CreateIconSurfaceIfNeeded( - const OSExchangeData& data) { - icon_bitmap_ = GetDragImage(data); - if (icon_bitmap_) - icon_surface_ = connection_->CreateSurface(); -} - // Asynchronously requests and reads data for every negotiated/supported mime // type, one after another, OnMimeTypeDataTransferred calls back into this // function once it finishes reading data for each mime type, until there is no // more unprocessed mime types on the |unprocessed_mime_types_| queue. Once this // process is finished, OnDataTransferFinished is called to deliver the // |received_data_| to the drop handler. -void WaylandDataDragController::HandleUnprocessedMimeTypes() { - DCHECK_EQ(state_, State::kTransferring); +void WaylandDataDragController::HandleUnprocessedMimeTypes( + base::TimeTicks start_time) { std::string mime_type = GetNextUnprocessedMimeType(); - if (mime_type.empty()) { - OnDataTransferFinished(std::move(received_data_)); + if (mime_type.empty() || is_leave_pending_ || state_ == State::kIdle) { + OnDataTransferFinished(start_time, std::move(received_data_)); } else { DCHECK(data_offer_); data_device_->RequestData( data_offer_.get(), mime_type, base::BindOnce(&WaylandDataDragController::OnMimeTypeDataTransferred, - weak_factory_.GetWeakPtr())); + weak_factory_.GetWeakPtr(), start_time)); } } void WaylandDataDragController::OnMimeTypeDataTransferred( + base::TimeTicks start_time, PlatformClipboard::Data contents) { - DCHECK_EQ(state_, State::kTransferring); DCHECK(contents); if (!contents->data().empty()) { std::string mime_type = unprocessed_mime_types_.front(); @@ -309,21 +313,37 @@ void WaylandDataDragController::OnMimeTypeDataTransferred( unprocessed_mime_types_.pop_front(); // Continue reading data for other negotiated mime types. - HandleUnprocessedMimeTypes(); + HandleUnprocessedMimeTypes(start_time); } void WaylandDataDragController::OnDataTransferFinished( + base::TimeTicks start_time, std::unique_ptr<OSExchangeData> received_data) { - data_offer_->FinishOffer(); - window_->OnDragDrop(std::move(received_data)); - unprocessed_mime_types_.clear(); + if (state_ == State::kIdle) + return; + state_ = State::kIdle; // If |is_leave_pending_| is set, it means a 'leave' event was fired while - // data was on transit, so process it here (See OnDragLeave for more context). - if (is_leave_pending_) - OnDragLeave(); + // data was on transit (see OnDragLeave for more context). Sending + // OnDragEnter to the window makes no sense anymore because the drag is no + // longer over it. Reset and exit. + if (is_leave_pending_) { + if (data_offer_) { + data_offer_->FinishOffer(); + data_offer_.reset(); + } + data_.reset(); + data_device_->ResetDragDelegate(); + is_leave_pending_ = false; + return; + } + + UMA_HISTOGRAM_TIMES("Event.WaylandDragDrop.IncomingDataTransferTime", + base::TimeTicks::Now() - start_time); + + PropagateOnDragEnter(last_drag_location_, std::move(received_data)); } // Returns the next MIME type to be received from the source process, or an @@ -342,21 +362,15 @@ std::string WaylandDataDragController::GetNextUnprocessedMimeType() { return {}; } -void WaylandDataDragController::SetOperation(const int operation) { - uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - - if (operation & DragDropTypes::DRAG_COPY) { - dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - } +void WaylandDataDragController::PropagateOnDragEnter( + const gfx::PointF& location, + std::unique_ptr<OSExchangeData> data) { + DCHECK(window_); - if (operation & DragDropTypes::DRAG_MOVE) { - dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) - preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - } - data_offer_->SetAction(dnd_actions, preferred_action); + window_->OnDragEnter( + location, std::move(data), + DndActionsToDragOperations(data_offer_->source_actions())); + OnDragMotion(location); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h index 5e4c15dd08b..e4a7efbe2ab 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h @@ -11,9 +11,11 @@ #include "base/gtest_prod_util.h" #include "base/memory/weak_ptr.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/platform/wayland/host/wayland_window_observer.h" struct wl_surface; class SkBitmap; @@ -28,15 +30,41 @@ class WaylandWindow; class WaylandWindowManager; class WaylandShmBuffer; -// WaylandDataDragController implements regular data exchanging between Chromium -// and other client applications on top of the Wayland Drag and Drop protocol. -// By implementing both DataDevice::DragDelegate and DataSource::Delegate, -// it is responsible for handling both DND sessions initiated from Chromium -// windows as well as those triggered by other clients. +// WaylandDataDragController implements regular data exchange on top of the +// Wayland Drag and Drop protocol. The data can be dragged within the Chromium +// window, or between Chromium and other application in both directions. +// +// The outgoing drag starts via the StartSession() method. For more context, +// see WaylandTopLevelWindow::StartDrag(). +// +// The incoming drag starts with the call to OnDragEnter() from the Wayland side +// (the data device), and ends up in call to WaylandWindow::OnDragEnter(), but +// two ways of coming there are possible: +// +// 1. The drag has been initiated by a Chromium window. In this case, the data +// that is being dragged is available right away, and therefore the controller +// can forward the data to the window immediately. +// +// 2. The data is being dragged from another application. Before notifying the +// window, the controller requests the data from the source side, which results +// in a number of requests to Wayland and data transfers from it. Only after +// data records of all supported MIME types have been received, the window will +// be notified. +// +// It is possible that further drag events come while the data is still being +// transferred. The drag motion event is ignored; the window will first receive +// OnDragEnter, and any OnDragMotion that comes after that. The drag leave +// event stops the transfer and cancels the operation; the window will not +// receive anything at all. class WaylandDataDragController : public WaylandDataDevice::DragDelegate, - public WaylandDataSource::Delegate { + public WaylandDataSource::Delegate, + public WaylandWindowObserver { public: - enum class State { kIdle, kStarted, kTransferring }; + enum class State { + kIdle, // Doing nothing special + kStarted, // The outgoing drag is in progress. + kTransferring, // The incoming data is transferred from the source. + }; WaylandDataDragController(WaylandConnection* connection, WaylandDataDeviceManager* data_device_manager); @@ -77,14 +105,21 @@ class WaylandDataDragController : public WaylandDataDevice::DragDelegate, void OnDataSourceSend(const std::string& mime_type, std::string* contents) override; + // WaylandWindowObserver: + void OnWindowRemoved(WaylandWindow* window) override; + void Offer(const OSExchangeData& data, int operation); - void CreateIconSurfaceIfNeeded(const OSExchangeData& data); - void HandleUnprocessedMimeTypes(); - void OnMimeTypeDataTransferred(PlatformClipboard::Data contents); + void HandleUnprocessedMimeTypes(base::TimeTicks start_time); + void OnMimeTypeDataTransferred(base::TimeTicks start_time, + PlatformClipboard::Data contents); void OnDataTransferFinished( + base::TimeTicks start_time, std::unique_ptr<ui::OSExchangeData> received_data); std::string GetNextUnprocessedMimeType(); - void SetOperation(const int operation); + // Calls the window's OnDragEnter with the given location and data, + // then immediately calls OnDragMotion to get the actual operation. + void PropagateOnDragEnter(const gfx::PointF& location, + std::unique_ptr<OSExchangeData> data); WaylandConnection* const connection_; WaylandDataDeviceManager* const data_device_manager_; @@ -93,6 +128,7 @@ class WaylandDataDragController : public WaylandDataDevice::DragDelegate, State state_ = State::kIdle; + // Data offered by us to the other side. std::unique_ptr<WaylandDataSource> data_source_; // When dragging is started from Chromium, |data_| holds the data to be sent @@ -117,6 +153,9 @@ class WaylandDataDragController : public WaylandDataDevice::DragDelegate, // Current window under pointer. WaylandWindow* window_ = nullptr; + // The most recent location received while dragging the data. + gfx::PointF last_drag_location_; + // The data delivered from Wayland std::unique_ptr<ui::OSExchangeData> received_data_; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc index 8bd11603863..3e827a00928 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc @@ -14,24 +14,26 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/clipboard/file_info.h" #include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/drag_drop_types.h" -#include "ui/base/dragdrop/file_info/file_info.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/events/base_event_utils.h" #include "ui/gfx/geometry/point.h" #include "ui/ozone/platform/wayland/common/data_util.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_data_device.h" #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" -#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/test_data_device.h" #include "ui/ozone/platform/wayland/test/test_data_device_manager.h" #include "ui/ozone/platform/wayland/test/test_data_offer.h" #include "ui/ozone/platform/wayland/test/test_data_source.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" +#include "ui/ozone/platform/wayland/test/wayland_drag_drop_test.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" #include "ui/ozone/public/platform_clipboard.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -78,12 +80,8 @@ class MockDropHandler : public WmDropHandler { MockDropHandler() = default; ~MockDropHandler() override = default; - MOCK_METHOD4(OnDragEnter, - void(const gfx::PointF& point, - std::unique_ptr<OSExchangeData> data, - int operation, - int modifiers)); - MOCK_METHOD3(OnDragMotion, + MOCK_METHOD0(MockOnDragEnter, void()); + MOCK_METHOD3(MockDragMotion, int(const gfx::PointF& point, int operation, int modifiers)); MOCK_METHOD0(MockOnDragDrop, void()); MOCK_METHOD0(OnDragLeave, void()); @@ -92,34 +90,52 @@ class MockDropHandler : public WmDropHandler { on_drop_closure_ = closure; } + void SetPreferredOperations(int preferred_operations) { + preferred_operations_ = preferred_operations; + } + OSExchangeData* dropped_data() { return dropped_data_.get(); } + int available_operations() const { return available_operations_; } + protected: + void OnDragEnter(const gfx::PointF& point, + std::unique_ptr<ui::OSExchangeData> data, + int operation, + int modifiers) override { + dropped_data_ = std::move(data); + MockOnDragEnter(); + } void OnDragDrop(std::unique_ptr<OSExchangeData> data, int modifiers) override { - dropped_data_ = std::move(data); MockOnDragDrop(); on_drop_closure_.Run(); on_drop_closure_.Reset(); } + int OnDragMotion(const gfx::PointF& point, + int operation, + int modifiers) override { + available_operations_ = operation; + MockDragMotion(point, operation, modifiers); + return preferred_operations_; + } + private: base::RepeatingClosure on_drop_closure_; std::unique_ptr<OSExchangeData> dropped_data_; + int preferred_operations_ = ui::DragDropTypes::DRAG_COPY; + int available_operations_ = ui::DragDropTypes::DRAG_NONE; }; -class WaylandDataDragControllerTest : public WaylandTest { +class WaylandDataDragControllerTest : public WaylandDragDropTest { public: WaylandDataDragControllerTest() = default; + ~WaylandDataDragControllerTest() override = default; void SetUp() override { - WaylandTest::SetUp(); - - Sync(); - - data_device_manager_ = server_.data_device_manager(); - DCHECK(data_device_manager_); + WaylandDragDropTest::SetUp(); drag_handler_delegate_ = std::make_unique<MockDragHandlerDelegate>(); drop_handler_ = std::make_unique<MockDropHandler>(); @@ -134,63 +150,68 @@ class WaylandDataDragControllerTest : public WaylandTest { return connection_->data_device_manager()->GetDevice(); } + MockDropHandler* drop_handler() { return drop_handler_.get(); } + + MockDragHandlerDelegate* drag_handler() { + return drag_handler_delegate_.get(); + } + + wl::MockSurface* GetMockSurface(uint32_t id) { + return server_.GetObject<wl::MockSurface>(id); + } + + WaylandConnection* connection() { return connection_.get(); } + + WaylandWindow* window() { return window_.get(); } + base::string16 sample_text_for_dnd() const { static auto text = base::ASCIIToUTF16(kSampleTextForDragAndDrop); return text; } - void ReadDataWhenSourceIsReady() { + void RunDragLoopWithSampleData(WaylandWindow* origin_window, int operations) { + ASSERT_TRUE(origin_window); + OSExchangeData os_exchange_data; + os_exchange_data.SetString(sample_text_for_dnd()); + origin_window->StartDrag(os_exchange_data, operations, /*cursor=*/{}, + /*can_grab_pointer=*/true, + drag_handler_delegate_.get()); Sync(); + } - if (!data_device_manager_->data_source()) { - // The data source is created asynchronously via the window's data drag - // controller. If it is null now, it means that the task for that has not - // yet executed, and we have to come later. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &WaylandDataDragControllerTest::ReadDataWhenSourceIsReady, - base::Unretained(this))); - return; - } - - // Now the server can read the data and give it to our callback. - base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - auto callback = base::BindOnce( - [](base::RunLoop* loop, std::vector<uint8_t>&& data) { - std::string result(data.begin(), data.end()); - EXPECT_EQ(kSampleTextForDragAndDrop, result); - loop->Quit(); - }, - &run_loop); - data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8, - std::move(callback)); - run_loop.Run(); - - data_device_manager_->data_source()->OnCancelled(); + std::unique_ptr<WaylandWindow> CreateTestWindow( + PlatformWindowType type, + const gfx::Size& size, + MockPlatformWindowDelegate* delegate) { + DCHECK(delegate); + PlatformWindowInitProperties properties{gfx::Rect(size)}; + properties.type = type; + EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_)).Times(1); + auto window = WaylandWindow::Create(delegate, connection_.get(), + std::move(properties)); + SetWmDropHandler(window.get(), drop_handler_.get()); + EXPECT_NE(gfx::kNullAcceleratedWidget, window->GetWidget()); Sync(); + return window; } void ScheduleDragCancel() { - Sync(); + ScheduleTestTask(base::BindOnce( + [](WaylandDataDragControllerTest* self) { + self->SendDndCancelled(); - if (!data_device_manager_->data_source()) { - // The data source is created asynchronously by the data drag controller. - // If it is null at this point, it means that the task for that has not - // yet executed, and we have to try again a bit later. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&WaylandDataDragControllerTest::ScheduleDragCancel, - base::Unretained(this))); - return; - } + // DnD handlers expect DragLeave to be sent before DragFinished when + // drag sessions end up with no data transfer (cancelled). Otherwise, + // it might lead to issues like https://crbug.com/1109324. + EXPECT_CALL(*self->drop_handler(), OnDragLeave).Times(1); + EXPECT_CALL(*self->drag_handler(), OnDragFinished).Times(1); - data_device_manager_->data_source()->OnCancelled(); - Sync(); + self->Sync(); + }, + base::Unretained(this))); } protected: - wl::TestDataDeviceManager* data_device_manager_; std::unique_ptr<MockDropHandler> drop_handler_; std::unique_ptr<MockDragHandlerDelegate> drag_handler_delegate_; }; @@ -199,22 +220,31 @@ TEST_P(WaylandDataDragControllerTest, StartDrag) { const bool restored_focus = window_->has_pointer_focus(); window_->SetPointerFocus(true); - // The client starts dragging. - ASSERT_EQ(PlatformWindowType::kWindow, window_->type()); - OSExchangeData os_exchange_data; - os_exchange_data.SetString(sample_text_for_dnd()); + auto test = [](WaylandDataDragControllerTest* self) { + // Now the server can read the data and give it to our callback. + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + auto read_callback = base::BindOnce( + [](base::RunLoop* loop, std::vector<uint8_t>&& data) { + std::string result(data.begin(), data.end()); + EXPECT_EQ(kSampleTextForDragAndDrop, result); + loop->Quit(); + }, + &run_loop); + self->ReadData(kMimeTypeTextUtf8, std::move(read_callback)); + run_loop.Run(); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&WaylandDataDragControllerTest::ReadDataWhenSourceIsReady, - base::Unretained(this))); + self->SendDndCancelled(); + self->Sync(); + }; - static_cast<WaylandToplevelWindow*>(window_.get()) - ->StartDrag(os_exchange_data, - DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE, {}, true, - drag_handler_delegate_.get()); - Sync(); + // Post test task to be performed asynchronously once the dnd-related protocol + // objects are ready. + ScheduleTestTask(base::BindOnce(test, base::Unretained(this))); + RunDragLoopWithSampleData( + window_.get(), DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE); + + // Ensure drag delegate it properly reset when the drag loop quits. EXPECT_FALSE(data_device()->drag_delegate_); window_->SetPointerFocus(restored_focus); @@ -286,6 +316,8 @@ TEST_P(WaylandDataDragControllerTest, ReceiveDrag) { 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + int64_t time = (EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX; gfx::Point motion_point(11, 11); @@ -323,27 +355,27 @@ TEST_P(WaylandDataDragControllerTest, DropSeveralMimeTypes) { kMimeTypeURIList, ToClipboardData(std::string("file:///home/user/file\r\n"))); - EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1); + EXPECT_CALL(*drop_handler_, MockOnDragEnter()).Times(1); gfx::Point entered_point(10, 10); data_device_manager_->data_device()->OnEnter( 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), wl_fixed_from_int(entered_point.y()), data_offer); + // Here we are expecting three data items, so there will be three roundtrips + // to the Wayland and back. Hence Sync() three times. + Sync(); + Sync(); Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); base::RunLoop loop; drop_handler_->SetOnDropClosure(loop.QuitClosure()); data_device_manager_->data_device()->OnDrop(); - // Here we are expecting three data items, so there will be three roundtrips - // to the Wayland and back. Hence Sync() three times. - Sync(); - Sync(); Sync(); loop.Run(); Mock::VerifyAndClearExpectations(drop_handler_.get()); + ASSERT_NE(drop_handler_->dropped_data(), nullptr); EXPECT_TRUE(drop_handler_->dropped_data()->HasString()); EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); EXPECT_TRUE(drop_handler_->dropped_data()->HasURL(kFilenameToURLPolicy)); @@ -370,13 +402,12 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedUriList) { auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); data_offer->OnOffer(kMimeTypeURIList, ToClipboardData(kCase.content)); - EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1); + EXPECT_CALL(*drop_handler_, MockOnDragEnter()).Times(1); gfx::Point entered_point(10, 10); data_device_manager_->data_device()->OnEnter( 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), wl_fixed_from_int(entered_point.y()), data_offer); Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); base::RunLoop loop; @@ -425,13 +456,12 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) { data_offer->OnOffer(kMimeTypeMozillaURL, ToClipboardData(base::UTF8ToUTF16(kCase.content))); - EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1); + EXPECT_CALL(*drop_handler_, MockOnDragEnter()).Times(1); gfx::Point entered_point(10, 10); data_device_manager_->data_device()->OnEnter( 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), wl_fixed_from_int(entered_point.y()), data_offer); Sync(); - Mock::VerifyAndClearExpectations(drop_handler_.get()); EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); base::RunLoop loop; @@ -468,27 +498,253 @@ TEST_P(WaylandDataDragControllerTest, StartAndCancel) { const bool restored_focus = window_->has_pointer_focus(); window_->SetPointerFocus(true); - ASSERT_EQ(PlatformWindowType::kWindow, window_->type()); + // Schedule a wl_data_source::cancelled event to be sent asynchronously + // once the drag session gets started. + ScheduleDragCancel(); + + RunDragLoopWithSampleData(window_.get(), DragDropTypes::DRAG_COPY); + + window_->SetPointerFocus(restored_focus); +} + +TEST_P(WaylandDataDragControllerTest, ForeignDragHandleAskAction) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(kMimeTypeText, + ToClipboardData(std::string(kSampleTextForDragAndDrop))); + data_offer->OnSourceActions(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + data_offer->OnAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK); + + gfx::Point entered_point(10, 10); + // The server sends an enter event. + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + + int64_t time = 1; + gfx::Point motion_point(11, 11); + + // Verify ask handling with drop handler preferring "copy" operation. + drop_handler_->SetPreferredOperations(ui::DragDropTypes::DRAG_COPY); + data_device_manager_->data_device()->OnMotion( + time, wl_fixed_from_int(motion_point.x()), + wl_fixed_from_int(motion_point.y())); + Sync(); + + EXPECT_EQ(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, + data_offer->preferred_action()); + EXPECT_EQ(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, + data_offer->supported_actions()); + + data_device_manager_->data_device()->OnLeave(); +} + +// Verifies entered surface destruction is properly handled. +// Regression test for https://crbug.com/1143707. +TEST_P(WaylandDataDragControllerTest, DestroyEnteredSurface) { + auto* window_1 = window_.get(); + const bool restored_focus = window_1->has_pointer_focus(); + window_1->SetPointerFocus(true); + ASSERT_EQ(PlatformWindowType::kWindow, window_1->type()); + + auto test = [](WaylandDataDragControllerTest* self) { + // Init and open |target_window|. + MockPlatformWindowDelegate delegate_2; + auto window_2 = self->CreateTestWindow(PlatformWindowType::kWindow, + gfx::Size(80, 80), &delegate_2); + + // Leave |window_1| and enter |window_2|. + self->SendDndLeave(); + self->SendDndEnter(window_2.get(), gfx::Point(20, 20)); + self->Sync(); + + // Destroy the entered window at client side and emulates a + // wl_data_device::leave to ensure no UAF happens. + window_2->PrepareForShutdown(); + window_2.reset(); + self->SendDndLeave(); + self->Sync(); + + // Emulate server sending an wl_data_source::cancelled event so the drag + // loop is finished. + self->SendDndCancelled(); + self->Sync(); + }; + + // Post test task to be performed asynchronously once the drag session gets + // started. + ScheduleTestTask(base::BindOnce(test, base::Unretained(this))); + + RunDragLoopWithSampleData(window_.get(), DragDropTypes::DRAG_COPY); + + window_1->SetPointerFocus(restored_focus); +} + +// Verifies that early origin surface destruction is properly handled. +// Regression test for https://crbug.com/1143707. +TEST_P(WaylandDataDragControllerTest, DestroyOriginSurface) { + auto* window_1 = window_.get(); + const bool restored_focus = window_1->has_pointer_focus(); + window_1->SetPointerFocus(false); + ASSERT_EQ(PlatformWindowType::kWindow, window_1->type()); + + auto test = [](WaylandDataDragControllerTest* self, + std::unique_ptr<WaylandWindow>* origin) { + // Leave origin surface and enter |window_|. + self->SendDndLeave(); + self->SendDndEnter(self->window(), gfx::Point(20, 20)); + self->Sync(); + + // Shutdown and destroy the popup window where the drag session was + // initiated, which leads to the drag loop to finish. + (*origin)->PrepareForShutdown(); + origin->reset(); + }; + + // Init and open |target_window|. + MockPlatformWindowDelegate delegate_2; + auto window_2 = CreateTestWindow(PlatformWindowType::kPopup, + gfx::Size(80, 80), &delegate_2); + window_2->SetPointerFocus(true); + Sync(); + + // Post test task to be performed asynchronously once the drag session gets + // started. + ScheduleTestTask(base::BindOnce(test, base::Unretained(this), + base::Unretained(&window_2))); + + // Request to start the drag session, which spins a nested run loop. OSExchangeData os_exchange_data; os_exchange_data.SetString(sample_text_for_dnd()); + window_2->StartDrag(os_exchange_data, DragDropTypes::DRAG_COPY, {}, true, + drag_handler_delegate_.get()); + Sync(); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&WaylandDataDragControllerTest::ScheduleDragCancel, - base::Unretained(this))); + // Send wl_data_source::cancelled event. The drag controller is then + // expected to gracefully reset its internal state. + SendDndLeave(); + SendDndCancelled(); + Sync(); - // DnD handlers expect DragLeave to be sent before DragFinished when drag - // sessions end up with no data transfer (cancelled). Otherwise, it might lead - // to issues like https://crbug.com/1109324. - EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); - EXPECT_CALL(*drag_handler_delegate_, OnDragFinished(_)).Times(1); + window_1->SetPointerFocus(restored_focus); +} - static_cast<WaylandToplevelWindow*>(window_.get()) - ->StartDrag(os_exchange_data, DragDropTypes::DRAG_COPY, {}, true, - drag_handler_delegate_.get()); - Sync(); +// Ensures drag/drop events are properly propagated to non-toplevel windows. +TEST_P(WaylandDataDragControllerTest, DragToNonToplevelWindows) { + auto* origin_window = window_.get(); + const bool restored_focus = origin_window->has_pointer_focus(); + origin_window->SetPointerFocus(true); + + auto test = [](WaylandDataDragControllerTest* self, + PlatformWindowType window_type) { + // Init and open |target_window|. + MockPlatformWindowDelegate aux_window_delegate; + auto aux_window = self->CreateTestWindow(window_type, gfx::Size(100, 40), + &aux_window_delegate); + + // Leave |origin_window| and enter non-toplevel |aux_window|. + self->SendDndLeave(); + EXPECT_CALL(*self->drop_handler(), OnDragLeave).Times(1); + self->Sync(); + + self->SendDndEnter(aux_window.get(), {}); + EXPECT_CALL(*self->drop_handler(), MockOnDragEnter()).Times(1); + EXPECT_CALL(*self->drop_handler(), MockDragMotion(_, _, _)).Times(1); + self->Sync(); + + // Goes back to |origin_window|, as |aux_window| is going to get destroyed + // once this test task finishes. + self->SendDndLeave(); + EXPECT_CALL(*self->drop_handler(), OnDragLeave).Times(1); + self->Sync(); + + self->SendDndEnter(self->window(), {}); + EXPECT_CALL(*self->drop_handler(), MockOnDragEnter()).Times(1); + EXPECT_CALL(*self->drop_handler(), MockDragMotion(_, _, _)).Times(1); + self->Sync(); + }; + + // Post test tasks, for each non-toplevel window type, to be performed + // asynchronously once the dnd-related protocol objects are ready. + constexpr PlatformWindowType kNonToplevelWindowTypes[]{ + PlatformWindowType::kPopup, PlatformWindowType::kMenu, + PlatformWindowType::kTooltip, PlatformWindowType::kBubble}; + for (auto window_type : kNonToplevelWindowTypes) + ScheduleTestTask(base::BindOnce(test, base::Unretained(this), window_type)); + + // Post a wl_data_source::cancelled notifying the client to tear down the drag + // session. + ScheduleDragCancel(); + + // Request to start the drag session, which spins a nested run loop. + RunDragLoopWithSampleData(origin_window, DragDropTypes::DRAG_COPY); + + origin_window->SetPointerFocus(restored_focus); +} - window_->SetPointerFocus(restored_focus); +// Ensures that requests to create a |PlatformWindowType::kPopup| during drag +// sessions return wl_subsurface-backed windows. +TEST_P(WaylandDataDragControllerTest, PopupRequestCreatesAuxiliaryWindow) { + auto* origin_window = window_.get(); + const bool restored_focus = origin_window->has_pointer_focus(); + origin_window->SetPointerFocus(true); + + auto test = [](WaylandDataDragControllerTest* self) { + MockPlatformWindowDelegate delegate; + auto popup_window = self->CreateTestWindow(PlatformWindowType::kPopup, + gfx::Size(100, 40), &delegate); + popup_window->Show(false); + self->Sync(); + + auto* surface = + self->GetMockSurface(popup_window->root_surface()->GetSurfaceId()); + ASSERT_TRUE(surface); + EXPECT_NE(nullptr, surface->sub_surface()); + }; + + ScheduleTestTask(base::BindOnce(test, base::Unretained(this))); + + // Post a wl_data_source::cancelled notifying the client to tear down the drag + // session. + ScheduleDragCancel(); + + // Request to start the drag session, which spins a nested run loop. + RunDragLoopWithSampleData(origin_window, DragDropTypes::DRAG_COPY); + + origin_window->SetPointerFocus(restored_focus); +} + +// Ensures that requests to create a |PlatformWindowType::kMenu| during drag +// sessions return xdg_popup-backed windows. +TEST_P(WaylandDataDragControllerTest, MenuRequestCreatesPopupWindow) { + auto* origin_window = window_.get(); + const bool restored_focus = origin_window->has_pointer_focus(); + origin_window->SetPointerFocus(true); + + auto test = [](WaylandDataDragControllerTest* self) { + MockPlatformWindowDelegate delegate; + auto menu_window = self->CreateTestWindow(PlatformWindowType::kMenu, + gfx::Size(100, 40), &delegate); + menu_window->Show(false); + self->Sync(); + + auto* surface = + self->GetMockSurface(menu_window->root_surface()->GetSurfaceId()); + ASSERT_TRUE(surface); + EXPECT_EQ(nullptr, surface->sub_surface()); + }; + + ScheduleTestTask(base::BindOnce(test, base::Unretained(this))); + + // Post a wl_data_source::cancelled notifying the client to tear down the drag + // session. + ScheduleDragCancel(); + + // Request to start the drag session, which spins a nested run loop. + RunDragLoopWithSampleData(origin_window, DragDropTypes::DRAG_COPY); + + origin_window->SetPointerFocus(restored_focus); } INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc index 84d29d32b60..7e2a7143fd7 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.cc @@ -24,14 +24,6 @@ WaylandDataOffer::~WaylandDataOffer() { data_offer_.reset(); } -void WaylandDataOffer::SetAction(uint32_t dnd_actions, - uint32_t preferred_action) { - if (wl::get_version_of_object(data_offer_.get()) >= - WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { - wl_data_offer_set_actions(data_offer_.get(), dnd_actions, preferred_action); - } -} - void WaylandDataOffer::Accept(uint32_t serial, const std::string& mime_type) { wl_data_offer_accept(data_offer_.get(), serial, mime_type.c_str()); } @@ -68,12 +60,21 @@ void WaylandDataOffer::FinishOffer() { } } -uint32_t WaylandDataOffer::source_actions() const { - return source_actions_; -} +void WaylandDataOffer::SetActions(uint32_t dnd_actions) { + if (wl::get_version_of_object(data_offer_.get()) < + WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + return; + } + + // Determine preferred action based on the given |dnd_actions|, prioritizing + // "copy" over "move", if both are set. + uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + else if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; -uint32_t WaylandDataOffer::dnd_action() const { - return dnd_action_; + wl_data_offer_set_actions(data_offer_.get(), dnd_actions, preferred_action); } // static diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h index a724709c9c1..467e4c48d4c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer.h @@ -27,16 +27,16 @@ class WaylandDataOffer : public WaylandDataOfferBase { explicit WaylandDataOffer(wl_data_offer* data_offer); ~WaylandDataOffer() override; - void SetAction(uint32_t dnd_actions, uint32_t preferred_action); void Accept(uint32_t serial, const std::string& mime_type); void Reject(uint32_t serial); + void FinishOffer(); // WaylandDataOfferBase overrides: base::ScopedFD Receive(const std::string& mime_type) override; - void FinishOffer(); - uint32_t source_actions() const; - uint32_t dnd_action() const; + uint32_t source_actions() const { return source_actions_; } + uint32_t dnd_action() const { return dnd_action_; } + void SetActions(uint32_t dnd_actions); private: // wl_data_offer_listener callbacks. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc index cbdd82a25d0..bf4393a9a17 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc @@ -4,7 +4,7 @@ #include "ui/ozone/platform/wayland/host/wayland_data_offer_base.h" -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "ui/base/clipboard/clipboard_constants.h" namespace ui { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc index 15eb6b6e2c2..28f5892a0e9 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc @@ -17,6 +17,8 @@ #include "ui/events/keycodes/dom/dom_key.h" #include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/ozone/layout/keyboard_layout_engine.h" +#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/events/pointer_details.h" #include "ui/events/types/event_type.h" #include "ui/gfx/geometry/point_f.h" @@ -85,17 +87,6 @@ bool WaylandEventSource::StopProcessingEvents() { return event_watcher_->StopProcessingEvents(); } -void WaylandEventSource::OnKeyboardCreated(WaylandKeyboard* keyboard) { - DCHECK(keyboard); - keyboard_ = keyboard; -} - -void WaylandEventSource::OnKeyboardDestroyed(WaylandKeyboard* keyboard) { - DCHECK_EQ(keyboard_, keyboard); - keyboard_modifiers_ = 0; - keyboard_ = nullptr; -} - void WaylandEventSource::OnKeyboardFocusChanged(WaylandWindow* window, bool focused) { DCHECK(window); @@ -109,14 +100,15 @@ void WaylandEventSource::OnKeyboardModifiersChanged(int modifiers) { uint32_t WaylandEventSource::OnKeyboardKeyEvent(EventType type, DomCode dom_code, bool repeat, - base::TimeTicks timestamp) { + base::TimeTicks timestamp, + int device_id) { DCHECK(type == ET_KEY_PRESSED || type == ET_KEY_RELEASED); - if (!keyboard_) - return POST_DISPATCH_NONE; DomKey dom_key; KeyboardCode key_code; - if (!keyboard_->Decode(dom_code, keyboard_modifiers_, &dom_key, &key_code)) { + auto* layout_engine = KeyboardLayoutEngineManager::GetKeyboardLayoutEngine(); + if (!layout_engine || !layout_engine->Lookup(dom_code, keyboard_modifiers_, + &dom_key, &key_code)) { LOG(ERROR) << "Failed to decode key event."; return POST_DISPATCH_NONE; } @@ -128,30 +120,12 @@ uint32_t WaylandEventSource::OnKeyboardKeyEvent(EventType type, KeyEvent event(type, key_code, dom_code, keyboard_modifiers_, dom_key, timestamp); - event.set_source_device_id(keyboard_->device_id()); + event.set_source_device_id(device_id); return DispatchEvent(&event); } -void WaylandEventSource::OnPointerCreated(WaylandPointer* pointer) { - DCHECK(pointer); - pointer_ = pointer; -} - -void WaylandEventSource::OnPointerDestroyed(WaylandPointer* pointer) { - DCHECK_EQ(pointer_, pointer); - - // Clear focused window, if any. - HandlePointerFocusChange(nullptr); - - ResetPointerFlags(); - pointer_ = nullptr; -} - void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window, const gfx::PointF& location) { - if (!pointer_) - return; - // Save new pointer location. pointer_location_ = location; @@ -174,9 +148,6 @@ void WaylandEventSource::OnPointerButtonEvent(EventType type, DCHECK(type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED); DCHECK(HasAnyPointerButtonFlag(changed_button)); - if (!pointer_) - return; - auto* prev_focused_window = window_with_pointer_focus_; if (window) HandlePointerFocusChange(window); @@ -212,6 +183,10 @@ void WaylandEventSource::OnPointerAxisEvent(const gfx::Vector2d& offset) { current_pointer_frame_.dy += offset.y(); } +void WaylandEventSource::OnResetPointerFlags() { + ResetPointerFlags(); +} + void WaylandEventSource::OnPointerFrameEvent() { base::TimeTicks now = EventTimeForNow(); current_pointer_frame_.dt = now - last_pointer_frame_time_; @@ -255,17 +230,6 @@ void WaylandEventSource::OnPointerAxisStopEvent(uint32_t axis) { current_pointer_frame_.is_axis_stop = true; } -void WaylandEventSource::OnTouchCreated(WaylandTouch* touch) { - DCHECK(touch); - touch_ = touch; -} - -void WaylandEventSource::OnTouchDestroyed(WaylandTouch* touch) { - DCHECK_EQ(touch_, touch); - touch_points_.clear(); - touch_ = nullptr; -} - void WaylandEventSource::OnTouchPressEvent(WaylandWindow* window, const gfx::PointF& location, base::TimeTicks timestamp, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h index dfda62810e5..ed0a6f85250 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h @@ -81,18 +81,15 @@ class WaylandEventSource : public PlatformEventSource, protected: // WaylandKeyboard::Delegate - void OnKeyboardCreated(WaylandKeyboard* keyboard) override; - void OnKeyboardDestroyed(WaylandKeyboard* keyboard) override; void OnKeyboardFocusChanged(WaylandWindow* window, bool focused) override; void OnKeyboardModifiersChanged(int modifiers) override; uint32_t OnKeyboardKeyEvent(EventType type, DomCode dom_code, bool repeat, - base::TimeTicks timestamp) override; + base::TimeTicks timestamp, + int device_id) override; // WaylandPointer::Delegate - void OnPointerCreated(WaylandPointer* pointer) override; - void OnPointerDestroyed(WaylandPointer* pointer) override; void OnPointerFocusChanged(WaylandWindow* window, const gfx::PointF& location) override; void OnPointerButtonEvent(EventType evtype, @@ -103,10 +100,9 @@ class WaylandEventSource : public PlatformEventSource, void OnPointerFrameEvent() override; void OnPointerAxisSourceEvent(uint32_t axis_source) override; void OnPointerAxisStopEvent(uint32_t axis) override; + void OnResetPointerFlags() override; // WaylandTouch::Delegate - void OnTouchCreated(WaylandTouch* touch) override; - void OnTouchDestroyed(WaylandTouch* touch) override; void OnTouchPressEvent(WaylandWindow* window, const gfx::PointF& location, base::TimeTicks timestamp, @@ -146,11 +142,6 @@ class WaylandEventSource : public PlatformEventSource, WaylandWindowManager* const window_manager_; - // Input device objects. Owned by WaylandConnection. - WaylandKeyboard* keyboard_ = nullptr; - WaylandPointer* pointer_ = nullptr; - WaylandTouch* touch_ = nullptr; - // Bitmask of EventFlags used to keep track of the the pointer state. int pointer_flags_ = 0; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc index 9fd65bd9afa..c97a0e31bd3 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc @@ -4,8 +4,11 @@ #include "ui/ozone/platform/wayland/host/wayland_event_watcher.h" +#include <cstring> + #include "base/bind.h" #include "base/check.h" +#include "base/logging.h" #include "base/task/current_thread.h" #include "ui/events/event.h" #include "ui/ozone/platform/wayland/common/wayland.h" @@ -55,6 +58,8 @@ void WaylandEventWatcher::OnFileCanReadWithoutBlocking(int fd) { if (prepared_) { prepared_ = false; + // Errors will be checked the next time OnFileCanReadWithoutBlocking calls + // CheckForErrors. if (wl_display_read_events(display_) == -1) return; wl_display_dispatch_pending(display_); @@ -114,13 +119,38 @@ void WaylandEventWatcher::MaybePrepareReadQueue() { } bool WaylandEventWatcher::CheckForErrors() { + // Errors are fatal. If this function returns non-zero the display can no + // longer be used. int err = wl_display_get_error(display_); - if (err == EPROTO) { + + // TODO(crbug.com/1172305): Wayland display_error message should be printed + // automatically by wl_log(). However, wl_log() does not print anything. Needs + // investigation. + if (err) { + // When |err| is EPROTO, we can still use the |display_| to retrieve the + // protocol error. Otherwise, get the error string from strerror and + // shutdown the browser. + if (err == EPROTO) { + uint32_t ec, id; + const struct wl_interface* intf; + ec = wl_display_get_protocol_error(display_, &intf, &id); + if (intf) { + LOG(ERROR) << "Fatal Wayland protocol error " << ec << " on interface " + << intf->name << " (object " << id << "). Shutting down.."; + } else { + LOG(ERROR) << "Fatal Wayland protocol error " << ec + << ". Shutting down.."; + } + } else { + LOG(ERROR) << "Fatal Wayland communication error: " << std::strerror(err); + } + // This can be null in tests. if (!shutdown_cb_.is_null()) std::move(shutdown_cb_).Run(); return false; } + return true; } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc index a7bd5baba4f..b7e3c6b3238 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_input_method_context.cc @@ -12,6 +12,7 @@ #include "base/optional.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "build/chromeos_buildflags.h" #include "ui/base/ime/composition_text.h" #include "ui/base/ime/ime_text_span.h" #include "ui/events/base_event_utils.h" @@ -24,9 +25,16 @@ #include "ui/ozone/public/ozone_switches.h" #if BUILDFLAG(USE_XKBCOMMON) +#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h" #endif +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "base/check.h" +#include "chromeos/crosapi/mojom/crosapi.mojom.h" +#include "chromeos/lacros/lacros_chrome_service_impl.h" +#endif + namespace ui { namespace { @@ -42,6 +50,41 @@ base::Optional<size_t> OffsetFromUTF8Offset(const base::StringPiece& text, return converted.size(); } +bool IsImeEnabled() { + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + // We do not expect both switches are set at the same time. + DCHECK(!cmd_line->HasSwitch(switches::kEnableWaylandIme) || + !cmd_line->HasSwitch(switches::kDisableWaylandIme)); + // Force enable/disable wayland IMEs, when explictly specified via commandline + // arguments. + if (cmd_line->HasSwitch(switches::kEnableWaylandIme)) + return true; + if (cmd_line->HasSwitch(switches::kDisableWaylandIme)) + return false; + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // On Lacros chrome, we check whether ash-chrome supports IME, then + // enable IME if so. This allows us to control IME enabling state in + // Lacros-chrome side, which helps us on releasing. + // TODO(crbug.com/1159237): In the future, we may want to unify the behavior + // of ozone/wayland across platforms. + const auto* lacros_chrome_service = chromeos::LacrosChromeServiceImpl::Get(); + + // Note: |init_params| may be null, if ash-chrome is too old. + // TODO(crbug.com/1156033): Clean up the condition, after ash-chrome in the + // world becomes new enough. + const crosapi::mojom::BrowserInitParams* init_params = + lacros_chrome_service->init_params(); + if (init_params && init_params->exo_ime_support != + crosapi::mojom::ExoImeSupport::kUnsupported) { + return true; + } +#endif + + // Do not enable wayland IME by default. + return false; +} + } // namespace WaylandInputMethodContext::WaylandInputMethodContext( @@ -65,10 +108,8 @@ WaylandInputMethodContext::~WaylandInputMethodContext() { } void WaylandInputMethodContext::Init(bool initialize_for_testing) { - bool use_ozone_wayland_vkb = - initialize_for_testing || - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableWaylandIme); + bool use_ozone_wayland_vkb = initialize_for_testing || IsImeEnabled(); + // If text input instance is not created then all ime context operations // are noop. This option is because in some environments someone might not // want to enable ime/virtual keyboard even if it's available. @@ -215,16 +256,24 @@ void WaylandInputMethodContext::OnKeysym(uint32_t keysym, uint32_t state, uint32_t modifiers) { #if BUILDFLAG(USE_XKBCOMMON) + auto* layout_engine = KeyboardLayoutEngineManager::GetKeyboardLayoutEngine(); + if (!layout_engine) + return; + // TODO(crbug.com/1079353): Handle modifiers. - DomCode dom_code = - connection_->keyboard()->layout_engine()->GetDomCodeByKeysym(keysym); + DomCode dom_code = static_cast<XkbKeyboardLayoutEngine*>(layout_engine) + ->GetDomCodeByKeysym(keysym); if (dom_code == DomCode::NONE) return; + // Keyboard might not exist. + int device_id = + connection_->keyboard() ? connection_->keyboard()->device_id() : 0; + EventType type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? ET_KEY_PRESSED : ET_KEY_RELEASED; key_delegate_->OnKeyboardKeyEvent(type, dom_code, /*repeat=*/false, - EventTimeForNow()); + EventTimeForNow(), device_id); #else NOTIMPLEMENTED(); #endif diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc index 7b1e06e7ad5..812bdfcaf5a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc @@ -51,9 +51,6 @@ WaylandKeyboard::WaylandKeyboard( &WaylandKeyboard::Modifiers, &WaylandKeyboard::RepeatInfo, }; - DCHECK(delegate_); - delegate_->OnKeyboardCreated(this); - wl_keyboard_add_listener(obj_.get(), &listener, this); // TODO(tonikitoo): Default auto-repeat to ON here? @@ -63,7 +60,8 @@ WaylandKeyboard::WaylandKeyboard( } WaylandKeyboard::~WaylandKeyboard() { - delegate_->OnKeyboardDestroyed(this); + // Reset keyboard modifiers on destruction. + delegate_->OnKeyboardModifiersChanged(0); } void WaylandKeyboard::Keymap(void* data, @@ -194,8 +192,9 @@ void WaylandKeyboard::DispatchKey(uint32_t key, // Pass empty DomKey and KeyboardCode here so the delegate can pre-process // and decode it when needed. - uint32_t result = delegate_->OnKeyboardKeyEvent( - down ? ET_KEY_PRESSED : ET_KEY_RELEASED, dom_code, repeat, timestamp); + uint32_t result = + delegate_->OnKeyboardKeyEvent(down ? ET_KEY_PRESSED : ET_KEY_RELEASED, + dom_code, repeat, timestamp, device_id); if (extended_keyboard_v1_) { bool handled = result & POST_DISPATCH_STOP_PROPAGATION; @@ -204,15 +203,6 @@ void WaylandKeyboard::DispatchKey(uint32_t key, } } -bool WaylandKeyboard::Decode(DomCode dom_code, - int modifiers, - DomKey* out_dom_key, - KeyboardCode* out_key_code) { - DCHECK(out_dom_key); - DCHECK(out_key_code); - return layout_engine_->Lookup(dom_code, modifiers, out_dom_key, out_key_code); -} - void WaylandKeyboard::SyncCallback(void* data, struct wl_callback* cb, uint32_t time) { diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h index d655dc09cc3..d6f0b0bb025 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h @@ -17,7 +17,6 @@ namespace ui { -class DomKey; class KeyboardLayoutEngine; class WaylandConnection; class WaylandWindow; @@ -29,14 +28,6 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate { public: class Delegate; - using LayoutEngine = -#if BUILDFLAG(USE_XKBCOMMON) - XkbKeyboardLayoutEngine -#else - KeyboardLayoutEngine -#endif - ; - WaylandKeyboard(wl_keyboard* keyboard, zcr_keyboard_extension_v1* keyboard_extension_v1, WaylandConnection* connection, @@ -45,13 +36,16 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate { virtual ~WaylandKeyboard(); int device_id() const { return obj_.id(); } - bool Decode(DomCode dom_code, - int modifiers, - DomKey* out_dom_key, - KeyboardCode* out_key_code); - LayoutEngine* layout_engine() const { return layout_engine_; } private: + using LayoutEngine = +#if BUILDFLAG(USE_XKBCOMMON) + XkbKeyboardLayoutEngine +#else + KeyboardLayoutEngine +#endif + ; + // wl_keyboard_listener static void Keymap(void* data, wl_keyboard* obj, @@ -113,8 +107,6 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate { class WaylandKeyboard::Delegate { public: - virtual void OnKeyboardCreated(WaylandKeyboard* keyboard) = 0; - virtual void OnKeyboardDestroyed(WaylandKeyboard* keyboard) = 0; virtual void OnKeyboardFocusChanged(WaylandWindow* window, bool focused) = 0; virtual void OnKeyboardModifiersChanged(int modifiers) = 0; // Returns a mask of ui::PostDispatchAction indicating how the event was @@ -122,7 +114,8 @@ class WaylandKeyboard::Delegate { virtual uint32_t OnKeyboardKeyEvent(EventType type, DomCode dom_code, bool repeat, - base::TimeTicks timestamp) = 0; + base::TimeTicks timestamp, + int device_id) = 0; protected: // Prevent deletion through a WaylandKeyboard::Delegate pointer. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc index b4fb4f09c5c..50d55f66b5c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc @@ -27,14 +27,15 @@ WaylandPointer::WaylandPointer(wl_pointer* pointer, &WaylandPointer::AxisSource, &WaylandPointer::AxisStop, &WaylandPointer::AxisDiscrete}; - DCHECK(delegate_); - delegate_->OnPointerCreated(this); - wl_pointer_add_listener(obj_.get(), &listener, this); } WaylandPointer::~WaylandPointer() { - delegate_->OnPointerDestroyed(this); + // Even though, WaylandPointer::Leave is always called when Wayland destroys + // wl_pointer, it's better to be explicit as some Wayland compositors may have + // bugs. + delegate_->OnPointerFocusChanged(nullptr, {}); + delegate_->OnResetPointerFlags(); } // static @@ -130,7 +131,7 @@ void WaylandPointer::Axis(void* data, offset.set_y(-wl_fixed_to_double(value) / kAxisValueScale * MouseWheelEvent::kWheelDelta); } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - offset.set_x(wl_fixed_to_double(value) / kAxisValueScale * + offset.set_x(-wl_fixed_to_double(value) / kAxisValueScale * MouseWheelEvent::kWheelDelta); } else { return; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h index 2ebb8419769..f9ad83b385a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h @@ -80,8 +80,6 @@ class WaylandPointer { class WaylandPointer::Delegate { public: - virtual void OnPointerCreated(WaylandPointer* pointer) = 0; - virtual void OnPointerDestroyed(WaylandPointer* pointer) = 0; virtual void OnPointerFocusChanged(WaylandWindow* window, const gfx::PointF& location) = 0; virtual void OnPointerButtonEvent(EventType evtype, @@ -92,6 +90,7 @@ class WaylandPointer::Delegate { virtual void OnPointerFrameEvent() = 0; virtual void OnPointerAxisSourceEvent(uint32_t axis_source) = 0; virtual void OnPointerAxisStopEvent(uint32_t axis) = 0; + virtual void OnResetPointerFlags() = 0; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc index 91e9b9707c6..94d56df903a 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc @@ -234,7 +234,7 @@ TEST_P(WaylandPointerTest, AxisHorizontal) { ASSERT_TRUE(event); ASSERT_TRUE(event->IsMouseWheelEvent()); auto* mouse_wheel_event = event->AsMouseWheelEvent(); - EXPECT_EQ(gfx::Vector2d(MouseWheelEvent::kWheelDelta, 0), + EXPECT_EQ(gfx::Vector2d(-MouseWheelEvent::kWheelDelta, 0), mouse_wheel_event->offset()); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_wheel_event->button_flags()); EXPECT_EQ(0, mouse_wheel_event->changed_button_flags()); @@ -340,11 +340,11 @@ TEST_P(WaylandPointerTest, FlingVertical) { EXPECT_EQ(ET_SCROLL_FLING_START, scroll_event->type()); EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); EXPECT_EQ(0.0f, scroll_event->x_offset()); + EXPECT_EQ(0.0f, scroll_event->x_offset_ordinal()); // Initial vertical velocity depends on the implementation outside of // WaylandPointer, but it should be negative value based on the direction of // recent two axis events. EXPECT_GT(0.0f, scroll_event->y_offset()); - EXPECT_EQ(0.0f, scroll_event->x_offset_ordinal()); EXPECT_GT(0.0f, scroll_event->y_offset_ordinal()); } @@ -393,13 +393,13 @@ TEST_P(WaylandPointerTest, FlingHorizontal) { auto* scroll_event = event3->AsScrollEvent(); EXPECT_EQ(ET_SCROLL_FLING_START, scroll_event->type()); EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); - // Initial horizontal velocity depends on the implementation outside of - // WaylandPointer, but it should be positive value based on the direction of - // recent two axis events. - EXPECT_LT(0.0f, scroll_event->x_offset()); EXPECT_EQ(0.0f, scroll_event->y_offset()); - EXPECT_LT(0.0f, scroll_event->x_offset_ordinal()); EXPECT_EQ(0.0f, scroll_event->y_offset_ordinal()); + // Initial horizontal velocity depends on the implementation outside of + // WaylandPointer, but it should be negative value based on the direction of + // recent two axis events. + EXPECT_GT(0.0f, scroll_event->x_offset()); + EXPECT_GT(0.0f, scroll_event->x_offset_ordinal()); } TEST_P(WaylandPointerTest, FlingCancel) { @@ -518,10 +518,10 @@ TEST_P(WaylandPointerTest, FlingDiagonal) { auto* scroll_event = event5->AsScrollEvent(); EXPECT_EQ(ET_SCROLL_FLING_START, scroll_event->type()); EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); - // Check the offset direction. It should non-zero in both directions. - EXPECT_LT(0.0f, scroll_event->x_offset()); + // Check the offset direction. It should non-zero in both axes. + EXPECT_GT(0.0f, scroll_event->x_offset()); EXPECT_GT(0.0f, scroll_event->y_offset()); - EXPECT_LT(0.0f, scroll_event->x_offset_ordinal()); + EXPECT_GT(0.0f, scroll_event->x_offset_ordinal()); EXPECT_GT(0.0f, scroll_event->y_offset_ordinal()); // Horizontal offset should be larger than vertical one, given the scroll // offset in each direction. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc index 62601c3dde5..736b2388374 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc @@ -4,11 +4,14 @@ #include "ui/ozone/platform/wayland/host/wayland_popup.h" +#include <aura-shell-client-protocol.h> + #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/shell_object_factory.h" #include "ui/ozone/platform/wayland/host/shell_popup_wrapper.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_zaura_shell.h" namespace ui { @@ -33,9 +36,22 @@ bool WaylandPopup::CreateShellPopup() { } parent_window()->set_child_window(this); + InitializeAuraShellSurface(); return true; } +void WaylandPopup::InitializeAuraShellSurface() { + DCHECK(shell_popup_); + if (!connection()->zaura_shell() || aura_surface_) + return; + aura_surface_.reset(zaura_shell_get_aura_surface( + connection()->zaura_shell()->wl_object(), root_surface()->surface())); + if (shadow_type_ == PlatformWindowShadowType::kDrop) { + zaura_surface_set_frame(aura_surface_.get(), + ZAURA_SURFACE_FRAME_TYPE_SHADOW); + } +} + void WaylandPopup::Show(bool inactive) { if (shell_popup_) return; @@ -121,6 +137,10 @@ void WaylandPopup::HandlePopupConfigure(const gfx::Rect& bounds_dip) { SetBoundsDip(new_bounds_dip); } +void WaylandPopup::HandleSurfaceConfigure(uint32_t serial) { + shell_popup()->AckConfigure(serial); +} + void WaylandPopup::OnCloseRequest() { // Before calling OnCloseRequest, the |shell_popup_| must become hidden and // only then call OnCloseRequest(). @@ -133,6 +153,7 @@ bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) { DCHECK(parent_window()); root_surface()->SetBufferScale(parent_window()->buffer_scale(), false); set_ui_scale(parent_window()->ui_scale()); + shadow_type_ = properties.shadow_type; return true; } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.h b/chromium/ui/ozone/platform/wayland/host/wayland_popup.h index a5810317d86..38af4fa80b4 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.h @@ -27,12 +27,17 @@ class WaylandPopup : public WaylandWindow { private: // WaylandWindow overrides: void HandlePopupConfigure(const gfx::Rect& bounds) override; + void HandleSurfaceConfigure(uint32_t serial) override; void OnCloseRequest() override; bool OnInitialize(PlatformWindowInitProperties properties) override; // Creates a popup window, which is visible as a menu window. bool CreateShellPopup(); + // Initializes the aura-shell surface, in the case aura-shell EXO extension + // is available. + void InitializeAuraShellSurface(); + // Returns bounds with origin relative to parent window's origin. gfx::Rect AdjustPopupWindowPosition(); @@ -40,6 +45,10 @@ class WaylandPopup : public WaylandWindow { // know anything about the version. std::unique_ptr<ShellPopupWrapper> shell_popup_; + wl::Object<zaura_surface> aura_surface_; + + PlatformWindowShadowType shadow_type_ = PlatformWindowShadowType::kNone; + DISALLOW_COPY_AND_ASSIGN(WaylandPopup); }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc index 07e4c2864ec..42c13a25090 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc @@ -7,7 +7,7 @@ #include <set> #include <vector> -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "ui/display/display.h" @@ -40,7 +40,7 @@ WaylandScreen::WaylandScreen(WaylandConnection* connection) auto format = buffer_format.first; // TODO(crbug.com/1127822): Investigate a better fix for this. -#if !BUILDFLAG(IS_LACROS) +#if !BUILDFLAG(IS_CHROMEOS_LACROS) // RGBA_8888 is the preferred format, except when running on ChromiumOS. See // crbug.com/1127558. if (format == gfx::BufferFormat::RGBA_8888) @@ -50,7 +50,7 @@ WaylandScreen::WaylandScreen(WaylandConnection* connection) // available, but for some reason Chromium gets broken when it's used. // Though, we can import RGBX_8888 dma buffer to EGLImage successfully. // Enable that back when the issue is resolved. -#endif // !BUILDFLAG(IS_LACROS) +#endif // !BUILDFLAG(IS_CHROMEOS_LACROS) if (!image_format_alpha_ && format == gfx::BufferFormat::BGRA_8888) image_format_alpha_ = gfx::BufferFormat::BGRA_8888; @@ -99,10 +99,12 @@ void WaylandScreen::AddOrUpdateDisplay(uint32_t output_id, const gfx::Rect& new_bounds, int32_t scale_factor) { display::Display changed_display(output_id); - if (!display::Display::HasForceDeviceScaleFactor()) - changed_display.set_device_scale_factor(scale_factor); - changed_display.set_bounds(new_bounds); - changed_display.set_work_area(new_bounds); + if (!display::Display::HasForceDeviceScaleFactor()) { + changed_display.SetScaleAndBounds(scale_factor, new_bounds); + } else { + changed_display.set_bounds(new_bounds); + changed_display.set_work_area(new_bounds); + } gfx::DisplayColorSpaces color_spaces; color_spaces.SetOutputBufferFormats(image_format_no_alpha_.value(), @@ -235,6 +237,9 @@ gfx::AcceleratedWidget WaylandScreen::GetLocalProcessWidgetAtPoint( display::Display WaylandScreen::GetDisplayNearestPoint( const gfx::Point& point) const { + auto displays = GetAllDisplays(); + if (displays.size() <= 1) + return GetPrimaryDisplay(); return *FindDisplayNearestPoint(display_list_.displays(), point); } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc index 46b30f43b54..865ebbe730b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc @@ -232,9 +232,13 @@ TEST_P(WaylandScreenTest, OutputPropertyChanges) { Sync(); - changed_values = display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; + changed_values = + display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR | + display::DisplayObserver::DISPLAY_METRIC_WORK_AREA | + display::DisplayObserver::DISPLAY_METRIC_BOUNDS; EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values); EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value); + EXPECT_EQ(observer.GetDisplay().bounds(), gfx::Rect(50, 50)); platform_screen_->RemoveObserver(&observer); } diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_shm.h b/chromium/ui/ozone/platform/wayland/host/wayland_shm.h index 4f7f13ea8bf..0b484ed6e87 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_shm.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_shm.h @@ -24,6 +24,8 @@ class WaylandShm { WaylandShm(wl_shm* shm, WaylandConnection* connection); ~WaylandShm(); + wl_shm* get() const { return shm_.get(); } + // Creates a wl_buffer based on shared memory handle for the specified // |widget|. wl::Object<struct wl_buffer> CreateBuffer(base::ScopedFD fd, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc index f3092c43e31..99906d1c6dd 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc @@ -4,8 +4,10 @@ #include "ui/ozone/platform/wayland/host/wayland_surface.h" +#include <linux-explicit-synchronization-unstable-v1-client-protocol.h> #include <viewporter-client-protocol.h> +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" @@ -50,7 +52,19 @@ bool WaylandSurface::Initialize() { LOG(ERROR) << "Failed to create wp_viewport"; return false; } + } else { + LOG(WARNING) << "Server doesn't support wp_viewporter."; + } + + // The server needs to support the linux_explicit_synchronization protocol. + if (!connection_->linux_explicit_synchronization_v1()) { + LOG(WARNING) + << "Server doesn't support zwp_linux_explicit_synchronization_v1."; + return true; } + surface_sync_.reset(zwp_linux_explicit_synchronization_v1_get_synchronization( + connection_->linux_explicit_synchronization_v1(), surface_.get())); + DCHECK(surface_sync()); return true; } @@ -60,6 +74,11 @@ void WaylandSurface::UnsetRootWindow() { root_window_ = nullptr; } +void WaylandSurface::SetAcquireFence(const gfx::GpuFenceHandle& acquire_fence) { + zwp_linux_surface_synchronization_v1_set_acquire_fence( + surface_sync(), acquire_fence.owned_fd.get()); +} + void WaylandSurface::AttachBuffer(wl_buffer* buffer) { // The logic in DamageBuffer currently relies on attachment coordinates of // (0, 0). If this changes, then the calculation in DamageBuffer will also @@ -87,10 +106,12 @@ void WaylandSurface::UpdateBufferDamageRegion( if (!crop_rect_.IsEmpty()) { viewport_src = gfx::ToEnclosedRect( gfx::ScaleRect(crop_rect_, bounds.width(), bounds.height())); - wp_viewport_set_source(viewport(), wl_fixed_from_int(viewport_src.x()), - wl_fixed_from_int(viewport_src.y()), - wl_fixed_from_int(viewport_src.width()), - wl_fixed_from_int(viewport_src.height())); + if (viewport()) { + wp_viewport_set_source(viewport(), wl_fixed_from_int(viewport_src.x()), + wl_fixed_from_int(viewport_src.y()), + wl_fixed_from_int(viewport_src.width()), + wl_fixed_from_int(viewport_src.height())); + } } // Apply viewport scale (wp_viewport.set_destination). gfx::Size viewport_dst = bounds; @@ -162,8 +183,10 @@ void WaylandSurface::SetBufferScale(int32_t new_scale, bool update_bounds) { if (!display_size_px_.IsEmpty()) { gfx::Size viewport_dst = gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_); - wp_viewport_set_destination(viewport(), viewport_dst.width(), - viewport_dst.height()); + if (viewport()) { + wp_viewport_set_destination(viewport(), viewport_dst.width(), + viewport_dst.height()); + } } connection_->ScheduleFlush(); @@ -175,42 +198,83 @@ void WaylandSurface::SetOpaqueRegion(const gfx::Rect& region_px) { if (!root_window_ || !root_window_->IsOpaqueWindow()) return; - wl::Object<wl_region> region( - wl_compositor_create_region(connection_->compositor())); - gfx::Rect region_dip = - gfx::ScaleToEnclosingRect(region_px, 1.f / buffer_scale_); - wl_region_add(region.get(), region_dip.x(), region_dip.y(), - region_dip.width(), region_dip.height()); + wl_surface_set_opaque_region(surface_.get(), + CreateAndAddRegion(region_px).get()); + + connection_->ScheduleFlush(); +} + +void WaylandSurface::SetInputRegion(const gfx::Rect& region_px) { + // Don't set input region when use_native_frame is enabled. + if (!root_window_ || root_window_->ShouldUseNativeFrame()) + return; - wl_surface_set_opaque_region(surface_.get(), region.get()); + // Sets input region for input events to allow go through and + // for the compositor to ignore the parts of the input region that fall + // outside of the surface. + wl_surface_set_input_region(surface_.get(), + CreateAndAddRegion(region_px).get()); connection_->ScheduleFlush(); } +wl::Object<wl_region> WaylandSurface::CreateAndAddRegion( + const gfx::Rect& region_px) { + DCHECK(root_window_); + + wl::Object<wl_region> region( + wl_compositor_create_region(connection_->compositor())); + + auto window_shape_in_dips = root_window_->GetWindowShape(); + if (window_shape_in_dips.has_value()) { + for (const auto& rect : window_shape_in_dips.value()) + wl_region_add(region.get(), rect.x(), rect.y(), rect.width(), + rect.height()); + } else { + gfx::Rect region_dip = + gfx::ScaleToEnclosingRect(region_px, 1.f / buffer_scale_); + wl_region_add(region.get(), region_dip.x(), region_dip.y(), + region_dip.width(), region_dip.height()); + } + return region; +} + void WaylandSurface::SetViewportSource(const gfx::RectF& src_rect) { - if (src_rect == crop_rect_) { + if (src_rect == crop_rect_) return; - } else if (src_rect.IsEmpty() || src_rect == gfx::RectF{0.f, 0.f, 1.f, 1.f}) { - wp_viewport_set_source(viewport(), wl_fixed_from_int(-1), - wl_fixed_from_int(-1), wl_fixed_from_int(-1), - wl_fixed_from_int(-1)); + // |src_rect| {1.f, 1.f} does not apply cropping so set it to empty. + if (src_rect.IsEmpty() || src_rect == gfx::RectF{1.f, 1.f}) { + crop_rect_ = gfx::RectF(); + if (viewport()) { + wp_viewport_set_source(viewport(), wl_fixed_from_int(-1), + wl_fixed_from_int(-1), wl_fixed_from_int(-1), + wl_fixed_from_int(-1)); + } return; } + // wp_viewport_set_source() needs pixel inputs. Store |src_rect| and calculate + // in UpdateBufferDamageRegion(). crop_rect_ = src_rect; } void WaylandSurface::SetViewportDestination(const gfx::Size& dest_size_px) { - if (dest_size_px == display_size_px_) { + if (dest_size_px == display_size_px_) + return; + if (dest_size_px.IsEmpty()) { + display_size_px_ = gfx::Size(); + if (viewport()) { + wp_viewport_set_destination(viewport(), -1, -1); + } return; - } else if (dest_size_px.IsEmpty()) { - wp_viewport_set_destination(viewport(), -1, -1); } display_size_px_ = dest_size_px; gfx::Size viewport_dst = gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_); - wp_viewport_set_destination(viewport(), viewport_dst.width(), - viewport_dst.height()); + if (viewport()) { + wp_viewport_set_destination(viewport(), viewport_dst.width(), + viewport_dst.height()); + } } wl::Object<wl_subsurface> WaylandSurface::CreateSubsurface( diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h index c026bc22cb7..2f7284aeac3 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h @@ -9,6 +9,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/gpu_fence_handle.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/overlay_transform.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" @@ -29,6 +30,9 @@ class WaylandSurface { WaylandWindow* root_window() const { return root_window_; } wl_surface* surface() const { return surface_.get(); } wp_viewport* viewport() const { return viewport_.get(); } + zwp_linux_surface_synchronization_v1* surface_sync() const { + return surface_sync_.get(); + } int32_t buffer_scale() const { return buffer_scale_; } void set_buffer_scale(int32_t scale) { buffer_scale_ = scale; } @@ -47,6 +51,10 @@ class WaylandSurface { // (e.g: window/tab dragging sessions). void UnsetRootWindow(); + // Sets a non-null in-fence, must be combined with an AttachBuffer() and a + // Commit(). + void SetAcquireFence(const gfx::GpuFenceHandle& acquire_fence); + // Attaches the given wl_buffer to the underlying wl_surface at (0, 0). void AttachBuffer(wl_buffer* buffer); @@ -67,8 +75,16 @@ class WaylandSurface { // Sets the region that is opaque on this surface in physical pixels. This is // expected to be called whenever the region that the surface span changes or - // the opacity changes. - void SetOpaqueRegion(const gfx::Rect& bounds_px); + // the opacity changes. |region_px| is specified surface-local, in physical + // pixels. + void SetOpaqueRegion(const gfx::Rect& region_px); + + // Sets the input region on this surface in physical pixels. + // The input region indicates which parts of the surface accept pointer and + // touch input events. This is expected to be called from ToplevelWindow + // whenever the region that the surface span changes or window state changes + // when custom frame is used. + void SetInputRegion(const gfx::Rect& region_px); // Set the source rectangle of the associated wl_surface. // See: @@ -89,10 +105,13 @@ class WaylandSurface { wl::Object<wl_subsurface> CreateSubsurface(WaylandSurface* parent); private: + wl::Object<wl_region> CreateAndAddRegion(const gfx::Rect& region_px); + WaylandConnection* const connection_; WaylandWindow* root_window_ = nullptr; wl::Object<wl_surface> surface_; wl::Object<wp_viewport> viewport_; + wl::Object<zwp_linux_surface_synchronization_v1> surface_sync_; // Transformation for how the compositor interprets the contents of the // buffer. diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index f58063afdb3..8290a7ea497 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc @@ -9,23 +9,20 @@ #include "base/run_loop.h" #include "base/unguessable_token.h" #include "build/chromeos_buildflags.h" -#include "ui/base/dragdrop/drag_drop_types.h" -#include "ui/base/dragdrop/os_exchange_data.h" +#include "third_party/skia/include/core/SkPath.h" #include "ui/base/hit_test.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/shell_object_factory.h" -#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" +#include "ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" -#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h" #include "ui/ozone/platform/wayland/host/wayland_zaura_shell.h" #include "ui/platform_window/extensions/wayland_extension.h" -#include "ui/platform_window/wm/wm_drop_handler.h" -#if BUILDFLAG(IS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_LACROS) // TODO(jamescook): The nogncheck is to work around false-positive failures on // the code search bot. Remove after https://crrev.com/c/2432137 lands. #include "chromeos/crosapi/cpp/crosapi_constants.h" // nogncheck @@ -40,45 +37,41 @@ WaylandToplevelWindow::WaylandToplevelWindow(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, this); } -WaylandToplevelWindow::~WaylandToplevelWindow() { - if (drag_handler_delegate_) { - drag_handler_delegate_->OnDragFinished( - DragDropTypes::DragOperation::DRAG_NONE); - } - CancelDrag(); -} +WaylandToplevelWindow::~WaylandToplevelWindow() = default; -bool WaylandToplevelWindow::CreateShellSurface() { +bool WaylandToplevelWindow::CreateShellToplevel() { ShellObjectFactory factory; - shell_surface_ = factory.CreateShellSurfaceWrapper(connection(), this); - if (!shell_surface_) { - LOG(ERROR) << "Failed to create a ShellSurface."; + shell_toplevel_ = factory.CreateShellToplevelWrapper(connection(), this); + if (!shell_toplevel_) { + LOG(ERROR) << "Failed to create a ShellToplevel."; return false; } -#if BUILDFLAG(IS_LACROS) - shell_surface_->SetAppId(window_unique_id_); +#if BUILDFLAG(IS_CHROMEOS_LACROS) + shell_toplevel_->SetAppId(window_unique_id_); #else - shell_surface_->SetAppId(wm_class_class_); + shell_toplevel_->SetAppId(wm_class_class_); #endif - shell_surface_->SetTitle(window_title_); + shell_toplevel_->SetTitle(window_title_); SetSizeConstraints(); TriggerStateChanges(); + InitializeAuraShellSurface(); + OnDecorationModeChanged(); + // This could be the proper time to update window mask using + // NonClientView::GetWindowMask, since |non_client_view| is not created yet + // during the call to WaylandWindow::Initialize(). + UpdateWindowMask(); return true; } void WaylandToplevelWindow::ApplyPendingBounds() { if (pending_bounds_dip_.IsEmpty()) return; - DCHECK(shell_surface_); + DCHECK(shell_toplevel_); SetBoundsDip(pending_bounds_dip_); - shell_surface_->SetWindowGeometry(pending_bounds_dip_); pending_bounds_dip_ = gfx::Rect(); connection()->ScheduleFlush(); } @@ -86,47 +79,22 @@ void WaylandToplevelWindow::ApplyPendingBounds() { void WaylandToplevelWindow::DispatchHostWindowDragMovement( int hittest, const gfx::Point& pointer_location_in_px) { - DCHECK(shell_surface_); + DCHECK(shell_toplevel_); connection()->event_source()->ResetPointerFlags(); if (hittest == HTCAPTION) - shell_surface_->SurfaceMove(connection()); + shell_toplevel_->SurfaceMove(connection()); else - shell_surface_->SurfaceResize(connection(), hittest); + shell_toplevel_->SurfaceResize(connection(), hittest); connection()->ScheduleFlush(); } -bool WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data, - int operation, - gfx::NativeCursor cursor, - bool can_grab_pointer, - WmDragHandler::Delegate* delegate) { - DCHECK(!drag_handler_delegate_); - drag_handler_delegate_ = delegate; - connection()->data_drag_controller()->StartSession(data, operation); - - base::RunLoop drag_loop(base::RunLoop::Type::kNestableTasksAllowed); - drag_loop_quit_closure_ = drag_loop.QuitClosure(); - - auto alive = weak_ptr_factory_.GetWeakPtr(); - drag_loop.Run(); - if (!alive) - return false; - return true; -} - -void WaylandToplevelWindow::CancelDrag() { - if (drag_loop_quit_closure_.is_null()) - return; - std::move(drag_loop_quit_closure_).Run(); -} - void WaylandToplevelWindow::Show(bool inactive) { - if (shell_surface_) + if (shell_toplevel_) return; - if (!CreateShellSurface()) { + if (!CreateShellToplevel()) { Close(); return; } @@ -140,7 +108,7 @@ void WaylandToplevelWindow::Show(bool inactive) { } void WaylandToplevelWindow::Hide() { - if (!shell_surface_) + if (!shell_toplevel_) return; if (child_window()) { @@ -148,7 +116,7 @@ void WaylandToplevelWindow::Hide() { set_child_window(nullptr); } - shell_surface_.reset(); + shell_toplevel_.reset(); connection()->ScheduleFlush(); // Detach buffer from surface in order to completely shutdown menus and @@ -159,7 +127,7 @@ void WaylandToplevelWindow::Hide() { bool WaylandToplevelWindow::IsVisible() const { // X and Windows return true if the window is minimized. For consistency, do // the same. - return !!shell_surface_ || state_ == PlatformWindowState::kMinimized; + return !!shell_toplevel_ || state_ == PlatformWindowState::kMinimized; } void WaylandToplevelWindow::SetTitle(const base::string16& title) { @@ -168,8 +136,8 @@ void WaylandToplevelWindow::SetTitle(const base::string16& title) { window_title_ = title; - if (shell_surface_) { - shell_surface_->SetTitle(title); + if (shell_toplevel_) { + shell_toplevel_->SetTitle(title); connection()->ScheduleFlush(); } } @@ -203,7 +171,7 @@ void WaylandToplevelWindow::Minimize() { } void WaylandToplevelWindow::Restore() { - DCHECK(shell_surface_); + DCHECK(shell_toplevel_); // Differently from other platforms, under Wayland, unmaximizing the dragged // window before starting the drag loop is not needed as it is assumed to be @@ -222,30 +190,59 @@ PlatformWindowState WaylandToplevelWindow::GetPlatformWindowState() const { return state_; } +void WaylandToplevelWindow::Activate() { + // Only supported by compositors that support zaura_shell (e.g. exo). + // TODO(https://crbug.com/1175327): Use standard Wayland extensions, such as + // xdg-activation, when those are available. + if (aura_surface_) + zaura_surface_activate(aura_surface_.get()); +} + void WaylandToplevelWindow::SizeConstraintsChanged() { // Size constraints only make sense for normal windows. - if (!shell_surface_) + if (!shell_toplevel_) return; - DCHECK(delegate()); - min_size_ = delegate()->GetMinimumSizeForWindow(); - max_size_ = delegate()->GetMaximumSizeForWindow(); SetSizeConstraints(); } std::string WaylandToplevelWindow::GetWindowUniqueId() const { -#if BUILDFLAG(IS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_LACROS) return window_unique_id_; #else return std::string(); #endif } -void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width, - int32_t height, - bool is_maximized, - bool is_fullscreen, - bool is_activated) { +void WaylandToplevelWindow::SetUseNativeFrame(bool use_native_frame) { + if (use_native_frame_ == use_native_frame) + return; + use_native_frame_ = use_native_frame; + if (shell_toplevel_) + OnDecorationModeChanged(); + + UpdateWindowMask(); +} + +bool WaylandToplevelWindow::ShouldUseNativeFrame() const { + // This depends on availability of xdg-decoration protocol extension. + // Returns false if there is no xdg-decoration protocol extension provided + // even if use_native_frame_ is true. + return use_native_frame_ && const_cast<WaylandToplevelWindow*>(this) + ->connection() + ->xdg_decoration_manager_v1(); +} + +base::Optional<std::vector<gfx::Rect>> WaylandToplevelWindow::GetWindowShape() + const { + return window_shape_in_dips_; +} + +void WaylandToplevelWindow::HandleToplevelConfigure(int32_t width, + int32_t height, + bool is_maximized, + bool is_fullscreen, + bool is_activated) { // Store the old state to propagte state changes if Wayland decides to change // the state to something else. PlatformWindowState old_state = state_; @@ -259,9 +256,14 @@ void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width, state_ = PlatformWindowState::kNormal; } - const bool state_changed = old_state != state_; const bool is_normal = state_ == PlatformWindowState::kNormal; + bool did_send_delegate_notification = !!requested_window_show_state_count_; + if (requested_window_show_state_count_) + requested_window_show_state_count_--; + + const bool did_window_show_state_change = old_state != state_; + // Update state before notifying delegate. const bool did_active_change = is_active_ != is_activated; is_active_ = is_activated; @@ -287,7 +289,6 @@ void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width, gfx::ScaleToRoundedSize(GetRestoredBoundsInPixels().IsEmpty() ? GetBounds().size() : GetRestoredBoundsInPixels().size(), - 1.0 / buffer_scale())); } @@ -295,71 +296,60 @@ void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width, // It can be client or compositor side change from normal to something else. // Thus, we must store previous bounds to restore later. SetOrResetRestoredBounds(); - ApplyPendingBounds(); - if (state_changed) + if (did_window_show_state_change && !did_send_delegate_notification) { + previous_state_ = old_state; delegate()->OnWindowStateChanged(state_); + } if (did_active_change) delegate()->OnActivationChanged(is_active_); } -void WaylandToplevelWindow::OnDragEnter(const gfx::PointF& point, - std::unique_ptr<OSExchangeData> data, - int operation) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return; +void WaylandToplevelWindow::HandleSurfaceConfigure(uint32_t serial) { + if (pending_bounds_dip_.size() == + gfx::ScaleToRoundedSize(GetBounds().size(), 1.f / buffer_scale())) { + // If |pending_bounds_dip_| matches GetBounds(), then a frame matching + // |pending_bounds_dip_| may not arrive soon, despite the window delegate + // receives the updated bounds. Without a new frame, UpdateVisualSize() is + // not invoked, leaving this |configure| unacknowledged. + // E.g. With static window content, |configure| that does not + // change window size will not cause the window to redraw. + // Hence, acknowledge this |configure| now to tell the Wayland compositor + // that this window has been configured. + shell_toplevel()->SetWindowGeometry(pending_bounds_dip_); + shell_toplevel()->AckConfigure(serial); + } else { + // Otherwise, push the pending |configure| to |pending_configures_|, wait + // for a frame update, which will invoke UpdateVisualSize(). + DCHECK_LT(pending_configures_.size(), 25u); + pending_configures_.push_back({pending_bounds_dip_.size(), serial}); + } - // Wayland sends locations in DIP so they need to be translated to - // physical pixels. - // TODO(crbug.com/1102857): get the real event modifier here. - drop_handler->OnDragEnter( - gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data), - operation, - /*modifiers=*/0); + ApplyPendingBounds(); } -int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point, - int operation) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) - return 0; +void WaylandToplevelWindow::UpdateVisualSize(const gfx::Size& size_px) { + WaylandWindow::UpdateVisualSize(size_px); - // Wayland sends locations in DIP so they need to be translated to - // physical pixels. - // TODO(crbug.com/1102857): get the real event modifier here. - return drop_handler->OnDragMotion( - gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation, - /*modifiers=*/0); -} - -void WaylandToplevelWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) + if (!shell_toplevel_) return; - // TODO(crbug.com/1102857): get the real event modifier here. - drop_handler->OnDragDrop(std::move(data), /*modifiers=*/0); -} + auto size_dip = gfx::ScaleToRoundedSize(size_px, 1.f / buffer_scale()); + auto result = std::find_if( + pending_configures_.begin(), pending_configures_.end(), + [&size_dip](auto& configure) { return size_dip == configure.size_dip; }); -void WaylandToplevelWindow::OnDragLeave() { - WmDropHandler* drop_handler = GetWmDropHandler(*this); - if (!drop_handler) + if (result == pending_configures_.end()) return; - drop_handler->OnDragLeave(); -} -void WaylandToplevelWindow::OnDragSessionClose(uint32_t dnd_action) { - DCHECK(drag_handler_delegate_); - drag_handler_delegate_->OnDragFinished(dnd_action); - drag_handler_delegate_ = nullptr; - connection()->event_source()->ResetPointerFlags(); - std::move(drag_loop_quit_closure_).Run(); + shell_toplevel()->SetWindowGeometry(gfx::Rect(size_dip)); + shell_toplevel()->AckConfigure(result->serial); + pending_configures_.erase(pending_configures_.begin(), ++result); } bool WaylandToplevelWindow::OnInitialize( PlatformWindowInitProperties properties) { -#if BUILDFLAG(IS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_LACROS) auto token = base::UnguessableToken::Create(); window_unique_id_ = std::string(crosapi::kLacrosAppIdPrefix) + token.ToString(); @@ -368,7 +358,6 @@ bool WaylandToplevelWindow::OnInitialize( #endif SetWaylandExtension(this, static_cast<WaylandExtension*>(this)); SetWmMoveLoopHandler(this, static_cast<WmMoveLoopHandler*>(this)); - InitializeAuraShell(); return true; } @@ -391,34 +380,99 @@ void WaylandToplevelWindow::StartWindowDraggingSessionIfNeeded() { connection()->window_drag_controller()->StartDragSession(); } -void WaylandToplevelWindow::TriggerStateChanges() { - if (!shell_surface_) +void WaylandToplevelWindow::SetImmersiveFullscreenStatus(bool status) { + if (aura_surface_) { + auto mode = status ? ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE + : ZAURA_SURFACE_FULLSCREEN_MODE_PLAIN; + zaura_surface_set_fullscreen_mode(aura_surface_.get(), mode); + } else { + // TODO(https://crbug.com/1113900): Implement AuraShell support for + // non-browser windows and replace this if-else clause by a DCHECK. + NOTIMPLEMENTED_LOG_ONCE() + << "Implement AuraShell support for non-browser windows."; + } +} + +void WaylandToplevelWindow::ShowSnapPreview( + WaylandWindowSnapDirection snap_direction) { + if (aura_surface_ && zaura_surface_get_version(aura_surface_.get()) >= + ZAURA_SURFACE_INTENT_TO_SNAP_SINCE_VERSION) { + uint32_t zaura_shell_snap_direction = ZAURA_SURFACE_SNAP_DIRECTION_NONE; + switch (snap_direction) { + case WaylandWindowSnapDirection::kLeft: + zaura_shell_snap_direction = ZAURA_SURFACE_SNAP_DIRECTION_LEFT; + break; + case WaylandWindowSnapDirection::kRight: + zaura_shell_snap_direction = ZAURA_SURFACE_SNAP_DIRECTION_RIGHT; + break; + case WaylandWindowSnapDirection::kNone: + break; + } + zaura_surface_intent_to_snap(aura_surface_.get(), + zaura_shell_snap_direction); return; + } - if (state_ == PlatformWindowState::kFullScreen) - shell_surface_->SetFullscreen(); - else - shell_surface_->UnSetFullscreen(); + NOTIMPLEMENTED_LOG_ONCE() + << "Window snapping isn't available for non-lacros builds."; +} + +void WaylandToplevelWindow::CommitSnap( + WaylandWindowSnapDirection snap_direction) { + if (aura_surface_ && zaura_surface_get_version(aura_surface_.get()) >= + ZAURA_SURFACE_UNSET_SNAP_SINCE_VERSION) { + switch (snap_direction) { + case WaylandWindowSnapDirection::kLeft: + zaura_surface_set_snap_left(aura_surface_.get()); + return; + case WaylandWindowSnapDirection::kRight: + zaura_surface_set_snap_right(aura_surface_.get()); + return; + case WaylandWindowSnapDirection::kNone: + zaura_surface_unset_snap(aura_surface_.get()); + return; + } + } + + NOTIMPLEMENTED_LOG_ONCE() + << "Window snapping isn't available for non-lacros builds."; +} + +void WaylandToplevelWindow::TriggerStateChanges() { + if (!shell_toplevel_) + return; // Call UnSetMaximized only if current state is normal. Otherwise, if the // current state is fullscreen and the previous is maximized, calling // UnSetMaximized may result in wrong restored window position that clients // are not allowed to know about. - if (state_ == PlatformWindowState::kMaximized) - shell_surface_->SetMaximized(); - else if (state_ == PlatformWindowState::kNormal) - shell_surface_->UnSetMaximized(); + if (state_ == PlatformWindowState::kMinimized) { + shell_toplevel_->SetMinimized(); + } else if (state_ == PlatformWindowState::kFullScreen) { + shell_toplevel_->SetFullscreen(); + } else if (previous_state_ == PlatformWindowState::kFullScreen) { + shell_toplevel_->UnSetFullscreen(); + } else if (state_ == PlatformWindowState::kMaximized) { + shell_toplevel_->SetMaximized(); + } else if (state_ == PlatformWindowState::kNormal) { + shell_toplevel_->UnSetMaximized(); + } - if (state_ == PlatformWindowState::kMinimized) - shell_surface_->SetMinimized(); + delegate()->OnWindowStateChanged(state_); connection()->ScheduleFlush(); } void WaylandToplevelWindow::SetWindowState(PlatformWindowState state) { - previous_state_ = state_; - state_ = state; - TriggerStateChanges(); + if (state_ != state) { + previous_state_ = state_; + state_ = state; + + // Tracks this window show state change request, coming from the Browser. + requested_window_show_state_count_++; + + TriggerStateChanges(); + } } WmMoveResizeHandler* WaylandToplevelWindow::AsWmMoveResizeHandler() { @@ -426,10 +480,22 @@ WmMoveResizeHandler* WaylandToplevelWindow::AsWmMoveResizeHandler() { } void WaylandToplevelWindow::SetSizeConstraints() { - if (min_size_.has_value()) - shell_surface_->SetMinSize(min_size_->width(), min_size_->height()); - if (max_size_.has_value()) - shell_surface_->SetMaxSize(max_size_->width(), max_size_->height()); + DCHECK(delegate()); + + min_size_ = delegate()->GetMinimumSizeForWindow(); + max_size_ = delegate()->GetMaximumSizeForWindow(); + + if (min_size_.has_value()) { + auto min_size_dip = + gfx::ScaleToRoundedSize(min_size_.value(), 1.0f / buffer_scale()); + shell_toplevel_->SetMinSize(min_size_dip.width(), min_size_dip.height()); + } + + if (max_size_.has_value()) { + auto max_size_dip = + gfx::ScaleToRoundedSize(max_size_.value(), 1.0f / buffer_scale()); + shell_toplevel_->SetMaxSize(max_size_dip.width(), max_size_dip.height()); + } connection()->ScheduleFlush(); } @@ -447,14 +513,56 @@ void WaylandToplevelWindow::SetOrResetRestoredBounds() { } } -void WaylandToplevelWindow::InitializeAuraShell() { - if (connection()->zaura_shell()) { - DCHECK(!aura_surface_); +void WaylandToplevelWindow::InitializeAuraShellSurface() { + // InitializeAuraShellSurface() should be called after the XDG surface is + // initialized. + DCHECK(shell_toplevel_); + + if (connection()->zaura_shell() && !aura_surface_) { aura_surface_.reset(zaura_shell_get_aura_surface( connection()->zaura_shell()->wl_object(), root_surface()->surface())); - zaura_surface_set_fullscreen_mode(aura_surface_.get(), - ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE); + SetImmersiveFullscreenStatus(false); + } +} + +void WaylandToplevelWindow::OnDecorationModeChanged() { + DCHECK(shell_toplevel_); + if (use_native_frame_) { + // Set server-side decoration for windows using a native frame, + // e.g. taskmanager + shell_toplevel_->SetDecoration( + ShellToplevelWrapper::DecorationMode::kServerSide); + } else if (aura_surface_ && + zaura_surface_get_version(aura_surface_.get()) >= + ZAURA_SURFACE_SET_SERVER_START_RESIZE_SINCE_VERSION) { + // Sets custom-decoration mode for window that supports aura_shell. + // e.g. lacros-browser. + zaura_surface_set_server_start_resize(aura_surface_.get()); + } else { + shell_toplevel_->SetDecoration( + ShellToplevelWrapper::DecorationMode::kClientSide); + } +} + +void WaylandToplevelWindow::UpdateWindowMask() { + // TODO(http://crbug.com/1158733): When supporting PlatformWindow::SetShape, + // update window region with the given |shape|. + WaylandWindow::UpdateWindowMask(); + root_surface()->SetInputRegion(gfx::Rect(visual_size_px())); +} + +void WaylandToplevelWindow::UpdateWindowShape() { + // Create |window_shape_in_dips_| using the window mask of + // PlatformWindowDelegate otherwise resets it. + SkPath window_mask_in_pixels = + delegate()->GetWindowMaskForWindowShapeInPixels(); + if (window_mask_in_pixels.isEmpty()) { + window_shape_in_dips_.reset(); + return; } + SkPath window_mask_in_dips = + wl::ConvertPathToDIP(window_mask_in_pixels, buffer_scale()); + window_shape_in_dips_ = wl::CreateRectsFromSkPath(window_mask_in_dips); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h index 7f091d3f48f..849ee9ae60d 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h @@ -5,21 +5,20 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_ +#include "base/containers/circular_deque.h" #include "build/chromeos_buildflags.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" #include "ui/platform_window/extensions/wayland_extension.h" -#include "ui/platform_window/wm/wm_drag_handler.h" #include "ui/platform_window/wm/wm_move_loop_handler.h" #include "ui/platform_window/wm/wm_move_resize_handler.h" namespace ui { -class ShellSurfaceWrapper; +class ShellToplevelWrapper; class WaylandToplevelWindow : public WaylandWindow, public WmMoveResizeHandler, - public WmDragHandler, public WmMoveLoopHandler, public WaylandExtension { public: @@ -29,7 +28,7 @@ class WaylandToplevelWindow : public WaylandWindow, WaylandToplevelWindow& operator=(const WaylandToplevelWindow&) = delete; ~WaylandToplevelWindow() override; - ShellSurfaceWrapper* shell_surface() const { return shell_surface_.get(); } + ShellToplevelWrapper* shell_toplevel() const { return shell_toplevel_.get(); } // Apply the bounds specified in the most recent configure event. This should // be called after processing all pending events in the wayland connection. @@ -40,15 +39,7 @@ class WaylandToplevelWindow : public WaylandWindow, int hittest, const gfx::Point& pointer_location_in_px) override; - // WmDragHandler - bool StartDrag(const ui::OSExchangeData& data, - int operation, - gfx::NativeCursor cursor, - bool can_grab_pointer, - WmDragHandler::Delegate* delegate) override; - void CancelDrag() override; - - // PlatformWindow + // PlatformWindow: void Show(bool inactive) override; void Hide() override; bool IsVisible() const override; @@ -58,25 +49,33 @@ class WaylandToplevelWindow : public WaylandWindow, void Minimize() override; void Restore() override; PlatformWindowState GetPlatformWindowState() const override; + void Activate() override; void SizeConstraintsChanged() override; std::string GetWindowUniqueId() const override; + // SetUseNativeFrame and ShouldUseNativeFrame decide on + // xdg-decoration mode for a window. + void SetUseNativeFrame(bool use_native_frame) override; + bool ShouldUseNativeFrame() const override; + + // WaylandWindow overrides: + base::Optional<std::vector<gfx::Rect>> GetWindowShape() const override; private: // WaylandWindow overrides: - void HandleSurfaceConfigure(int32_t widht, - int32_t height, - bool is_maximized, - bool is_fullscreen, - bool is_activated) override; - void OnDragEnter(const gfx::PointF& point, - std::unique_ptr<OSExchangeData> data, - int operation) override; - int OnDragMotion(const gfx::PointF& point, int operation) override; - void OnDragDrop(std::unique_ptr<OSExchangeData> data) override; - void OnDragLeave() override; - void OnDragSessionClose(uint32_t dnd_action) override; + void HandleToplevelConfigure(int32_t width, + int32_t height, + bool is_maximized, + bool is_fullscreen, + bool is_activated) override; + void HandleSurfaceConfigure(uint32_t serial) override; + void UpdateVisualSize(const gfx::Size& size_px) override; bool OnInitialize(PlatformWindowInitProperties properties) override; bool IsActive() const override; + // Calls UpdateWindowShape, set_input_region and set_opaque_region + // for this toplevel window. + void UpdateWindowMask() override; + // Update the window shape using the window mask of PlatformWindowDelegate. + void UpdateWindowShape() override; // WmMoveLoopHandler: bool RunMoveLoop(const gfx::Vector2d& drag_offset) override; @@ -84,27 +83,32 @@ class WaylandToplevelWindow : public WaylandWindow, // WaylandExtension: void StartWindowDraggingSessionIfNeeded() override; + void SetImmersiveFullscreenStatus(bool status) override; + void ShowSnapPreview(WaylandWindowSnapDirection snap) override; + void CommitSnap(WaylandWindowSnapDirection snap) override; void TriggerStateChanges(); void SetWindowState(PlatformWindowState state); // Creates a surface window, which is visible as a main window. - bool CreateShellSurface(); + bool CreateShellToplevel(); WmMoveResizeHandler* AsWmMoveResizeHandler(); - // Propagates the |min_size_| and |max_size_| to the ShellSurface. + // Propagates the |min_size_| and |max_size_| to the ShellToplevel. void SetSizeConstraints(); void SetOrResetRestoredBounds(); - // Initializes the aura-shell EXO extension, if available. - void InitializeAuraShell(); + // Initializes the aura-shell surface, in the case aura-shell EXO extension + // is available. + void InitializeAuraShellSurface(); - // Wrappers around shell surface. - std::unique_ptr<ShellSurfaceWrapper> shell_surface_; + // Sets decoration mode for a window. + void OnDecorationModeChanged(); - WmDragHandler::Delegate* drag_handler_delegate_ = nullptr; + // Wrappers around shell surface. + std::unique_ptr<ShellToplevelWrapper> shell_toplevel_; // These bounds attributes below have suffices that indicate units used. // Wayland operates in DIP but the platform operates in physical pixels so @@ -125,7 +129,7 @@ class WaylandToplevelWindow : public WaylandWindow, bool is_active_ = false; -#if BUILDFLAG(IS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_LACROS) // Unique ID for this window. May be shared over non-Wayland IPC transports // (e.g. mojo) to identify the window. std::string window_unique_id_; @@ -137,18 +141,40 @@ class WaylandToplevelWindow : public WaylandWindow, std::string wm_class_class_; #endif - // Title of the ShellSurface. + // Title of the ShellToplevel. base::string16 window_title_; // Max and min sizes of the WaylandToplevelWindow window. base::Optional<gfx::Size> min_size_; base::Optional<gfx::Size> max_size_; - base::OnceClosure drag_loop_quit_closure_; - wl::Object<zaura_surface> aura_surface_; - base::WeakPtrFactory<WaylandToplevelWindow> weak_ptr_factory_{this}; + // When use_native_frame is false, client-side decoration is set, + // e.g. lacros-browser. + // When use_native_frame is true, server-side decoration is set, + // e.g. lacros-taskmanager. + bool use_native_frame_ = false; + + base::Optional<std::vector<gfx::Rect>> window_shape_in_dips_; + + // Pending xdg-shell configures, once this window is drawn to |size_dip|, + // ack_configure with |serial| will be sent to the Wayland compositor. + struct PendingConfigure { + gfx::Size size_dip; + uint32_t serial; + }; + base::circular_deque<PendingConfigure> pending_configures_; + + // Tracks how many the window show state requests by made by the Browser + // are currently being processed by the Wayland Compositor. In practice, + // each individual increment corresponds to an explicit window show state + // change request, and gets a response by the Compositor. + // + // This mechanism allows Ozone/Wayland to filter out notifying the delegate + // (PlatformWindowDelegate) more than once, for the same window show state + // change. + uint32_t requested_window_show_state_count_ = 0; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc index 6bb9802ed9a..8eba29721e8 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc @@ -22,14 +22,11 @@ WaylandTouch::WaylandTouch(wl_touch* touch, &WaylandTouch::Frame, &WaylandTouch::Cancel, }; - DCHECK(delegate_); - delegate_->OnTouchCreated(this); - wl_touch_add_listener(obj_.get(), &listener, this); } WaylandTouch::~WaylandTouch() { - delegate_->OnTouchDestroyed(this); + delegate_->OnTouchCancelEvent(); } void WaylandTouch::Down(void* data, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_touch.h b/chromium/ui/ozone/platform/wayland/host/wayland_touch.h index 14c9aaa0f78..4c3e3b7aaac 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_touch.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_touch.h @@ -61,8 +61,6 @@ class WaylandTouch { class WaylandTouch::Delegate { public: - virtual void OnTouchCreated(WaylandTouch* touch) = 0; - virtual void OnTouchDestroyed(WaylandTouch* touch) = 0; virtual void OnTouchPressEvent(WaylandWindow* window, const gfx::PointF& location, base::TimeTicks timestamp, diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc index f59e227aba9..edc21c70b89 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc @@ -4,27 +4,37 @@ #include "ui/ozone/platform/wayland/host/wayland_window.h" +#include <wayland-cursor.h> #include <algorithm> #include <memory> #include "base/bind.h" +#include "base/run_loop.h" #include "build/chromeos_buildflags.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/ozone/events_ozone.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/native_widget_types.h" #include "ui/ozone/common/features.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h" +#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h" +#include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include "ui/ozone/platform/wayland/host/wayland_subsurface.h" #include "ui/ozone/platform/wayland/host/wayland_zcr_cursor_shapes.h" #include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h" +#include "ui/platform_window/wm/wm_drag_handler.h" +#include "ui/platform_window/wm/wm_drop_handler.h" namespace { @@ -42,9 +52,13 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection) : delegate_(delegate), connection_(connection), - wayland_overlay_delegation_enabled_(IsWaylandOverlayDelegationEnabled()), + wayland_overlay_delegation_enabled_(connection->viewporter() && + IsWaylandOverlayDelegationEnabled()), accelerated_widget_( - connection->wayland_window_manager()->AllocateAcceleratedWidget()) {} + connection->wayland_window_manager()->AllocateAcceleratedWidget()) { + // Set a class property key, which allows |this| to be used for drag action. + SetWmDragHandler(this, this); +} WaylandWindow::~WaylandWindow() { shutting_down_ = true; @@ -97,11 +111,11 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) { else ui_scale_ = display.device_scale_factor(); } - // At this point, buffer_scale() still returns the old scale. - if (update_bounds) - SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / buffer_scale())); - + int32_t old_scale = buffer_scale(); root_surface_->SetBufferScale(new_scale, update_bounds); + // We need to keep DIP size of the window the same whenever the scale changes. + if (update_bounds) + SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / old_scale)); } gfx::AcceleratedWidget WaylandWindow::GetWidget() const { @@ -115,14 +129,46 @@ void WaylandWindow::SetPointerFocus(bool focus) { // cursor. Otherwise, it is invalidated whenever the pointer leaves the // surface and is not restored by the Wayland compositor. if (has_pointer_focus_ && bitmap_) { - // Translate physical pixels to DIPs. - gfx::Point hotspot_in_dips = - gfx::ScaleToRoundedPoint(bitmap_->hotspot(), 1.0f / ui_scale_); - connection_->SetCursorBitmap(bitmap_->bitmaps(), hotspot_in_dips, - buffer_scale()); + // Check for theme-provided cursor. + if (bitmap_->platform_data()) { + connection_->SetPlatformCursor( + reinterpret_cast<wl_cursor*>(bitmap_->platform_data()), + buffer_scale()); + } else { + // Translate physical pixels to DIPs. + gfx::Point hotspot_in_dips = + gfx::ScaleToRoundedPoint(bitmap_->hotspot(), 1.0f / ui_scale_); + connection_->SetCursorBitmap(bitmap_->bitmaps(), hotspot_in_dips, + buffer_scale()); + } } } +bool WaylandWindow::StartDrag(const ui::OSExchangeData& data, + int operation, + gfx::NativeCursor cursor, + bool can_grab_pointer, + WmDragHandler::Delegate* delegate) { + DCHECK(!drag_handler_delegate_); + drag_handler_delegate_ = delegate; + connection()->data_drag_controller()->StartSession(data, operation); + + base::RunLoop drag_loop(base::RunLoop::Type::kNestableTasksAllowed); + drag_loop_quit_closure_ = drag_loop.QuitClosure(); + + auto alive = weak_ptr_factory_.GetWeakPtr(); + drag_loop.Run(); + if (!alive) + return false; + return true; +} + +void WaylandWindow::CancelDrag() { + if (drag_loop_quit_closure_.is_null()) + return; + std::move(drag_loop_quit_closure_).Run(); +} + void WaylandWindow::Show(bool inactive) { if (background_buffer_id_ != 0u) should_attach_background_buffer_ = true; @@ -141,14 +187,18 @@ bool WaylandWindow::IsVisible() const { return false; } -void WaylandWindow::PrepareForShutdown() {} +void WaylandWindow::PrepareForShutdown() { + if (drag_handler_delegate_) + OnDragSessionClose(DragDropTypes::DRAG_NONE); +} void WaylandWindow::SetBounds(const gfx::Rect& bounds_px) { if (bounds_px_ == bounds_px) return; bounds_px_ = bounds_px; - root_surface_->SetOpaqueRegion(bounds_px); + if (update_visual_size_immediately_) + UpdateVisualSize(bounds_px.size()); delegate_->OnBoundsChanged(bounds_px_); } @@ -194,22 +244,20 @@ PlatformWindowState WaylandWindow::GetPlatformWindowState() const { return PlatformWindowState::kNormal; } -void WaylandWindow::Activate() { - NOTIMPLEMENTED_LOG_ONCE(); -} +void WaylandWindow::Activate() {} void WaylandWindow::Deactivate() { NOTIMPLEMENTED_LOG_ONCE(); } void WaylandWindow::SetUseNativeFrame(bool use_native_frame) { - // See comment below in ShouldUseNativeFrame. - NOTIMPLEMENTED_LOG_ONCE(); + // Do nothing here since only shell surfaces can handle server-side + // decoration. } bool WaylandWindow::ShouldUseNativeFrame() const { - // This depends on availability of XDG-Decoration protocol extension. - NOTIMPLEMENTED_LOG_ONCE(); + // Always returns false here since only shell surfaces can handle server-side + // decoration. return false; } @@ -227,6 +275,12 @@ void WaylandWindow::SetCursor(PlatformCursor cursor) { buffer_scale()); return; } + // Check for theme-provided cursor. + if (bitmap_->platform_data()) { + connection_->SetPlatformCursor( + reinterpret_cast<wl_cursor*>(bitmap_->platform_data()), buffer_scale()); + return; + } // Check for Wayland server-side cursor support (e.g. exo for lacros). if (connection_->zcr_cursor_shapes()) { base::Optional<int32_t> shape = @@ -282,6 +336,10 @@ void WaylandWindow::SetWindowIcons(const gfx::ImageSkia& window_icon, void WaylandWindow::SizeConstraintsChanged() {} +bool WaylandWindow::ShouldUseLayerForShapedWindow() const { + return true; +} + bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) { if (event->IsMouseEvent()) return has_pointer_focus_; @@ -304,6 +362,7 @@ uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) { // Wayland sends locations in DIP so they need to be translated to // physical pixels. + UpdateCursorPositionFromEvent(Event::Clone(*event)); event->AsLocatedEvent()->set_location_f(gfx::ScalePoint( event->AsLocatedEvent()->location_f(), buffer_scale(), buffer_scale())); @@ -333,36 +392,98 @@ uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) { return DispatchEventToDelegate(native_event); } -void WaylandWindow::HandleSurfaceConfigure(int32_t widht, - int32_t height, - bool is_maximized, - bool is_fullscreen, - bool is_activated) { +void WaylandWindow::HandleSurfaceConfigure(uint32_t serial) { NOTREACHED() << "Only shell surfaces must receive HandleSurfaceConfigure calls."; } +void WaylandWindow::HandleToplevelConfigure(int32_t widht, + int32_t height, + bool is_maximized, + bool is_fullscreen, + bool is_activated) { + NOTREACHED() + << "Only shell toplevels must receive HandleToplevelConfigure calls."; +} + void WaylandWindow::HandlePopupConfigure(const gfx::Rect& bounds_dip) { NOTREACHED() << "Only shell popups must receive HandlePopupConfigure calls."; } +void WaylandWindow::UpdateVisualSize(const gfx::Size& size_px) { + visual_size_px_ = size_px; + UpdateWindowMask(); +} + void WaylandWindow::OnCloseRequest() { delegate_->OnCloseRequest(); } +base::Optional<std::vector<gfx::Rect>> WaylandWindow::GetWindowShape() const { + return base::nullopt; +} + +void WaylandWindow::UpdateWindowMask() { + UpdateWindowShape(); + root_surface_->SetOpaqueRegion(gfx::Rect(visual_size_px())); +} + +void WaylandWindow::UpdateWindowShape() {} + void WaylandWindow::OnDragEnter(const gfx::PointF& point, std::unique_ptr<OSExchangeData> data, - int operation) {} + int operation) { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return; + + auto location_px = gfx::ScalePoint(TranslateLocationToRootWindow(point), + buffer_scale(), buffer_scale()); + + // Wayland sends locations in DIP so they need to be translated to + // physical pixels. + // TODO(crbug.com/1102857): get the real event modifier here. + drop_handler->OnDragEnter(location_px, std::move(data), operation, + /*modifiers=*/0); +} int WaylandWindow::OnDragMotion(const gfx::PointF& point, int operation) { - return -1; + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return 0; + + auto location_px = gfx::ScalePoint(TranslateLocationToRootWindow(point), + buffer_scale(), buffer_scale()); + + // Wayland sends locations in DIP so they need to be translated to + // physical pixels. + // TODO(crbug.com/1102857): get the real event modifier here. + return drop_handler->OnDragMotion(location_px, operation, + /*modifiers=*/0); } -void WaylandWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) {} +void WaylandWindow::OnDragDrop() { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return; + // TODO(crbug.com/1102857): get the real event modifier here. + drop_handler->OnDragDrop({}, /*modifiers=*/0); +} -void WaylandWindow::OnDragLeave() {} +void WaylandWindow::OnDragLeave() { + WmDropHandler* drop_handler = GetWmDropHandler(*this); + if (!drop_handler) + return; + drop_handler->OnDragLeave(); +} -void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) {} +void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) { + DCHECK(drag_handler_delegate_); + drag_handler_delegate_->OnDragFinished(dnd_action); + drag_handler_delegate_ = nullptr; + connection()->event_source()->ResetPointerFlags(); + std::move(drag_loop_quit_closure_).Run(); +} void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) { SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale())); @@ -403,7 +524,7 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { // Will do nothing for menus because they have got their scale above. UpdateBufferScale(false); - root_surface_->SetOpaqueRegion(bounds_px_); + root_surface_->SetOpaqueRegion(gfx::Rect(bounds_px_.size())); return true; } @@ -416,7 +537,7 @@ void WaylandWindow::AddEnteredOutputId(struct wl_output* output) { // Wayland does weird things for menus so instead of tracking outputs that // we entered or left, we take that from the parent window and ignore this // event. - if (wl::IsMenuType(type())) + if (wl::IsMenuType(type()) || type() == ui::PlatformWindowType::kTooltip) return; const uint32_t entered_output_id = @@ -480,8 +601,16 @@ void WaylandWindow::UpdateCursorPositionFromEvent( } } -WaylandWindow* WaylandWindow::GetTopLevelWindow() { - return parent_window_ ? parent_window_->GetTopLevelWindow() : this; +gfx::PointF WaylandWindow::TranslateLocationToRootWindow( + const gfx::PointF& location) { + auto* root_window = GetRootParentWindow(); + DCHECK(root_window); + if (root_window == this) + return location; + + gfx::Vector2d offset = + GetBounds().origin() - root_window->GetBounds().origin(); + return location + gfx::Vector2dF(offset); } WaylandWindow* WaylandWindow::GetTopMostChildWindow() { @@ -499,10 +628,6 @@ bool WaylandWindow::IsActive() const { uint32_t WaylandWindow::DispatchEventToDelegate( const PlatformEvent& native_event) { - auto* event = static_cast<Event*>(native_event); - if (event->IsLocatedEvent()) - UpdateCursorPositionFromEvent(Event::Clone(*event)); - bool handled = DispatchEventFromNativeUiEvent( native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, base::Unretained(delegate_))); @@ -584,6 +709,9 @@ bool WaylandWindow::CommitOverlays( if (!ArrangeSubsurfaceStack(above, below)) return false; + if (wayland_overlay_delegation_enabled_) + connection_->buffer_manager_host()->StartFrame(root_surface()); + { // Iterate through |subsurface_stack_below_|, setup subsurfaces and place // them in corresponding order. Commit wl_buffers once a subsurface is @@ -608,7 +736,9 @@ bool WaylandWindow::CommitOverlays( nullptr, reference_above); connection_->buffer_manager_host()->CommitBufferInternal( (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), - /*wait_for_frame_callback=*/false); + /*wait_for_frame_callback=*/true, + /*commit_synced_subsurface=*/true, + std::move((*overlay_iter)->access_fence_handle)); } else { // If there're more subsurfaces requested that we don't need at the // moment, hide them. @@ -637,7 +767,9 @@ bool WaylandWindow::CommitOverlays( reference_below, nullptr); connection_->buffer_manager_host()->CommitBufferInternal( (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), - /*wait_for_frame_callback=*/false); + /*wait_for_frame_callback=*/true, + /*commit_synced_subsurface=*/true, + std::move((*overlay_iter)->access_fence_handle)); } else { // If there're more subsurfaces requested that we don't need at the // moment, hide them. @@ -646,7 +778,13 @@ bool WaylandWindow::CommitOverlays( } } + if (!num_primary_planes && overlays.front()->z_order == INT32_MIN) + split = overlays.begin(); + UpdateVisualSize((*split)->bounds_rect.size()); + root_surface_->SetViewportDestination(visual_size_px_); + if (!wayland_overlay_delegation_enabled_) { + root_surface_->SetViewportSource((*split)->crop_rect); connection_->buffer_manager_host()->CommitBufferInternal( root_surface(), (*split)->buffer_id, (*split)->damage_region, /*wait_for_frame_callback=*/true); @@ -659,26 +797,27 @@ bool WaylandWindow::CommitOverlays( (*split)->enable_blend, nullptr, nullptr); connection_->buffer_manager_host()->CommitBufferInternal( primary_subsurface_->wayland_surface(), (*split)->buffer_id, - (*split)->damage_region, /*wait_for_frame_callback=*/false); + (*split)->damage_region, + /*wait_for_frame_callback=*/true, + /*commit_synced_subsurface=*/true, + std::move((*split)->access_fence_handle)); } - root_surface_->SetViewportDestination(bounds_px_.size()); - + gfx::Rect background_damage; if (overlays.front()->z_order == INT32_MIN) { background_buffer_id_ = overlays.front()->buffer_id; + background_damage = overlays.front()->damage_region; should_attach_background_buffer_ = true; } if (should_attach_background_buffer_) { - connection_->buffer_manager_host()->CommitBufferInternal( - root_surface(), background_buffer_id_, /*damage_region=*/gfx::Rect(), - /*wait_for_frame_callback=*/true); + connection_->buffer_manager_host()->EndFrame(background_buffer_id_, + background_damage); should_attach_background_buffer_ = false; } else { // Subsurfaces are set to sync, above surface configs will only take effect // when root_surface is committed. - connection_->buffer_manager_host()->CommitWithoutBufferInternal( - root_surface(), /*wait_for_frame_callback=*/true); + connection_->buffer_manager_host()->EndFrame(); } return true; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_window.h index e719914fdd4..8bcc007f766 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.h @@ -15,6 +15,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" @@ -23,10 +24,7 @@ #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" - -namespace gfx { -class PointF; -} +#include "ui/platform_window/wm/wm_drag_handler.h" namespace ui { @@ -38,7 +36,9 @@ class WaylandWindowDragController; using WidgetSubsurfaceSet = base::flat_set<std::unique_ptr<WaylandSubsurface>>; -class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { +class WaylandWindow : public PlatformWindow, + public PlatformEventDispatcher, + public WmDragHandler { public: ~WaylandWindow() override; @@ -111,6 +111,21 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Returns current type of the window. PlatformWindowType type() const { return type_; } + gfx::Size visual_size_px() const { return visual_size_px_; } + + // This is never intended to be used except in unit tests. + void set_update_visual_size_immediately(bool update_immediately) { + update_visual_size_immediately_ = update_immediately; + } + + // WmDragHandler + bool StartDrag(const ui::OSExchangeData& data, + int operation, + gfx::NativeCursor cursor, + bool can_grab_pointer, + WmDragHandler::Delegate* delegate) override; + void CancelDrag() override; + // PlatformWindow void Show(bool inactive) override; void Hide() override; @@ -142,6 +157,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { void SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) override; void SizeConstraintsChanged() override; + bool ShouldUseLayerForShapedWindow() const override; // PlatformEventDispatcher bool CanDispatchEvent(const PlatformEvent& event) override; @@ -150,12 +166,14 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Handles the configuration events coming from the shell objects. // The width and height come in DIP of the output that the surface is // currently bound to. - virtual void HandleSurfaceConfigure(int32_t widht, - int32_t height, - bool is_maximized, - bool is_fullscreen, - bool is_activated); + virtual void HandleSurfaceConfigure(uint32_t serial); + virtual void HandleToplevelConfigure(int32_t widht, + int32_t height, + bool is_maximized, + bool is_fullscreen, + bool is_activated); virtual void HandlePopupConfigure(const gfx::Rect& bounds); + virtual void UpdateVisualSize(const gfx::Size& size_px); // Handles close requests. virtual void OnCloseRequest(); @@ -165,10 +183,12 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { std::unique_ptr<OSExchangeData> data, int operation); virtual int OnDragMotion(const gfx::PointF& point, int operation); - virtual void OnDragDrop(std::unique_ptr<OSExchangeData> data); + virtual void OnDragDrop(); virtual void OnDragLeave(); virtual void OnDragSessionClose(uint32_t dnd_action); + virtual base::Optional<std::vector<gfx::Rect>> GetWindowShape() const; + // Returns a root parent window within the same hierarchy. WaylandWindow* GetRootParentWindow(); @@ -203,6 +223,9 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { void set_ui_scale(int32_t ui_scale) { ui_scale_ = ui_scale; } + // Calls set_opaque_region for this window. + virtual void UpdateWindowMask(); + private: FRIEND_TEST_ALL_PREFIXES(WaylandScreenTest, SetBufferScale); @@ -211,13 +234,15 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { void UpdateCursorPositionFromEvent(std::unique_ptr<Event> event); - WaylandWindow* GetTopLevelWindow(); + gfx::PointF TranslateLocationToRootWindow(const gfx::PointF& location); uint32_t DispatchEventToDelegate(const PlatformEvent& native_event); // Additional initialization of derived classes. virtual bool OnInitialize(PlatformWindowInitProperties properties) = 0; + virtual void UpdateWindowShape(); + // WaylandWindowDragController might need to take ownership of the wayland // surface whether the window that originated the DND session gets destroyed // in the middle of that session (e.g: when it is snapped into a tab strip). @@ -253,10 +278,23 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // The current cursor bitmap (immutable). scoped_refptr<BitmapCursorOzone> bitmap_; - // Current bounds of the platform window. + // Current bounds of the platform window. This is either initialized, or the + // requested size by the Wayland compositor. When this is set in SetBounds(), + // delegate_->OnBoundsChanged() is called and updates current_surface_size in + // Viz. However, it is not guaranteed that the next arriving frame will match + // |bounds_px_|. gfx::Rect bounds_px_; // The bounds of the platform window before it went maximized or fullscreen. gfx::Rect restored_bounds_px_; + // The size presented by the gpu process. This is the visible size of the + // window, which can be different from |bounds_px_| due to renderers taking + // time to produce a compositor frame. + // The rough flow of size changes: + // Wayland compositor -> xdg_surface.configure() + // -> WaylandWindow::SetBounds() -> IPC -> DisplayPrivate::Resize() + // -> OutputSurface::SwapBuffers() -> WaylandWindow::UpdateVisualSize() + // -> xdg_surface.ack_configure() -> Wayland compositor. + gfx::Size visual_size_px_; bool has_pointer_focus_ = false; bool has_keyboard_focus_ = false; @@ -285,9 +323,21 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Set when the window enters in shutdown process. bool shutting_down_ = false; + // In a non-test environment, a frame update makes a SetBounds() change + // visible in |visual_size_px_|, but in some unit tests there will never be + // any frame updates. This flag causes UpdateVisualSize() to be invoked during + // SetBounds() in unit tests. + bool update_visual_size_immediately_ = false; + // AcceleratedWidget for this window. This will be unique even over time. gfx::AcceleratedWidget accelerated_widget_; + WmDragHandler::Delegate* drag_handler_delegate_ = nullptr; + + base::OnceClosure drag_loop_quit_closure_; + + base::WeakPtrFactory<WaylandWindow> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); }; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc index 05f6bcc2ec7..3431631b596 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc @@ -36,7 +36,6 @@ #include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h" #include "ui/ozone/platform/wayland/host/wayland_data_offer.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" -#include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include "ui/ozone/platform/wayland/host/wayland_surface.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -142,11 +141,14 @@ bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window, RunLoop(); SetDraggedWindow(nullptr, {}); - DCHECK(state_ == State::kAttached || state_ == State::kDropped); - bool dropped = state_ == State::kDropped; - if (dropped) - HandleDropAndResetState(); - return dropped; + DCHECK(state_ == State::kAttaching || state_ == State::kDropped); + if (state_ == State::kAttaching) { + state_ = State::kAttached; + return false; + } + + HandleDropAndResetState(); + return true; } void WaylandWindowDragController::StopDragging() { @@ -158,7 +160,7 @@ void WaylandWindowDragController::StopDragging() { // This function is supposed to be called to indicate that the window was just // snapped into a tab strip. So switch to |kAttached| state, store the focused // window as the pointer grabber and ask to quit the nested loop. - state_ = State::kAttached; + state_ = State::kAttaching; pointer_grab_owner_ = window_manager_->GetCurrentFocusedWindow(); DCHECK(pointer_grab_owner_); QuitLoop(); @@ -209,7 +211,7 @@ void WaylandWindowDragController::OnDragEnter(WaylandWindow* window, DCHECK_EQ(data_offer_->mime_types().front(), kMimeTypeChromiumWindow); // Accept the offer and set the dnd action. - data_offer_->SetAction(kDndActionWindowDrag, kDndActionWindowDrag); + data_offer_->SetActions(kDndActionWindowDrag); data_offer_->Accept(serial, kMimeTypeChromiumWindow); } @@ -217,6 +219,11 @@ void WaylandWindowDragController::OnDragMotion(const gfx::PointF& location) { DCHECK_GE(state_, State::kAttached); VLOG(2) << "OnMotion. location=" << location.ToString(); + // Motion events are not expected to be dispatched while waiting for the drag + // loop to exit, ie: kAttaching transitional state. See crbug.com/1169446. + if (state_ == State::kAttaching) + return; + // Forward cursor location update info to the input handling delegate. should_process_drag_event_ = true; pointer_location_ = location; @@ -225,7 +232,6 @@ void WaylandWindowDragController::OnDragMotion(const gfx::PointF& location) { void WaylandWindowDragController::OnDragLeave() { DCHECK_GE(state_, State::kAttached); - DCHECK_LE(state_, State::kDetached); // In order to guarantee ET_MOUSE_RELEASED event is delivered once the DND // session finishes, the focused window is not reset here. This is similar to @@ -324,8 +330,6 @@ uint32_t WaylandWindowDragController::DispatchEvent( DCHECK_EQ(state_, State::kDetached); DCHECK(base::CurrentUIThread::IsSet()); - VLOG(2) << "Dispatch. event=" << event->GetName(); - if (event->type() == ET_MOUSE_MOVED || event->type() == ET_MOUSE_DRAGGED) { HandleMotionEvent(event->AsMouseEvent()); return POST_DISPATCH_STOP_PROPAGATION; @@ -348,7 +352,6 @@ void WaylandWindowDragController::OnToplevelWindowCreated( << " calculated_offset=" << offset.ToString(); SetDraggedWindow(window, offset); - state_ = State::kDetached; } void WaylandWindowDragController::OnWindowRemoved(WaylandWindow* window) { @@ -395,7 +398,6 @@ void WaylandWindowDragController::HandleDropAndResetState() { VLOG(1) << "Notifying drop. window=" << pointer_grab_owner_; EventFlags pointer_button = EF_LEFT_MOUSE_BUTTON; - DCHECK(connection_->event_source()->IsPointerButtonPressed(pointer_button)); pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button, pointer_grab_owner_); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h index 94dae03f336..96da9124b5c 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h @@ -46,10 +46,11 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, public: // Constants used to keep track of the drag controller state. enum class State { - kIdle, // No DnD session nor drag loop running. - kAttached, // DnD session ongoing but no drag loop running. - kDetached, // Drag loop running. ie: blocked in a Drag() call. - kDropped // Drop event was just received. + kIdle, // No DnD session nor drag loop running. + kAttached, // DnD session ongoing but no drag loop running. + kDetached, // Drag loop running. ie: blocked in a Drag() call. + kDropped, // Drop event was just received. + kAttaching, // About to transition back to |kAttached|. }; WaylandWindowDragController(WaylandConnection* connection, @@ -153,8 +154,10 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate, std::unique_ptr<ScopedEventDispatcher> nested_dispatcher_; base::OnceClosure quit_loop_closure_; - // Tells if the current drag event should be processedc. E.g: received through - // wl_data_device::motion wayland event. + // Tells if the current drag event should be processed. Buggy compositors may + // send wl_pointer::motion events, for example, while a DND session is still + // in progress, which leads to issues in window dragging sessions, this flag + // is used to make window drag controller resistant to such scenarios. bool should_process_drag_event_ = false; base::WeakPtrFactory<WaylandWindowDragController> weak_factory_{this}; diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc index db3944812a3..79f66eb7db1 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc @@ -10,6 +10,7 @@ #include <cstdint> +#include "base/bind.h" #include "base/notreached.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/base_event_utils.h" @@ -30,7 +31,7 @@ #include "ui/ozone/platform/wayland/test/test_data_offer.h" #include "ui/ozone/platform/wayland/test/test_data_source.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" -#include "ui/ozone/platform/wayland/test/wayland_test.h" +#include "ui/ozone/platform/wayland/test/wayland_drag_drop_test.h" #include "ui/ozone/test/mock_platform_window_delegate.h" #include "ui/platform_window/extensions/wayland_extension.h" #include "ui/platform_window/platform_window_delegate.h" @@ -41,14 +42,14 @@ using testing::Mock; namespace ui { -class WaylandWindowDragControllerTest : public WaylandTest, - public wl::TestDataDevice::Delegate { +class WaylandWindowDragControllerTest : public WaylandDragDropTest { public: WaylandWindowDragControllerTest() = default; ~WaylandWindowDragControllerTest() override = default; void SetUp() override { - WaylandTest::SetUp(); + WaylandDragDropTest::SetUp(); + screen_ = std::make_unique<WaylandScreen>(connection_.get()); wl_seat_send_capabilities(server_.seat()->resource(), @@ -59,16 +60,6 @@ class WaylandWindowDragControllerTest : public WaylandTest, EXPECT_FALSE(window_->has_pointer_focus()); EXPECT_EQ(State::kIdle, drag_controller()->state()); - - data_device_manager_ = server_.data_device_manager(); - DCHECK(data_device_manager_); - - source_ = nullptr; - data_device_manager_->data_device()->set_delegate(this); - } - - void TearDown() override { - data_device_manager_->data_device()->set_delegate(nullptr); } WaylandWindowDragController* drag_controller() const { @@ -79,52 +70,11 @@ class WaylandWindowDragControllerTest : public WaylandTest, return connection_->wayland_window_manager(); } - uint32_t NextSerial() const { - static uint32_t serial = 0; - return ++serial; - } - - uint32_t NextTime() const { - static uint32_t timestamp = 0; - return ++timestamp; - } + MockPlatformWindowDelegate& delegate() { return delegate_; } protected: using State = WaylandWindowDragController::State; - // wl::TestDataDevice::Delegate: - void StartDrag(wl::TestDataSource* source, - wl::MockSurface* origin, - uint32_t serial) override { - EXPECT_FALSE(source_); - source_ = source; - OfferAndEnter(origin); - } - - // Helper functions - void SendDndMotion(const gfx::Point& location) { - EXPECT_TRUE(source_); - wl_fixed_t x = wl_fixed_from_int(location.x()); - wl_fixed_t y = wl_fixed_from_int(location.y()); - data_device_manager_->data_device()->OnMotion(NextTime(), x, y); - } - - void SendDndEnter(WaylandWindow* window) { - EXPECT_TRUE(window); - OfferAndEnter(server_.GetObject<wl::MockSurface>( - window->root_surface()->GetSurfaceId())); - } - - void SendDndLeave() { - EXPECT_TRUE(source_); - data_device_manager_->data_device()->OnLeave(); - } - - void SendDndDrop() { - EXPECT_TRUE(source_); - source_->OnCancelled(); - } - void SendPointerEnter(WaylandWindow* window, MockPlatformWindowDelegate* delegate) { auto* surface = server_.GetObject<wl::MockSurface>( @@ -181,24 +131,17 @@ class WaylandWindowDragControllerTest : public WaylandTest, screen_->GetLocalProcessWidgetAtPoint(location, {})); } - void OfferAndEnter(wl::MockSurface* surface) { - EXPECT_TRUE(source_); - auto* data_device = data_device_manager_->data_device(); - auto* offer = data_device->OnDataOffer(); - EXPECT_EQ(1u, source_->mime_types().size()); - for (const auto& mime_type : source_->mime_types()) - offer->OnOffer(mime_type, {}); - - wl_data_device_send_enter(data_device->resource(), NextSerial(), - surface->resource(), 0, 0, offer->resource()); - } + // For the context of window drag, "drop" is detected through + // wl_data_source::cancelled in the regular case. Unless extended-drag + // protocol is available. + // + // TODO(crbug.com/1116431): Support extended-drag in test compositor. + void SendDndDrop() { SendDndCancelled(); } // client objects std::unique_ptr<WaylandScreen> screen_; // server objects - wl::TestDataDeviceManager* data_device_manager_; - wl::TestDataSource* source_; wl::MockPointer* pointer_; }; @@ -471,7 +414,7 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) { // Exit |source_window| and enter the |target_window|. SendDndLeave(); - SendDndEnter(target_window); + SendDndEnter(target_window, {}); test_step = kEnteredTarget; }); @@ -723,6 +666,65 @@ TEST_P(WaylandWindowDragControllerTest, IgnorePointerEventsUntilDrop) { screen_->GetLocalProcessWidgetAtPoint({20, 20}, {})); } +// Regression test for https://crbug.com/1169446. +TEST_P(WaylandWindowDragControllerTest, MotionEventsSkippedWhileReattaching) { + auto* dragged_window = window_.get(); + EXPECT_TRUE(dragged_window); + + SendPointerEnter(dragged_window, &delegate_); + SendPointerPress(dragged_window, &delegate_, BTN_LEFT); + SendPointerMotion(dragged_window, &delegate_, {10, 10}); + + auto* wayland_extension = GetWaylandExtension(*window_); + wayland_extension->StartWindowDraggingSessionIfNeeded(); + EXPECT_EQ(State::kAttached, drag_controller()->state()); + + auto* move_loop_handler = GetWmMoveLoopHandler(*window_); + ASSERT_TRUE(move_loop_handler); + + auto test = [](WaylandWindowDragControllerTest* self, + WmMoveLoopHandler* move_loop_handler) { + // While in |kDetached| state, motion events are expected to be propagated + // by window drag controller. + EXPECT_EQ(State::kDetached, self->drag_controller()->state()); + self->SendDndMotion({30, 30}); + EXPECT_CALL(self->delegate(), DispatchEvent(_)).Times(1); + self->Sync(); + + move_loop_handler->EndMoveLoop(); + self->Sync(); + + // Otherwise, after the move loop is requested to quit, but before it really + // ends (ie. kAttaching state), motion events are **not** expected to be + // propagated. + EXPECT_EQ(State::kAttaching, self->drag_controller()->state()); + self->SendDndMotion({30, 30}); + EXPECT_CALL(self->delegate(), DispatchEvent(_)).Times(0); + self->Sync(); + }; + ScheduleTestTask(base::BindOnce(test, base::Unretained(this), + base::Unretained(move_loop_handler))); + + // Spins move loop for |window_1|. + move_loop_handler->RunMoveLoop({}); + + // When the transition to |kAttached| state is finally done (ie. nested loop + // quits), motion events are then expected to be propagated by window drag + // controller as usual. + EXPECT_EQ(State::kAttached, drag_controller()->state()); + SendDndMotion({30, 30}); + EXPECT_CALL(delegate(), DispatchEvent(_)).Times(1); + Sync(); + + SendDndDrop(); + EXPECT_CALL(delegate(), DispatchEvent(_)).Times(1); + Sync(); + + SendPointerEnter(window_.get(), &delegate_); + + EXPECT_EQ(State::kIdle, drag_controller()->state()); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandWindowDragControllerTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc index 7cca6ba6a82..90f2b76a2b0 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc @@ -4,12 +4,14 @@ #include <memory> +#include "base/compiler_specific.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/host/wayland_auxiliary_window.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_popup.h" #include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/platform_window/platform_window_init_properties.h" namespace ui { @@ -20,32 +22,32 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create( PlatformWindowInitProperties properties) { std::unique_ptr<WaylandWindow> window; switch (properties.type) { - case PlatformWindowType::kMenu: case PlatformWindowType::kPopup: if (connection->IsDragInProgress()) { // We are in the process of drag and requested a popup. Most probably, // it is an arrow window. window = std::make_unique<WaylandAuxiliaryWindow>(delegate, connection); + break; + } + FALLTHROUGH; + case PlatformWindowType::kMenu: + // Set the parent window in advance otherwise it is not possible to know + // if the popup is able to find one and if WaylandWindow::Initialize() + // fails or not. Otherwise, WaylandWindow::Create() returns nullptr and + // makes the browser to fail. To fix this problem, search for the parent + // window and if one is not found, create WaylandToplevelWindow instead. + // It's also worth noting that searching twice (one time here and + // another by WaylandPopup) is a bad practice, and the parent window is + // set here instead. TODO(crbug.com/1078328): Feed ozone/wayland with full + // layout info required to properly position popup windows. + if (auto* parent_window = + connection->wayland_window_manager()->FindParentForNewWindow( + properties.parent_widget)) { + window = std::make_unique<WaylandPopup>(delegate, connection); + window->set_parent_window(parent_window); } else { - auto* parent_window = - connection->wayland_window_manager()->FindParentForNewWindow( - properties.parent_widget); - if (parent_window) { - // Set the parent window in advance otherwise it is not possible to - // know if the WaylandPopup is able to find one and if - // WaylandWindow::Initialize() fails or not. Otherwise, - // WaylandWindow::Create() returns nullptr and makes the browser to - // fail. To fix this problem, search for the parent window and if - // one is not found, create WaylandToplevelWindow instead. It's - // also worth noting that searching twice (one time here and another - // by WaylandPopup) is a bad practice, and the parent window is set - // here instead. - window = std::make_unique<WaylandPopup>(delegate, connection); - window->set_parent_window(parent_window); - } else { - window = - std::make_unique<WaylandToplevelWindow>(delegate, connection); - } + DLOG(WARNING) << "Failed to determine for menu/popup window."; + window = std::make_unique<WaylandToplevelWindow>(delegate, connection); } break; case PlatformWindowType::kTooltip: diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index 458f6b5f0a4..f6be452b0a1 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc @@ -34,6 +34,7 @@ #include "ui/ozone/platform/wayland/test/mock_pointer.h" #include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/test_keyboard.h" +#include "ui/ozone/platform/wayland/test/test_output.h" #include "ui/ozone/platform/wayland/test/test_region.h" #include "ui/ozone/platform/wayland/test/test_touch.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" @@ -44,7 +45,9 @@ #include "ui/platform_window/wm/wm_move_resize_handler.h" using ::testing::_; +using ::testing::DoAll; using ::testing::Eq; +using ::testing::InvokeWithoutArgs; using ::testing::Mock; using ::testing::Return; using ::testing::SaveArg; @@ -270,6 +273,146 @@ TEST_P(WaylandWindowTest, SetTitle) { window_->SetTitle(base::ASCIIToUTF16("hello")); } +TEST_P(WaylandWindowTest, UpdateVisualSizeConfiguresWaylandWindow) { + const auto kNormalBounds = gfx::Rect{0, 0, 500, 300}; + uint32_t serial = 0; + auto state = InitializeWlArrayWithActivatedState(); + + window_->set_update_visual_size_immediately(false); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + + // Configure event makes Wayland update bounds, but does not change toplevel + // input region, opaque region or window geometry immediately. Such actions + // are postponed to UpdateVisualSize(); + EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(), + kNormalBounds.height())) + .Times(0); + EXPECT_CALL(*xdg_surface_, AckConfigure(1)).Times(0); + EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)).Times(0); + EXPECT_CALL(*mock_surface, SetInputRegion(_)).Times(0); + SendConfigureEvent(xdg_surface_, kNormalBounds.width(), + kNormalBounds.height(), ++serial, state.get()); + + Sync(); + + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(), + kNormalBounds.height())); + EXPECT_CALL(*xdg_surface_, AckConfigure(1)); + EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)); + EXPECT_CALL(*mock_surface, SetInputRegion(_)); + window_->UpdateVisualSize(kNormalBounds.size()); +} + +TEST_P(WaylandWindowTest, ShuffledUpdateVisualSizeOrder) { + const auto kNormalBounds1 = gfx::Rect{0, 0, 500, 300}; + const auto kNormalBounds2 = gfx::Rect{0, 0, 800, 600}; + const auto kNormalBounds3 = gfx::Rect{0, 0, 700, 400}; + uint32_t serial = 1; + + window_->set_update_visual_size_immediately(false); + + // Send 3 configures and only ack the second one, the first pending configure + // is cleared. The second can still be ack'ed. + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds1.width(), + kNormalBounds1.height())) + .Times(0); + EXPECT_CALL(*xdg_surface_, AckConfigure(2)).Times(0); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds2.width(), + kNormalBounds2.height())); + EXPECT_CALL(*xdg_surface_, AckConfigure(3)); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds3.width(), + kNormalBounds3.height())); + EXPECT_CALL(*xdg_surface_, AckConfigure(4)); + + auto state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds1.width(), + kNormalBounds1.height(), ++serial, state.get()); + state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds2.width(), + kNormalBounds2.height(), ++serial, state.get()); + state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds3.width(), + kNormalBounds3.height(), ++serial, state.get()); + Sync(); + + window_->UpdateVisualSize(kNormalBounds2.size()); + window_->UpdateVisualSize(kNormalBounds1.size()); + window_->UpdateVisualSize(kNormalBounds3.size()); +} + +TEST_P(WaylandWindowTest, MismatchUpdateVisualSize) { + const auto kNormalBounds1 = gfx::Rect{0, 0, 500, 300}; + const auto kNormalBounds2 = gfx::Rect{0, 0, 800, 600}; + const auto kNormalBounds3 = gfx::Rect{0, 0, 700, 400}; + uint32_t serial = 1; + + window_->set_update_visual_size_immediately(false); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + + // UpdateVisualSize with different size from configure events does not + // acknowledge toplevel configure. + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(_, _, _, _)).Times(0); + EXPECT_CALL(*xdg_surface_, AckConfigure(_)).Times(0); + EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)); + EXPECT_CALL(*mock_surface, SetInputRegion(_)); + + auto state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds1.width(), + kNormalBounds1.height(), ++serial, state.get()); + state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds2.width(), + kNormalBounds2.height(), ++serial, state.get()); + state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds3.width(), + kNormalBounds3.height(), ++serial, state.get()); + Sync(); + + window_->UpdateVisualSize({100, 100}); +} + +TEST_P(WaylandWindowTest, UpdateVisualSizeClearsPreviousUnackedConfigures) { + const auto kNormalBounds1 = gfx::Rect{0, 0, 500, 300}; + const auto kNormalBounds2 = gfx::Rect{0, 0, 800, 600}; + const auto kNormalBounds3 = gfx::Rect{0, 0, 700, 400}; + uint32_t serial = 1; + auto state = InitializeWlArrayWithActivatedState(); + + window_->set_update_visual_size_immediately(false); + + // Send 3 configures and only ack the second one, the first pending configure + // is cleared. The second can still be ack'ed. + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds1.width(), + kNormalBounds1.height())) + .Times(0); + EXPECT_CALL(*xdg_surface_, AckConfigure(2)).Times(0); + SendConfigureEvent(xdg_surface_, kNormalBounds1.width(), + kNormalBounds1.height(), ++serial, state.get()); + Sync(); + + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds2.width(), + kNormalBounds2.height())); + EXPECT_CALL(*xdg_surface_, AckConfigure(3)); + state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds2.width(), + kNormalBounds2.height(), ++serial, state.get()); + Sync(); + + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds3.width(), + kNormalBounds3.height())); + EXPECT_CALL(*xdg_surface_, AckConfigure(4)); + state = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, kNormalBounds3.width(), + kNormalBounds3.height(), ++serial, state.get()); + Sync(); + + window_->UpdateVisualSize(kNormalBounds2.size()); + window_->UpdateVisualSize(kNormalBounds1.size()); + window_->UpdateVisualSize(kNormalBounds3.size()); +} + TEST_P(WaylandWindowTest, MaximizeAndRestore) { const auto kNormalBounds = gfx::Rect{0, 0, 500, 300}; const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600}; @@ -295,7 +438,7 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { kMaximizedBounds.height())); EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds)); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->Maximize(); SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(), kMaximizedBounds.height(), ++serial, @@ -326,7 +469,7 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(), kNormalBounds.height())); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); EXPECT_CALL(delegate_, OnActivationChanged(_)).Times(0); EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); EXPECT_CALL(*GetXdgToplevel(), UnsetMaximized()); @@ -346,7 +489,7 @@ TEST_P(WaylandWindowTest, Minimize) { Sync(); EXPECT_CALL(*GetXdgToplevel(), SetMinimized()); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->Minimize(); EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized); @@ -363,8 +506,14 @@ TEST_P(WaylandWindowTest, Minimize) { // Send one additional empty configuration event (which means the surface is // not maximized, fullscreen or activated) to ensure, WaylandWindow stays in - // the same minimized state and doesn't notify its delegate. - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + // the same minimized state, but the delegate is always notified. + // + // TODO(tonikito): Improve filtering of delegate notification here. + ui::PlatformWindowState state; + EXPECT_CALL(delegate_, OnWindowStateChanged(_)) + .WillRepeatedly(DoAll(SaveArg<0>(&state), InvokeWithoutArgs([&]() { + EXPECT_EQ(state, PlatformWindowState::kMinimized); + }))); SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get()); Sync(); @@ -384,7 +533,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); EXPECT_CALL(*GetXdgToplevel(), SetFullscreen()); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->ToggleFullscreen(); // Make sure than WaylandWindow manually handles fullscreen states. Check the // comment in the WaylandWindow::ToggleFullscreen. @@ -394,7 +543,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { Sync(); EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen()); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->Restore(); EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); // Reinitialize wl_array, which removes previous old states. @@ -467,12 +616,11 @@ TEST_P(WaylandWindowTest, StartMaximized) { // Make sure the window is initialized to normal state from the beginning. EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); - // The state must not be changed to the fullscreen before the surface is - // activated. + // The state gets changed to maximize and the delegate notified. auto* mock_surface = server_.GetObject<wl::MockSurface>( window->root_surface()->GetSurfaceId()); EXPECT_FALSE(mock_surface->xdg_surface()); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->Maximize(); // The state of the window must already be fullscreen one. @@ -480,8 +628,8 @@ TEST_P(WaylandWindowTest, StartMaximized) { Sync(); - // Once the surface will be activated, the window state mustn't be changed - // and retain the same. + // Window show state should be already up to date, so delegate is not + // notified. EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized); @@ -521,6 +669,8 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) { EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(), normal_bounds.height())); + Sync(); + // Now, set to fullscreen. AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); SendConfigureEvent(xdg_surface_, 2005, 2005, 3, states.get()); @@ -598,7 +748,7 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { kMaximizedBounds.height())); EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds)); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->Maximize(); // State changes are synchronous. EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState()); @@ -614,7 +764,7 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(), kMaximizedBounds.height())); EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->ToggleFullscreen(); // State changes are synchronous. EXPECT_EQ(PlatformWindowState::kFullScreen, @@ -632,9 +782,8 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(), kNormalBounds.height())); EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen()); - EXPECT_CALL(*GetXdgToplevel(), UnsetMaximized()); EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); - EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1); window_->Restore(); EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); // Reinitialize wl_array, which removes previous old states. @@ -808,6 +957,51 @@ TEST_P(WaylandWindowTest, SendsBoundsOnRequest) { EXPECT_EQ(restored_bounds, gfx::Rect()); } +TEST_P(WaylandWindowTest, UpdateWindowRegion) { + wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + + // Change bounds. + const gfx::Rect initial_bounds = window_->GetBounds(); + const gfx::Rect new_bounds = gfx::Rect(0, 0, initial_bounds.width() + 10, + initial_bounds.height() + 10); + EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)).Times(1); + EXPECT_CALL(*mock_surface, SetInputRegion(_)).Times(1); + window_->SetBounds(new_bounds); + Sync(); + VerifyAndClearExpectations(); + EXPECT_EQ(mock_surface->opaque_region(), new_bounds); + EXPECT_EQ(mock_surface->input_region(), new_bounds); + + // Maximize. + ScopedWlArray states = InitializeWlArrayWithActivatedState(); + EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)).Times(1); + EXPECT_CALL(*mock_surface, SetInputRegion(_)).Times(1); + const gfx::Rect maximized_bounds = gfx::Rect(0, 0, 1024, 768); + window_->Maximize(); + AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); + SendConfigureEvent(xdg_surface_, maximized_bounds.width(), + maximized_bounds.height(), 1, states.get()); + Sync(); + VerifyAndClearExpectations(); + EXPECT_EQ(mock_surface->opaque_region(), maximized_bounds); + EXPECT_EQ(mock_surface->input_region(), maximized_bounds); + + // Restore. + const gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels(); + EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)).Times(1); + EXPECT_CALL(*mock_surface, SetInputRegion(_)).Times(1); + window_->Restore(); + // Reinitialize wl_array, which removes previous old states. + auto active = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(xdg_surface_, 0, 0, 2, active.get()); + Sync(); + VerifyAndClearExpectations(); + + EXPECT_EQ(mock_surface->opaque_region(), restored_bounds); + EXPECT_EQ(mock_surface->input_region(), restored_bounds); +} + TEST_P(WaylandWindowTest, CanDispatchMouseEventDefault) { EXPECT_FALSE(window_->CanDispatchEvent(&test_mouse_event_)); } @@ -1397,6 +1591,102 @@ TEST_P(WaylandWindowTest, DispatchWindowResize) { } } +TEST_P(WaylandWindowTest, ToplevelWindowUpdateBufferScale) { + VerifyAndClearExpectations(); + + // Buffer scale must be 1 when no output has been entered by the window. + EXPECT_EQ(1, window_->buffer_scale()); + + // Creating an output with scale 1. + wl::TestOutput* output1 = server_.CreateAndInitializeOutput(); + output1->SetRect(gfx::Rect(0, 0, 1920, 1080)); + output1->SetScale(1); + Sync(); + + // Creating an output with scale 2. + wl::TestOutput* output2 = server_.CreateAndInitializeOutput(); + output2->SetRect(gfx::Rect(0, 0, 1920, 1080)); + output2->SetScale(2); + Sync(); + + // Send the window to |output1|. + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + ASSERT_TRUE(surface); + wl_surface_send_enter(surface->resource(), output1->resource()); + Sync(); + + // The window's scale and bounds must remain unchanged. + EXPECT_EQ(1, window_->buffer_scale()); + EXPECT_EQ(gfx::Rect(0, 0, 800, 600), window_->GetBounds()); + + // Simulating drag process from |output1| to |output2|. + wl_surface_send_enter(surface->resource(), output2->resource()); + wl_surface_send_leave(surface->resource(), output1->resource()); + Sync(); + + // The window must change its scale and bounds to keep DIP bounds the same. + EXPECT_EQ(2, window_->buffer_scale()); + EXPECT_EQ(gfx::Rect(0, 0, 1600, 1200), window_->GetBounds()); +} + +TEST_P(WaylandWindowTest, AuxiliaryWindowUpdateBufferScale) { + VerifyAndClearExpectations(); + + // Creating an output with scale 1. + wl::TestOutput* output1 = server_.CreateAndInitializeOutput(); + output1->SetRect(gfx::Rect(0, 0, 1920, 1080)); + output1->SetScale(1); + Sync(); + + // Creating an output with scale 2. + wl::TestOutput* output2 = server_.CreateAndInitializeOutput(); + output2->SetRect(gfx::Rect(0, 0, 1920, 1080)); + output2->SetScale(2); + Sync(); + + // Send the window to |output1|. + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + ASSERT_TRUE(surface); + wl_surface_send_enter(surface->resource(), output1->resource()); + Sync(); + + // Creating a tooltip on |window_|. + window_->SetPointerFocus(true); + gfx::Rect subsurface_bounds(15, 15, 10, 10); + std::unique_ptr<WaylandWindow> auxiliary_window = + CreateWaylandWindowWithParams(PlatformWindowType::kTooltip, + gfx::kNullAcceleratedWidget, + subsurface_bounds, &delegate_); + EXPECT_TRUE(auxiliary_window); + + auxiliary_window->Show(false); + + // |auxiliary_window| should inherit its buffer scale from the focused window. + EXPECT_EQ(1, auxiliary_window->buffer_scale()); + EXPECT_EQ(subsurface_bounds, auxiliary_window->GetBounds()); + auxiliary_window->Hide(); + + // Send the window to |output2|. + wl_surface_send_enter(surface->resource(), output2->resource()); + wl_surface_send_leave(surface->resource(), output1->resource()); + Sync(); + + EXPECT_EQ(2, window_->buffer_scale()); + auxiliary_window->Show(false); + + // |auxiliary_window|'s scale and bounds must change whenever its parents + // scale is changed. + EXPECT_EQ(2, window_->buffer_scale()); + EXPECT_EQ(2, auxiliary_window->buffer_scale()); + EXPECT_EQ(gfx::ScaleToRoundedRect(subsurface_bounds, 2), + auxiliary_window->GetBounds()); + + auxiliary_window->Hide(); + window_->SetPointerFocus(false); +} + // Tests WaylandWindow repositions menu windows to be relative to parent window // in a right way. TEST_P(WaylandWindowTest, AdjustPopupBounds) { @@ -1408,26 +1698,29 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) { ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | ZXDG_POSITIONER_V6_ANCHOR_RIGHT, ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT, ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X | - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y}; + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y}; nested_menu_window_positioner = { gfx::Rect(4, 80, 279, 1), gfx::Size(305, 99), ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_RIGHT, ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT, ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X | - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y}; + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y | + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y}; } else { - menu_window_positioner = {gfx::Rect(439, 46, 1, 30), gfx::Size(287, 409), - XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT, - XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT, - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y}; - + menu_window_positioner = { + gfx::Rect(439, 46, 1, 30), gfx::Size(287, 409), + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y}; nested_menu_window_positioner = { gfx::Rect(4, 80, 279, 1), gfx::Size(305, 99), XDG_POSITIONER_ANCHOR_TOP_RIGHT, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT, XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y}; + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y}; } auto* toplevel_window = window_.get(); @@ -1590,6 +1883,7 @@ TEST_P(WaylandWindowTest, SetOpaqueRegion) { Sync(); VerifyAndClearExpectations(); + EXPECT_EQ(mock_surface->opaque_region(), new_bounds); new_bounds.set_size(gfx::Size(1000, 534)); SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 2, @@ -1601,6 +1895,7 @@ TEST_P(WaylandWindowTest, SetOpaqueRegion) { Sync(); VerifyAndClearExpectations(); + EXPECT_EQ(mock_surface->opaque_region(), new_bounds); } TEST_P(WaylandWindowTest, OnCloseRequest) { @@ -1617,23 +1912,16 @@ TEST_P(WaylandWindowTest, OnCloseRequest) { TEST_P(WaylandWindowTest, AuxiliaryWindowSimpleParent) { VerifyAndClearExpectations(); - std::unique_ptr<WaylandWindow> second_window = CreateWaylandWindowWithParams( - PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget, - gfx::Rect(0, 0, 640, 480), &delegate_); - EXPECT_TRUE(second_window); - - // Test case 1: if the subsurface is provided with a parent widget, it must - // always use that as a parent. + // Auxiliary window must ignore the parent provided by aura and should always + // use focused window instead. gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10)); std::unique_ptr<WaylandWindow> auxiliary_window = CreateWaylandWindowWithParams(PlatformWindowType::kTooltip, - window_->GetWidget(), subsurface_bounds, - &delegate_); + gfx::kNullAcceleratedWidget, + subsurface_bounds, &delegate_); EXPECT_TRUE(auxiliary_window); - // The subsurface mustn't take the focused window as a parent, but use the - // provided one. - second_window->SetPointerFocus(true); + window_->SetPointerFocus(true); auxiliary_window->Show(false); Sync(); @@ -1644,6 +1932,8 @@ TEST_P(WaylandWindowTest, AuxiliaryWindowSimpleParent) { EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin()); EXPECT_FALSE(test_subsurface->sync()); + EXPECT_EQ(mock_surface_subsurface->opaque_region(), + gfx::Rect(subsurface_bounds.size())); auto* parent_resource = server_ @@ -1651,47 +1941,7 @@ TEST_P(WaylandWindowTest, AuxiliaryWindowSimpleParent) { ->resource(); EXPECT_EQ(parent_resource, test_subsurface->parent_resource()); - // Test case 2: the subsurface must use the focused window as its parent. - auxiliary_window = CreateWaylandWindowWithParams( - PlatformWindowType::kTooltip, gfx::kNullAcceleratedWidget, - subsurface_bounds, &delegate_); - EXPECT_TRUE(auxiliary_window); - - // The tooltip must take the focused window. - second_window->SetPointerFocus(true); - auxiliary_window->Show(false); - - Sync(); - - // Get new surface after recreating the WaylandAuxiliaryWindow. - mock_surface_subsurface = server_.GetObject<wl::MockSurface>( - auxiliary_window->root_surface()->GetSurfaceId()); - test_subsurface = mock_surface_subsurface->sub_surface(); - - auto* second_parent_resource = - server_ - .GetObject<wl::MockSurface>( - second_window->root_surface()->GetSurfaceId()) - ->resource(); - EXPECT_EQ(second_parent_resource, test_subsurface->parent_resource()); - auxiliary_window->Hide(); - - Sync(); - - // The subsurface must take the focused window. - second_window->SetPointerFocus(false); - window_->SetPointerFocus(true); - auxiliary_window->Show(false); - - Sync(); - - // The subsurface is invalidated on each Hide call. - test_subsurface = mock_surface_subsurface->sub_surface(); - - // The |window_|'s resource must be the parent resource. - EXPECT_EQ(parent_resource, test_subsurface->parent_resource()); - window_->SetPointerFocus(false); } @@ -1850,6 +2100,7 @@ TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) { EXPECT_TRUE(menu_window); VerifyAndClearExpectations(); + menu_window->SetPointerFocus(true); gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10)); std::unique_ptr<WaylandWindow> auxiliary_window = @@ -1860,8 +2111,6 @@ TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) { VerifyAndClearExpectations(); - menu_window->SetPointerFocus(true); - auxiliary_window->Show(false); Sync(); @@ -1873,6 +2122,8 @@ TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) { auto new_origin = subsurface_bounds.origin() - menu_window_bounds.origin().OffsetFromOrigin(); EXPECT_EQ(test_subsurface->position(), new_origin); + EXPECT_EQ(mock_surface_subsurface->opaque_region(), + gfx::Rect(subsurface_bounds.size())); menu_window->SetPointerFocus(false); } @@ -1969,7 +2220,7 @@ TEST_P(WaylandWindowTest, RemovesReattachesBackgroundOnHideShow) { EXPECT_TRUE(connection_->buffer_manager_host()); auto interface_ptr = connection_->buffer_manager_host()->BindInterface(); - buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, false); + buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, false, false); // Setup wl_buffers. constexpr uint32_t buffer_id1 = 1; @@ -2078,8 +2329,12 @@ TEST_P(WaylandWindowTest, SetsPropertiesOnShow) { // Now, propagate size constraints and title. base::Optional<gfx::Size> min_size(gfx::Size(1, 1)); base::Optional<gfx::Size> max_size(gfx::Size(100, 100)); - EXPECT_CALL(delegate, GetMinimumSizeForWindow()).WillOnce(Return(min_size)); - EXPECT_CALL(delegate, GetMaximumSizeForWindow()).WillOnce(Return(max_size)); + EXPECT_CALL(delegate, GetMinimumSizeForWindow()) + .Times(2) + .WillRepeatedly(Return(min_size)); + EXPECT_CALL(delegate, GetMaximumSizeForWindow()) + .Times(2) + .WillRepeatedly(Return(max_size)); EXPECT_CALL(*mock_xdg_toplevel, SetMinSize(min_size.value().width(), min_size.value().height())); diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc index 518a1a7cce6..2f926a008a2 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc +++ b/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc @@ -21,12 +21,17 @@ WaylandZAuraShell::WaylandZAuraShell(zaura_shell* aura_shell, static const zaura_shell_listener zaura_shell_listener = { &WaylandZAuraShell::OnLayoutMode, + &WaylandZAuraShell::OnBugFix, }; zaura_shell_add_listener(obj_.get(), &zaura_shell_listener, this); } WaylandZAuraShell::~WaylandZAuraShell() = default; +bool WaylandZAuraShell::HasBugFix(uint32_t id) { + return bug_fix_ids_.find(id) != bug_fix_ids_.end(); +} + // static void WaylandZAuraShell::OnLayoutMode(void* data, struct zaura_shell* zaura_shell, @@ -47,4 +52,12 @@ void WaylandZAuraShell::OnLayoutMode(void* data, } } +// static +void WaylandZAuraShell::OnBugFix(void* data, + struct zaura_shell* zaura_shell, + uint32_t id) { + auto* self = static_cast<WaylandZAuraShell*>(data); + self->bug_fix_ids_.insert(id); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.h b/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.h index 3057ff66ce8..9c0e8251b1b 100644 --- a/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.h +++ b/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell.h @@ -5,6 +5,9 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZAURA_SHELL_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZAURA_SHELL_H_ +#include <string> + +#include "base/containers/flat_set.h" #include "ui/ozone/platform/wayland/common/wayland_object.h" namespace ui { @@ -20,15 +23,24 @@ class WaylandZAuraShell { ~WaylandZAuraShell(); zaura_shell* wl_object() const { return obj_.get(); } + // Due to version skew between Lacros and Ash, there may be certain bug + // fixes in one but not in the other (crbug.com/1151508). Lacros can use + // |HasBugFix| to provide a temporary workaround to an exo bug until Ash + // uprevs and starts reporting that a given bug ID has been fixed. + bool HasBugFix(uint32_t id); private: - // zaura_shell_listener + // zaura_shell_listeners static void OnLayoutMode(void* data, struct zaura_shell* zaura_shell, uint32_t layout_mode); + static void OnBugFix(void* data, + struct zaura_shell* zaura_shell, + uint32_t id); wl::Object<zaura_shell> obj_; WaylandConnection* const connection_; + base::flat_set<uint32_t> bug_fix_ids_; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc new file mode 100644 index 00000000000..a8bba1eb24d --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/wayland_zaura_shell_unittest.cc @@ -0,0 +1,53 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <aura-shell-server-protocol.h> + +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_event_source.h" +#include "ui/ozone/platform/wayland/host/wayland_zaura_shell.h" +#include "ui/ozone/platform/wayland/test/global_object.h" +#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" + +namespace ui { + +namespace { + +constexpr uint32_t kXdgVersionStable = 7; +constexpr uint32_t kZAuraShellVersion = 14; + +} // namespace + +TEST(WaylandZauraShellTest, Foo) { + base::test::SingleThreadTaskEnvironment task_environment( + base::test::SingleThreadTaskEnvironment::MainThreadType::UI); + wl::TestWaylandServerThread server; + ASSERT_TRUE(server.Start(kXdgVersionStable)); + wl::GlobalObject zaura_shell_obj( + &zaura_shell_interface, nullptr /* implementation */, kZAuraShellVersion); + zaura_shell_obj.Initialize(server.display()); + + WaylandConnection connection; + ASSERT_TRUE(connection.Initialize()); + connection.event_source()->StartProcessingEvents(); + + base::RunLoop().RunUntilIdle(); + server.Pause(); + + zaura_shell_send_bug_fix(zaura_shell_obj.resource(), 1); + zaura_shell_send_bug_fix(zaura_shell_obj.resource(), 3); + + server.Resume(); + base::RunLoop().RunUntilIdle(); + server.Pause(); + + ASSERT_TRUE(connection.zaura_shell()->HasBugFix(1)); + ASSERT_TRUE(connection.zaura_shell()->HasBugFix(3)); + ASSERT_FALSE(connection.zaura_shell()->HasBugFix(2)); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc index cee4e46e6c1..c43f6c212fd 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc +++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc @@ -17,17 +17,17 @@ #include "ui/gfx/geometry/rect.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" -#include "ui/ozone/platform/wayland/host/wayland_event_source.h" #include "ui/ozone/platform/wayland/host/wayland_pointer.h" #include "ui/ozone/platform/wayland/host/wayland_popup.h" #include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" +#include "ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h" namespace ui { namespace { -uint32_t TranslateAnchorStable(WlAnchor anchor) { +uint32_t TranslateAnchor(WlAnchor anchor) { switch (anchor) { case WlAnchor::None: return XDG_POSITIONER_ANCHOR_NONE; @@ -50,7 +50,7 @@ uint32_t TranslateAnchorStable(WlAnchor anchor) { } } -uint32_t TranslateGravityStable(WlGravity gravity) { +uint32_t TranslateGravity(WlGravity gravity) { switch (gravity) { case WlGravity::None: return XDG_POSITIONER_GRAVITY_NONE; @@ -73,7 +73,7 @@ uint32_t TranslateGravityStable(WlGravity gravity) { } } -uint32_t TranslateContraintAdjustmentStable( +uint32_t TranslateContraintAdjustment( WlConstraintAdjustment constraint_adjustment) { uint32_t res = 0; if ((constraint_adjustment & WlConstraintAdjustment::SlideX) != @@ -97,175 +97,14 @@ uint32_t TranslateContraintAdjustmentStable( return res; } -uint32_t TranslateAnchorV6(WlAnchor anchor) { - switch (anchor) { - case WlAnchor::None: - return ZXDG_POSITIONER_V6_ANCHOR_NONE; - case WlAnchor::Top: - return ZXDG_POSITIONER_V6_ANCHOR_TOP; - case WlAnchor::Bottom: - return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; - case WlAnchor::Left: - return ZXDG_POSITIONER_V6_ANCHOR_LEFT; - case WlAnchor::Right: - return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - case WlAnchor::TopLeft: - return ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_LEFT; - case WlAnchor::BottomLeft: - return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | ZXDG_POSITIONER_V6_ANCHOR_LEFT; - case WlAnchor::TopRight: - return ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - case WlAnchor::BottomRight: - return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - } -} - -uint32_t TranslateGravityV6(WlGravity gravity) { - switch (gravity) { - case WlGravity::None: - return ZXDG_POSITIONER_V6_GRAVITY_NONE; - case WlGravity::Top: - return ZXDG_POSITIONER_V6_GRAVITY_TOP; - case WlGravity::Bottom: - return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; - case WlGravity::Left: - return ZXDG_POSITIONER_V6_GRAVITY_LEFT; - case WlGravity::Right: - return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - case WlGravity::TopLeft: - return ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_LEFT; - case WlGravity::BottomLeft: - return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_LEFT; - case WlGravity::TopRight: - return ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - case WlGravity::BottomRight: - return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - } -} - -uint32_t TranslateContraintAdjustmentV6( - WlConstraintAdjustment constraint_adjustment) { - uint32_t res = 0; - if ((constraint_adjustment & WlConstraintAdjustment::SlideX) != - WlConstraintAdjustment::None) - res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; - if ((constraint_adjustment & WlConstraintAdjustment::SlideY) != - WlConstraintAdjustment::None) - res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; - if ((constraint_adjustment & WlConstraintAdjustment::FlipX) != - WlConstraintAdjustment::None) - res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; - if ((constraint_adjustment & WlConstraintAdjustment::FlipY) != - WlConstraintAdjustment::None) - res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; - if ((constraint_adjustment & WlConstraintAdjustment::ResizeX) != - WlConstraintAdjustment::None) - res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; - if ((constraint_adjustment & WlConstraintAdjustment::ResizeY) != - WlConstraintAdjustment::None) - res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; - return res; -} - -uint32_t GetAnchor(MenuType menu_type, const gfx::Rect& bounds, bool stable) { - WlAnchor anchor = WlAnchor::None; - switch (menu_type) { - case MenuType::TYPE_RIGHT_CLICK: - anchor = WlAnchor::TopLeft; - break; - case MenuType::TYPE_3DOT_PARENT_MENU: - anchor = WlAnchor::BottomRight; - break; - case MenuType::TYPE_3DOT_CHILD_MENU: - // Chromium may want to manually position a child menu on the left side of - // its parent menu. Thus, react accordingly. Positive x means the child is - // located on the right side of the parent and negative - on the left - // side. - if (bounds.x() >= 0) - anchor = WlAnchor::TopRight; - else - anchor = WlAnchor::TopLeft; - break; - case MenuType::TYPE_UNKNOWN: - NOTREACHED() << "Unsupported menu type"; - break; - } - - if (stable) - return TranslateAnchorStable(anchor); - else { - return TranslateAnchorV6(anchor); - } -} - -uint32_t GetGravity(MenuType menu_type, const gfx::Rect& bounds, bool stable) { - WlGravity gravity = WlGravity::None; - switch (menu_type) { - case MenuType::TYPE_RIGHT_CLICK: - gravity = WlGravity::BottomRight; - break; - case MenuType::TYPE_3DOT_PARENT_MENU: - gravity = WlGravity::BottomRight; - break; - case MenuType::TYPE_3DOT_CHILD_MENU: - // Chromium may want to manually position a child menu on the left side of - // its parent menu. Thus, react accordingly. Positive x means the child is - // located on the right side of the parent and negative - on the left - // side. - if (bounds.x() >= 0) - gravity = WlGravity::BottomRight; - else - gravity = WlGravity::BottomLeft; - break; - case MenuType::TYPE_UNKNOWN: - NOTREACHED() << "Unsupported menu type"; - break; - } - - if (stable) - return TranslateGravityStable(gravity); - else { - return TranslateGravityV6(gravity); - } -} - -uint32_t GetConstraintAdjustment(MenuType menu_type, bool stable) { - WlConstraintAdjustment constraint = WlConstraintAdjustment::None; - - switch (menu_type) { - case MenuType::TYPE_RIGHT_CLICK: - constraint = WlConstraintAdjustment::SlideX | - WlConstraintAdjustment::SlideY | - WlConstraintAdjustment::FlipY; - break; - case MenuType::TYPE_3DOT_PARENT_MENU: - constraint = - WlConstraintAdjustment::SlideX | WlConstraintAdjustment::FlipY; - break; - case MenuType::TYPE_3DOT_CHILD_MENU: - constraint = - WlConstraintAdjustment::SlideY | WlConstraintAdjustment::FlipX; - break; - case MenuType::TYPE_UNKNOWN: - NOTREACHED() << "Unsupported menu type"; - break; - } - if (stable) - return TranslateContraintAdjustmentStable(constraint); - else { - return TranslateContraintAdjustmentV6(constraint); - } -} - } // namespace XDGPopupWrapperImpl::XDGPopupWrapperImpl( std::unique_ptr<XDGSurfaceWrapperImpl> surface, WaylandWindow* wayland_window) - : wayland_window_(wayland_window), xdg_surface_(std::move(surface)) { - DCHECK(xdg_surface_); + : wayland_window_(wayland_window), + xdg_surface_wrapper_(std::move(surface)) { + DCHECK(xdg_surface_wrapper_); DCHECK(wayland_window_ && wayland_window_->parent_window()); } @@ -286,15 +125,16 @@ bool XDGPopupWrapperImpl::Initialize(WaylandConnection* connection, static_cast<WaylandPopup*>(wayland_window_->parent_window()); XDGPopupWrapperImpl* popup = static_cast<XDGPopupWrapperImpl*>(wayland_popup->shell_popup()); - parent_xdg_surface = popup->xdg_surface(); + parent_xdg_surface = popup->xdg_surface_wrapper(); } else { WaylandToplevelWindow* wayland_surface = static_cast<WaylandToplevelWindow*>(wayland_window_->parent_window()); parent_xdg_surface = - static_cast<XDGSurfaceWrapperImpl*>(wayland_surface->shell_surface()); + static_cast<XDGToplevelWrapperImpl*>(wayland_surface->shell_toplevel()) + ->xdg_surface_wrapper(); } - if (!xdg_surface_ || !parent_xdg_surface) + if (!xdg_surface_wrapper_ || !parent_xdg_surface) return false; auto new_bounds = bounds; @@ -306,8 +146,6 @@ bool XDGPopupWrapperImpl::Initialize(WaylandConnection* connection, if (connection->shell()) return InitializeStable(connection, new_bounds, parent_xdg_surface); - else if (connection->shell_v6()) - return InitializeV6(connection, new_bounds, parent_xdg_surface); return false; } @@ -316,16 +154,16 @@ bool XDGPopupWrapperImpl::InitializeStable( const gfx::Rect& bounds, XDGSurfaceWrapperImpl* parent_xdg_surface) { static const struct xdg_popup_listener xdg_popup_listener = { - &XDGPopupWrapperImpl::ConfigureStable, - &XDGPopupWrapperImpl::PopupDoneStable, + &XDGPopupWrapperImpl::Configure, + &XDGPopupWrapperImpl::PopupDone, }; - struct xdg_positioner* positioner = CreatePositionerStable( - connection, wayland_window_->parent_window(), bounds); + struct xdg_positioner* positioner = + CreatePositioner(connection, wayland_window_->parent_window(), bounds); if (!positioner) return false; - xdg_popup_.reset(xdg_surface_get_popup(xdg_surface_->xdg_surface(), + xdg_popup_.reset(xdg_surface_get_popup(xdg_surface_wrapper_->xdg_surface(), parent_xdg_surface->xdg_surface(), positioner)); if (!xdg_popup_) @@ -342,7 +180,12 @@ bool XDGPopupWrapperImpl::InitializeStable( return true; } -struct xdg_positioner* XDGPopupWrapperImpl::CreatePositionerStable( +void XDGPopupWrapperImpl::AckConfigure(uint32_t serial) { + DCHECK(xdg_surface_wrapper_); + xdg_surface_wrapper_->AckConfigure(serial); +} + +struct xdg_positioner* XDGPopupWrapperImpl::CreatePositioner( WaylandConnection* connection, WaylandWindow* parent_window, const gfx::Rect& bounds) { @@ -366,115 +209,19 @@ struct xdg_positioner* XDGPopupWrapperImpl::CreatePositionerStable( xdg_positioner_set_anchor_rect(positioner, anchor_rect.x(), anchor_rect.y(), anchor_rect.width(), anchor_rect.height()); xdg_positioner_set_size(positioner, bounds.width(), bounds.height()); - xdg_positioner_set_anchor(positioner, GetAnchor(menu_type, bounds, true)); - xdg_positioner_set_gravity(positioner, GetGravity(menu_type, bounds, true)); + xdg_positioner_set_anchor(positioner, + TranslateAnchor(GetAnchor(menu_type, bounds))); + xdg_positioner_set_gravity(positioner, + TranslateGravity(GetGravity(menu_type, bounds))); xdg_positioner_set_constraint_adjustment( - positioner, GetConstraintAdjustment(menu_type, true)); + positioner, + TranslateContraintAdjustment(GetConstraintAdjustment(menu_type))); return positioner; } -bool XDGPopupWrapperImpl::InitializeV6( - WaylandConnection* connection, - const gfx::Rect& bounds, - XDGSurfaceWrapperImpl* parent_xdg_surface) { - static const struct zxdg_popup_v6_listener zxdg_popup_v6_listener = { - &XDGPopupWrapperImpl::ConfigureV6, - &XDGPopupWrapperImpl::PopupDoneV6, - }; - - zxdg_positioner_v6* positioner = - CreatePositionerV6(connection, wayland_window_->parent_window(), bounds); - if (!positioner) - return false; - - zxdg_popup_v6_.reset(zxdg_surface_v6_get_popup( - xdg_surface_->zxdg_surface(), parent_xdg_surface->zxdg_surface(), - positioner)); - if (!zxdg_popup_v6_) - return false; - - zxdg_positioner_v6_destroy(positioner); - - if (CanGrabPopup(connection)) { - zxdg_popup_v6_grab(zxdg_popup_v6_.get(), connection->seat(), - connection->serial()); - } - zxdg_popup_v6_add_listener(zxdg_popup_v6_.get(), &zxdg_popup_v6_listener, - this); - - wayland_window_->root_surface()->Commit(); - return true; -} - -zxdg_positioner_v6* XDGPopupWrapperImpl::CreatePositionerV6( - WaylandConnection* connection, - WaylandWindow* parent_window, - const gfx::Rect& bounds) { - struct zxdg_positioner_v6* positioner; - positioner = zxdg_shell_v6_create_positioner(connection->shell_v6()); - if (!positioner) - return nullptr; - - auto menu_type = GetMenuTypeForPositioner(connection, parent_window); - - // The parent we got must be the topmost in the stack of the same family - // windows. - DCHECK_EQ(parent_window->GetTopMostChildWindow(), parent_window); - - // Place anchor to the end of the possible position. - gfx::Rect anchor_rect = GetAnchorRect( - menu_type, bounds, - gfx::ScaleToRoundedRect(parent_window->GetBounds(), - 1.0 / parent_window->buffer_scale())); - - zxdg_positioner_v6_set_anchor_rect(positioner, anchor_rect.x(), - anchor_rect.y(), anchor_rect.width(), - anchor_rect.height()); - zxdg_positioner_v6_set_size(positioner, bounds.width(), bounds.height()); - zxdg_positioner_v6_set_anchor(positioner, - GetAnchor(menu_type, bounds, false)); - zxdg_positioner_v6_set_gravity(positioner, - GetGravity(menu_type, bounds, false)); - zxdg_positioner_v6_set_constraint_adjustment( - positioner, GetConstraintAdjustment(menu_type, false)); - return positioner; -} - -MenuType XDGPopupWrapperImpl::GetMenuTypeForPositioner( - WaylandConnection* connection, - WaylandWindow* parent_window) const { - bool is_right_click_menu = - connection->event_source()->last_pointer_button_pressed() & - EF_RIGHT_MOUSE_BUTTON; - - // Different types of menu require different anchors, constraint adjustments, - // gravity and etc. - if (is_right_click_menu) - return MenuType::TYPE_RIGHT_CLICK; - else if (!wl::IsMenuType(parent_window->type())) - return MenuType::TYPE_3DOT_PARENT_MENU; - else - return MenuType::TYPE_3DOT_CHILD_MENU; -} - -bool XDGPopupWrapperImpl::CanGrabPopup(WaylandConnection* connection) const { - // When drag process starts, as described the protocol - - // https://goo.gl/1Mskq3, the client must have an active implicit grab. If - // we try to create a popup and grab it, it will be immediately dismissed. - // Thus, do not take explicit grab during drag process. - if (connection->IsDragInProgress() || !connection->seat()) - return false; - - // According to the definition of the xdg protocol, the grab request must be - // used in response to some sort of user action like a button press, key - // press, or touch down event. - EventType last_event_type = connection->event_serial().event_type; - return last_event_type == ET_TOUCH_PRESSED || - last_event_type == ET_KEY_PRESSED || - last_event_type == ET_MOUSE_PRESSED; -} - +// static void XDGPopupWrapperImpl::Configure(void* data, + struct xdg_popup* xdg_popup, int32_t x, int32_t y, int32_t width, @@ -492,7 +239,7 @@ void XDGPopupWrapperImpl::Configure(void* data, } // static -void XDGPopupWrapperImpl::PopupDone(void* data) { +void XDGPopupWrapperImpl::PopupDone(void* data, struct xdg_popup* xdg_popup) { WaylandWindow* window = static_cast<XDGPopupWrapperImpl*>(data)->wayland_window_; DCHECK(window); @@ -500,41 +247,9 @@ void XDGPopupWrapperImpl::PopupDone(void* data) { window->OnCloseRequest(); } -// static -void XDGPopupWrapperImpl::ConfigureStable(void* data, - struct xdg_popup* xdg_popup, - int32_t x, - int32_t y, - int32_t width, - int32_t height) { - Configure(data, x, y, width, height); -} - -// static -void XDGPopupWrapperImpl::PopupDoneStable(void* data, - struct xdg_popup* xdg_popup) { - PopupDone(data); -} - -// static -void XDGPopupWrapperImpl::ConfigureV6(void* data, - struct zxdg_popup_v6* zxdg_popup_v6, - int32_t x, - int32_t y, - int32_t width, - int32_t height) { - Configure(data, x, y, width, height); -} - -// static -void XDGPopupWrapperImpl::PopupDoneV6(void* data, - struct zxdg_popup_v6* zxdg_popup_v6) { - PopupDone(data); -} - -XDGSurfaceWrapperImpl* XDGPopupWrapperImpl::xdg_surface() { - DCHECK(xdg_surface_.get()); - return xdg_surface_.get(); +XDGSurfaceWrapperImpl* XDGPopupWrapperImpl::xdg_surface_wrapper() const { + DCHECK(xdg_surface_wrapper_.get()); + return xdg_surface_wrapper_.get(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h index 62f38a2dc6a..0ffd6674836 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h +++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h @@ -16,7 +16,7 @@ class XDGSurfaceWrapperImpl; class WaylandConnection; class WaylandWindow; -// Popup wrapper for xdg-shell stable and xdg-shell-unstable-v6 +// Popup wrapper for xdg-shell stable class XDGPopupWrapperImpl : public ShellPopupWrapper { public: XDGPopupWrapperImpl(std::unique_ptr<XDGSurfaceWrapperImpl> surface, @@ -26,61 +26,35 @@ class XDGPopupWrapperImpl : public ShellPopupWrapper { // XDGPopupWrapper: bool Initialize(WaylandConnection* connection, const gfx::Rect& bounds) override; + void AckConfigure(uint32_t serial) override; private: bool InitializeStable(WaylandConnection* connection, const gfx::Rect& bounds, - XDGSurfaceWrapperImpl* parent_xdg_surface); - struct xdg_positioner* CreatePositionerStable(WaylandConnection* connection, - WaylandWindow* parent_window, - const gfx::Rect& bounds); - - bool InitializeV6(WaylandConnection* connection, - const gfx::Rect& bounds, - XDGSurfaceWrapperImpl* parent_xdg_surface); - struct zxdg_positioner_v6* CreatePositionerV6(WaylandConnection* connection, - WaylandWindow* parent_window, - const gfx::Rect& bounds); - - MenuType GetMenuTypeForPositioner(WaylandConnection* connection, - WaylandWindow* parent_window) const; - - bool CanGrabPopup(WaylandConnection* connection) const; + XDGSurfaceWrapperImpl* parent_xdg_surface_wrapper); + struct xdg_positioner* CreatePositioner(WaylandConnection* connection, + WaylandWindow* parent_window, + const gfx::Rect& bounds); // xdg_popup_listener static void Configure(void* data, - int32_t x, - int32_t y, - int32_t width, - int32_t height); - static void ConfigureStable(void* data, struct xdg_popup* xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height); - static void ConfigureV6(void* data, - struct zxdg_popup_v6* zxdg_popup_v6, - int32_t x, - int32_t y, - int32_t width, - int32_t height); - static void PopupDone(void* data); - static void PopupDoneStable(void* data, struct xdg_popup* xdg_popup); - static void PopupDoneV6(void* data, struct zxdg_popup_v6* zxdg_popup_v6); - - XDGSurfaceWrapperImpl* xdg_surface(); + static void PopupDone(void* data, struct xdg_popup* xdg_popup); + + XDGSurfaceWrapperImpl* xdg_surface_wrapper() const; // Non-owned WaylandWindow that uses this popup. WaylandWindow* const wayland_window_; // Ground surface for this popup. - std::unique_ptr<XDGSurfaceWrapperImpl> xdg_surface_; + std::unique_ptr<XDGSurfaceWrapperImpl> xdg_surface_wrapper_; // XDG Shell Stable object. wl::Object<xdg_popup> xdg_popup_; - // XDG Shell V6 object. - wl::Object<zxdg_popup_v6> zxdg_popup_v6_; DISALLOW_COPY_AND_ASSIGN(XDGPopupWrapperImpl); }; diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc index 6ee18c65b7e..852573ae6fc 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc +++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc @@ -4,13 +4,8 @@ #include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" -#include <xdg-decoration-unstable-v1-client-protocol.h> #include <xdg-shell-client-protocol.h> -#include <xdg-shell-unstable-v6-client-protocol.h> -#include "base/strings/utf_string_conversions.h" -#include "ui/base/hit_test.h" -#include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -20,244 +15,51 @@ XDGSurfaceWrapperImpl::XDGSurfaceWrapperImpl(WaylandWindow* wayland_window, WaylandConnection* connection) : wayland_window_(wayland_window), connection_(connection) {} -XDGSurfaceWrapperImpl::~XDGSurfaceWrapperImpl() {} +XDGSurfaceWrapperImpl::~XDGSurfaceWrapperImpl() = default; -bool XDGSurfaceWrapperImpl::Initialize(bool with_toplevel) { - if (connection_->shell()) - return InitializeStable(with_toplevel); - else if (connection_->shell_v6()) - return InitializeV6(with_toplevel); - NOTREACHED() << "Wrong shell protocol"; - return false; -} - -void XDGSurfaceWrapperImpl::SetMaximized() { - if (xdg_toplevel_) { - xdg_toplevel_set_maximized(xdg_toplevel_.get()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_maximized(zxdg_toplevel_v6_.get()); - } -} - -void XDGSurfaceWrapperImpl::UnSetMaximized() { - if (xdg_toplevel_) { - xdg_toplevel_unset_maximized(xdg_toplevel_.get()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_unset_maximized(zxdg_toplevel_v6_.get()); - } -} - -void XDGSurfaceWrapperImpl::SetFullscreen() { - if (xdg_toplevel_) { - xdg_toplevel_set_fullscreen(xdg_toplevel_.get(), nullptr); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_fullscreen(zxdg_toplevel_v6_.get(), nullptr); - } -} - -void XDGSurfaceWrapperImpl::UnSetFullscreen() { - if (xdg_toplevel_) { - xdg_toplevel_unset_fullscreen(xdg_toplevel_.get()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_unset_fullscreen(zxdg_toplevel_v6_.get()); - } -} - -void XDGSurfaceWrapperImpl::SetMinimized() { - if (xdg_toplevel_) { - xdg_toplevel_set_minimized(xdg_toplevel_.get()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_minimized(zxdg_toplevel_v6_.get()); +bool XDGSurfaceWrapperImpl::Initialize() { + if (!connection_->shell()) { + NOTREACHED() << "Wrong shell protocol"; + return false; } -} -void XDGSurfaceWrapperImpl::SurfaceMove(WaylandConnection* connection) { - if (xdg_toplevel_) { - xdg_toplevel_move(xdg_toplevel_.get(), connection_->seat(), - connection_->serial()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_move(zxdg_toplevel_v6_.get(), connection_->seat(), - connection_->serial()); - } -} + static const xdg_surface_listener xdg_surface_listener = { + &XDGSurfaceWrapperImpl::Configure, + }; -void XDGSurfaceWrapperImpl::SurfaceResize(WaylandConnection* connection, - uint32_t hittest) { - if (xdg_toplevel_) { - xdg_toplevel_resize(xdg_toplevel_.get(), connection_->seat(), - connection_->serial(), - wl::IdentifyDirection(*connection, hittest)); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_resize(zxdg_toplevel_v6_.get(), connection_->seat(), - connection_->serial(), - wl::IdentifyDirection(*connection, hittest)); + xdg_surface_.reset(xdg_wm_base_get_xdg_surface( + connection_->shell(), wayland_window_->root_surface()->surface())); + if (!xdg_surface_) { + LOG(ERROR) << "Failed to create xdg_surface"; + return false; } -} -void XDGSurfaceWrapperImpl::SetTitle(const base::string16& title) { - if (xdg_toplevel_) { - xdg_toplevel_set_title(xdg_toplevel_.get(), - base::UTF16ToUTF8(title).c_str()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_title(zxdg_toplevel_v6_.get(), - base::UTF16ToUTF8(title).c_str()); - } + xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); + connection_->ScheduleFlush(); + return true; } -void XDGSurfaceWrapperImpl::AckConfigure() { - if (xdg_surface_) { - xdg_surface_ack_configure(xdg_surface_.get(), pending_configure_serial_); - } else { - DCHECK(zxdg_surface_v6_); - zxdg_surface_v6_ack_configure(zxdg_surface_v6_.get(), - pending_configure_serial_); - } +void XDGSurfaceWrapperImpl::AckConfigure(uint32_t serial) { + DCHECK(xdg_surface_); + xdg_surface_ack_configure(xdg_surface_.get(), serial); connection_->wayland_window_manager()->NotifyWindowConfigured( wayland_window_); } void XDGSurfaceWrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) { - if (xdg_surface_) { - xdg_surface_set_window_geometry(xdg_surface_.get(), bounds.x(), bounds.y(), - bounds.width(), bounds.height()); - } else { - DCHECK(zxdg_surface_v6_); - zxdg_surface_v6_set_window_geometry(zxdg_surface_v6_.get(), bounds.x(), - bounds.y(), bounds.width(), - bounds.height()); - } -} - -void XDGSurfaceWrapperImpl::SetMinSize(int32_t width, int32_t height) { - if (xdg_toplevel_) { - xdg_toplevel_set_min_size(xdg_toplevel_.get(), width, height); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_min_size(zxdg_toplevel_v6_.get(), width, height); - } -} - -void XDGSurfaceWrapperImpl::SetMaxSize(int32_t width, int32_t height) { - if (xdg_toplevel_) { - xdg_toplevel_set_max_size(xdg_toplevel_.get(), width, height); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_max_size(zxdg_toplevel_v6_.get(), width, height); - } -} - -void XDGSurfaceWrapperImpl::SetAppId(const std::string& app_id) { - if (xdg_toplevel_) { - xdg_toplevel_set_app_id(xdg_toplevel_.get(), app_id.c_str()); - } else { - DCHECK(zxdg_toplevel_v6_); - zxdg_toplevel_v6_set_app_id(zxdg_toplevel_v6_.get(), app_id.c_str()); - } -} - -// static -void XDGSurfaceWrapperImpl::ConfigureStable(void* data, - struct xdg_surface* xdg_surface, - uint32_t serial) { - auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); - DCHECK(surface); - surface->pending_configure_serial_ = serial; - - surface->AckConfigure(); -} - -// static -void XDGSurfaceWrapperImpl::ConfigureTopLevelStable( - void* data, - struct xdg_toplevel* xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array* states) { - auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); - DCHECK(surface); - - bool is_maximized = - CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_MAXIMIZED); - bool is_fullscreen = - CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_FULLSCREEN); - bool is_activated = - CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_ACTIVATED); - - surface->wayland_window_->HandleSurfaceConfigure(width, height, is_maximized, - is_fullscreen, is_activated); -} - -// static -void XDGSurfaceWrapperImpl::CloseTopLevelStable( - void* data, - struct xdg_toplevel* xdg_toplevel) { - auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); - DCHECK(surface); - surface->wayland_window_->OnCloseRequest(); -} - -void XDGSurfaceWrapperImpl::SetTopLevelDecorationMode( - zxdg_toplevel_decoration_v1_mode requested_mode) { - if (requested_mode == decoration_mode_) - return; - - decoration_mode_ = requested_mode; - zxdg_toplevel_decoration_v1_set_mode(zxdg_toplevel_decoration_.get(), - requested_mode); -} - -// static -void XDGSurfaceWrapperImpl::ConfigureV6(void* data, - struct zxdg_surface_v6* zxdg_surface_v6, - uint32_t serial) { - auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); - DCHECK(surface); - surface->pending_configure_serial_ = serial; - - surface->AckConfigure(); -} - -// static -void XDGSurfaceWrapperImpl::ConfigureTopLevelV6( - void* data, - struct zxdg_toplevel_v6* zxdg_toplevel_v6, - int32_t width, - int32_t height, - struct wl_array* states) { - auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); - DCHECK(surface); - - bool is_maximized = - CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); - bool is_fullscreen = - CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); - bool is_activated = - CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); - - surface->wayland_window_->HandleSurfaceConfigure(width, height, is_maximized, - is_fullscreen, is_activated); + DCHECK(xdg_surface_); + xdg_surface_set_window_geometry(xdg_surface_.get(), bounds.x(), bounds.y(), + bounds.width(), bounds.height()); } // static -void XDGSurfaceWrapperImpl::CloseTopLevelV6( - void* data, - struct zxdg_toplevel_v6* zxdg_toplevel_v6) { +void XDGSurfaceWrapperImpl::Configure(void* data, + struct xdg_surface* xdg_surface, + uint32_t serial) { auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); DCHECK(surface); - surface->wayland_window_->OnCloseRequest(); -} -zxdg_surface_v6* XDGSurfaceWrapperImpl::zxdg_surface() const { - DCHECK(zxdg_surface_v6_); - return zxdg_surface_v6_.get(); + surface->wayland_window_->HandleSurfaceConfigure(serial); } xdg_surface* XDGSurfaceWrapperImpl::xdg_surface() const { @@ -265,112 +67,4 @@ xdg_surface* XDGSurfaceWrapperImpl::xdg_surface() const { return xdg_surface_.get(); } -// static -void XDGSurfaceWrapperImpl::ConfigureDecoration( - void* data, - struct zxdg_toplevel_decoration_v1* decoration, - uint32_t mode) { - auto* surface = static_cast<XDGSurfaceWrapperImpl*>(data); - DCHECK(surface); - surface->SetTopLevelDecorationMode( - static_cast<zxdg_toplevel_decoration_v1_mode>(mode)); -} - -bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) { - static const xdg_surface_listener xdg_surface_listener = { - &XDGSurfaceWrapperImpl::ConfigureStable, - }; - static const xdg_toplevel_listener xdg_toplevel_listener = { - &XDGSurfaceWrapperImpl::ConfigureTopLevelStable, - &XDGSurfaceWrapperImpl::CloseTopLevelStable, - }; - - // if this surface is created for the popup role, mark that it requires - // configuration acknowledgement on each configure event. - surface_for_popup_ = !with_toplevel; - - xdg_surface_.reset(xdg_wm_base_get_xdg_surface( - connection_->shell(), wayland_window_->root_surface()->surface())); - if (!xdg_surface_) { - LOG(ERROR) << "Failed to create xdg_surface"; - return false; - } - xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this); - // XDGPopup requires a separate surface to be created, so this is just a - // request to get an xdg_surface for it. - if (surface_for_popup_) { - connection_->ScheduleFlush(); - return true; - } - - xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get())); - if (!xdg_toplevel_) { - LOG(ERROR) << "Failed to create xdg_toplevel"; - return false; - } - - xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, this); - - InitializeXdgDecoration(); - - wayland_window_->root_surface()->Commit(); - connection_->ScheduleFlush(); - return true; -} - -bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) { - static const zxdg_surface_v6_listener zxdg_surface_v6_listener = { - &XDGSurfaceWrapperImpl::ConfigureV6, - }; - static const zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { - &XDGSurfaceWrapperImpl::ConfigureTopLevelV6, - &XDGSurfaceWrapperImpl::CloseTopLevelV6, - }; - - // if this surface is created for the popup role, mark that it requires - // configuration acknowledgement on each configure event. - surface_for_popup_ = !with_toplevel; - - zxdg_surface_v6_.reset(zxdg_shell_v6_get_xdg_surface( - connection_->shell_v6(), wayland_window_->root_surface()->surface())); - if (!zxdg_surface_v6_) { - LOG(ERROR) << "Failed to create zxdg_surface"; - return false; - } - zxdg_surface_v6_add_listener(zxdg_surface_v6_.get(), - &zxdg_surface_v6_listener, this); - // XDGPopupV6 requires a separate surface to be created, so this is just a - // request to get an xdg_surface for it. - if (surface_for_popup_) { - connection_->ScheduleFlush(); - return true; - } - - zxdg_toplevel_v6_.reset(zxdg_surface_v6_get_toplevel(zxdg_surface_v6_.get())); - if (!zxdg_toplevel_v6_) { - LOG(ERROR) << "Failed to create zxdg_toplevel"; - return false; - } - zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(), - &zxdg_toplevel_v6_listener, this); - - wayland_window_->root_surface()->Commit(); - connection_->ScheduleFlush(); - return true; -} - -void XDGSurfaceWrapperImpl::InitializeXdgDecoration() { - if (connection_->xdg_decoration_manager_v1()) { - DCHECK(!zxdg_toplevel_decoration_); - static const zxdg_toplevel_decoration_v1_listener decoration_listener = { - &XDGSurfaceWrapperImpl::ConfigureDecoration, - }; - zxdg_toplevel_decoration_.reset( - zxdg_decoration_manager_v1_get_toplevel_decoration( - connection_->xdg_decoration_manager_v1(), xdg_toplevel_.get())); - zxdg_toplevel_decoration_v1_add_listener(zxdg_toplevel_decoration_.get(), - &decoration_listener, this); - } -} - } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h index da945779a76..c88eccfba82 100644 --- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h +++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h @@ -7,8 +7,6 @@ #include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" -#include <xdg-decoration-unstable-v1-client-protocol.h> - #include <cstdint> #include <string> @@ -24,7 +22,7 @@ namespace ui { class WaylandConnection; class WaylandWindow; -// Surface wrapper for xdg-shell stable and xdg-shell-unstable-v6 +// Surface wrapper for xdg-shell stable class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { public: XDGSurfaceWrapperImpl(WaylandWindow* wayland_window, @@ -34,84 +32,23 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper { ~XDGSurfaceWrapperImpl() override; // ShellSurfaceWrapper overrides: - bool Initialize(bool with_toplevel) override; - void SetMaximized() override; - void UnSetMaximized() override; - void SetFullscreen() override; - void UnSetFullscreen() override; - void SetMinimized() override; - void SurfaceMove(WaylandConnection* connection) override; - void SurfaceResize(WaylandConnection* connection, uint32_t hittest) override; - void SetTitle(const base::string16& title) override; - void AckConfigure() override; + bool Initialize() override; + void AckConfigure(uint32_t serial) override; void SetWindowGeometry(const gfx::Rect& bounds) override; - void SetMinSize(int32_t width, int32_t height) override; - void SetMaxSize(int32_t width, int32_t height) override; - void SetAppId(const std::string& app_id) override; // xdg_surface_listener - static void ConfigureV6(void* data, - struct zxdg_surface_v6* zxdg_surface_v6, - uint32_t serial); - static void ConfigureTopLevelV6(void* data, - struct zxdg_toplevel_v6* zxdg_toplevel_v6, - int32_t width, - int32_t height, - struct wl_array* states); - - static void ConfigureStable(void* data, - struct xdg_surface* xdg_surface, - uint32_t serial); - static void ConfigureTopLevelStable(void* data, - struct xdg_toplevel* xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array* states); - - // xdg_toplevel_listener - static void CloseTopLevelStable(void* data, - struct xdg_toplevel* xdg_toplevel); - static void CloseTopLevelV6(void* data, - struct zxdg_toplevel_v6* zxdg_toplevel_v6); - - void SetTopLevelDecorationMode( - zxdg_toplevel_decoration_v1_mode requested_mode); - // zxdg_decoration_listener - static void ConfigureDecoration( - void* data, - struct zxdg_toplevel_decoration_v1* decoration, - uint32_t mode); + static void Configure(void* data, + struct xdg_surface* xdg_surface, + uint32_t serial); struct xdg_surface* xdg_surface() const; - zxdg_surface_v6* zxdg_surface() const; private: - // Initializes using XDG Shell Stable protocol. - bool InitializeStable(bool with_toplevel); - // Initializes using XDG Shell V6 protocol. - bool InitializeV6(bool with_toplevel); - - // Initializes the xdg-decoration protocol extension, if available. - void InitializeXdgDecoration(); - // Non-owing WaylandWindow that uses this surface wrapper. WaylandWindow* const wayland_window_; WaylandConnection* const connection_; - uint32_t pending_configure_serial_ = 0; - - wl::Object<zxdg_surface_v6> zxdg_surface_v6_; - wl::Object<zxdg_toplevel_v6> zxdg_toplevel_v6_; wl::Object<struct xdg_surface> xdg_surface_; - wl::Object<xdg_toplevel> xdg_toplevel_; - wl::Object<zxdg_toplevel_decoration_v1> zxdg_toplevel_decoration_; - - bool surface_for_popup_ = false; - - // Keeps track of the decoration mode currently in use if xdg-decoration - // protocol extension is available, otherwise CLIENT_SIDE is assumed. - enum zxdg_toplevel_decoration_v1_mode decoration_mode_ = - ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc new file mode 100644 index 00000000000..cd57c613a36 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc @@ -0,0 +1,202 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h" + +#include <xdg-decoration-unstable-v1-client-protocol.h> +#include <xdg-shell-client-protocol.h> +#include <xdg-shell-unstable-v6-client-protocol.h> + +#include "base/strings/utf_string_conversions.h" +#include "ui/base/hit_test.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" +#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h" + +namespace ui { + +XDGToplevelWrapperImpl::XDGToplevelWrapperImpl( + std::unique_ptr<XDGSurfaceWrapperImpl> surface, + WaylandWindow* wayland_window, + WaylandConnection* connection) + : xdg_surface_wrapper_(std::move(surface)), + wayland_window_(wayland_window), + connection_(connection), + decoration_mode_(DecorationMode::kClientSide) {} + +XDGToplevelWrapperImpl::~XDGToplevelWrapperImpl() = default; + +bool XDGToplevelWrapperImpl::Initialize() { + if (!connection_->shell()) { + NOTREACHED() << "Wrong shell protocol"; + return false; + } + + static const xdg_toplevel_listener xdg_toplevel_listener = { + &XDGToplevelWrapperImpl::ConfigureTopLevel, + &XDGToplevelWrapperImpl::CloseTopLevel, + }; + + if (!xdg_surface_wrapper_) + return false; + + xdg_toplevel_.reset( + xdg_surface_get_toplevel(xdg_surface_wrapper_->xdg_surface())); + if (!xdg_toplevel_) { + LOG(ERROR) << "Failed to create xdg_toplevel"; + return false; + } + + xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, this); + + InitializeXdgDecoration(); + + wayland_window_->root_surface()->Commit(); + connection_->ScheduleFlush(); + return true; +} + +void XDGToplevelWrapperImpl::SetMaximized() { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_maximized(xdg_toplevel_.get()); +} + +void XDGToplevelWrapperImpl::UnSetMaximized() { + DCHECK(xdg_toplevel_); + xdg_toplevel_unset_maximized(xdg_toplevel_.get()); +} + +void XDGToplevelWrapperImpl::SetFullscreen() { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_fullscreen(xdg_toplevel_.get(), nullptr); +} + +void XDGToplevelWrapperImpl::UnSetFullscreen() { + DCHECK(xdg_toplevel_); + xdg_toplevel_unset_fullscreen(xdg_toplevel_.get()); +} + +void XDGToplevelWrapperImpl::SetMinimized() { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_minimized(xdg_toplevel_.get()); +} + +void XDGToplevelWrapperImpl::SurfaceMove(WaylandConnection* connection) { + DCHECK(xdg_toplevel_); + xdg_toplevel_move(xdg_toplevel_.get(), connection->seat(), + connection->serial()); +} + +void XDGToplevelWrapperImpl::SurfaceResize(WaylandConnection* connection, + uint32_t hittest) { + DCHECK(xdg_toplevel_); + xdg_toplevel_resize(xdg_toplevel_.get(), connection->seat(), + connection->serial(), + wl::IdentifyDirection(*connection, hittest)); +} + +void XDGToplevelWrapperImpl::SetTitle(const base::string16& title) { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_title(xdg_toplevel_.get(), base::UTF16ToUTF8(title).c_str()); +} + +void XDGToplevelWrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) { + xdg_surface_wrapper_->SetWindowGeometry(bounds); +} + +void XDGToplevelWrapperImpl::SetMinSize(int32_t width, int32_t height) { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_min_size(xdg_toplevel_.get(), width, height); +} + +void XDGToplevelWrapperImpl::SetMaxSize(int32_t width, int32_t height) { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_max_size(xdg_toplevel_.get(), width, height); +} + +void XDGToplevelWrapperImpl::SetAppId(const std::string& app_id) { + DCHECK(xdg_toplevel_); + xdg_toplevel_set_app_id(xdg_toplevel_.get(), app_id.c_str()); +} + +void XDGToplevelWrapperImpl::SetDecoration(DecorationMode decoration) { + SetTopLevelDecorationMode(decoration); +} + +void XDGToplevelWrapperImpl::AckConfigure(uint32_t serial) { + DCHECK(xdg_surface_wrapper_); + xdg_surface_wrapper_->AckConfigure(serial); +} + +// static +void XDGToplevelWrapperImpl::ConfigureTopLevel( + void* data, + struct xdg_toplevel* xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array* states) { + auto* surface = static_cast<XDGToplevelWrapperImpl*>(data); + DCHECK(surface); + + bool is_maximized = + CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_MAXIMIZED); + bool is_fullscreen = + CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_FULLSCREEN); + bool is_activated = + CheckIfWlArrayHasValue(states, XDG_TOPLEVEL_STATE_ACTIVATED); + + surface->wayland_window_->HandleToplevelConfigure( + width, height, is_maximized, is_fullscreen, is_activated); +} + +// static +void XDGToplevelWrapperImpl::CloseTopLevel(void* data, + struct xdg_toplevel* xdg_toplevel) { + auto* surface = static_cast<XDGToplevelWrapperImpl*>(data); + DCHECK(surface); + surface->wayland_window_->OnCloseRequest(); +} + +void XDGToplevelWrapperImpl::SetTopLevelDecorationMode( + DecorationMode requested_mode) { + if (!zxdg_toplevel_decoration_ || requested_mode == decoration_mode_) + return; + + decoration_mode_ = requested_mode; + zxdg_toplevel_decoration_v1_set_mode(zxdg_toplevel_decoration_.get(), + static_cast<uint32_t>(requested_mode)); +} + +// static +void XDGToplevelWrapperImpl::ConfigureDecoration( + void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode) { + auto* surface = static_cast<XDGToplevelWrapperImpl*>(data); + DCHECK(surface); + surface->SetTopLevelDecorationMode(static_cast<DecorationMode>(mode)); +} + +void XDGToplevelWrapperImpl::InitializeXdgDecoration() { + if (connection_->xdg_decoration_manager_v1()) { + DCHECK(!zxdg_toplevel_decoration_); + static const zxdg_toplevel_decoration_v1_listener decoration_listener = { + &XDGToplevelWrapperImpl::ConfigureDecoration, + }; + zxdg_toplevel_decoration_.reset( + zxdg_decoration_manager_v1_get_toplevel_decoration( + connection_->xdg_decoration_manager_v1(), xdg_toplevel_.get())); + zxdg_toplevel_decoration_v1_add_listener(zxdg_toplevel_decoration_.get(), + &decoration_listener, this); + } +} + +XDGSurfaceWrapperImpl* XDGToplevelWrapperImpl::xdg_surface_wrapper() const { + DCHECK(xdg_surface_wrapper_.get()); + return xdg_surface_wrapper_.get(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h new file mode 100644 index 00000000000..867a54dd79e --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h @@ -0,0 +1,88 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_TOPLEVEL_WRAPPER_IMPL_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_TOPLEVEL_WRAPPER_IMPL_H_ + +#include <memory> + +#include "base/macros.h" +#include "ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h" + +namespace ui { + +class XDGSurfaceWrapperImpl; +class WaylandConnection; +class WaylandWindow; + +// Toplevel wrapper for xdg-shell stable +class XDGToplevelWrapperImpl : public ShellToplevelWrapper { + public: + XDGToplevelWrapperImpl(std::unique_ptr<XDGSurfaceWrapperImpl> surface, + WaylandWindow* wayland_window, + WaylandConnection* connection); + XDGToplevelWrapperImpl(const XDGToplevelWrapperImpl&) = delete; + XDGToplevelWrapperImpl& operator=(const XDGToplevelWrapperImpl&) = delete; + ~XDGToplevelWrapperImpl() override; + + // ShellSurfaceWrapper overrides: + bool Initialize() override; + void SetMaximized() override; + void UnSetMaximized() override; + void SetFullscreen() override; + void UnSetFullscreen() override; + void SetMinimized() override; + void SurfaceMove(WaylandConnection* connection) override; + void SurfaceResize(WaylandConnection* connection, uint32_t hittest) override; + void SetTitle(const base::string16& title) override; + void AckConfigure(uint32_t serial) override; + void SetWindowGeometry(const gfx::Rect& bounds) override; + void SetMinSize(int32_t width, int32_t height) override; + void SetMaxSize(int32_t width, int32_t height) override; + void SetAppId(const std::string& app_id) override; + void SetDecoration(DecorationMode decoration) override; + + XDGSurfaceWrapperImpl* xdg_surface_wrapper() const; + + private: + // xdg_toplevel_listener + static void ConfigureTopLevel(void* data, + struct xdg_toplevel* xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array* states); + static void CloseTopLevel(void* data, struct xdg_toplevel* xdg_toplevel); + + // zxdg_decoration_listener + static void ConfigureDecoration( + void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode); + + // Send request to wayland compositor to enable a requested decoration mode. + void SetTopLevelDecorationMode(DecorationMode requested_mode); + + // Initializes the xdg-decoration protocol extension, if available. + void InitializeXdgDecoration(); + + // Ground surface for this toplevel wrapper. + std::unique_ptr<XDGSurfaceWrapperImpl> xdg_surface_wrapper_; + + // Non-owing WaylandWindow that uses this toplevel wrapper. + WaylandWindow* const wayland_window_; + WaylandConnection* const connection_; + + // XDG Shell Stable object. + wl::Object<xdg_toplevel> xdg_toplevel_; + + wl::Object<zxdg_toplevel_decoration_v1> zxdg_toplevel_decoration_; + + // On client side, it keeps track of the decoration mode currently in + // use if xdg-decoration protocol extension is available. + DecorationMode decoration_mode_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_TOPLEVEL_WRAPPER_IMPL_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc index afa819257d6..a9e657d387b 100644 --- a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc +++ b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc @@ -6,9 +6,9 @@ #include <primary-selection-unstable-v1-client-protocol.h> -#include "ui/ozone/platform/wayland/host/zwp_primary_selection_offer.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" +#include "ui/ozone/platform/wayland/host/zwp_primary_selection_offer.h" namespace ui { @@ -18,7 +18,8 @@ ZwpPrimarySelectionDevice::ZwpPrimarySelectionDevice( zwp_primary_selection_device_v1* data_device) : WaylandDataDeviceBase(connection), data_device_(data_device) { static const struct zwp_primary_selection_device_v1_listener kListener = { - ZwpPrimarySelectionDevice::OnDataOffer, ZwpPrimarySelectionDevice::OnSelection}; + ZwpPrimarySelectionDevice::OnDataOffer, + ZwpPrimarySelectionDevice::OnSelection}; zwp_primary_selection_device_v1_add_listener(data_device_.get(), &kListener, this); } @@ -40,10 +41,6 @@ void ZwpPrimarySelectionDevice::OnDataOffer( zwp_primary_selection_offer_v1* offer) { auto* self = static_cast<ZwpPrimarySelectionDevice*>(data); DCHECK(self); - - self->connection()->clipboard()->UpdateSequenceNumber( - ClipboardBuffer::kSelection); - self->set_data_offer(std::make_unique<ZwpPrimarySelectionOffer>(offer)); } @@ -56,17 +53,16 @@ void ZwpPrimarySelectionDevice::OnSelection( DCHECK(self); // 'offer' will be null to indicate that the selection is no longer valid, - // i.e. there is no longer clipboard data available to paste. + // i.e. there is no longer selection data available to be fetched. if (!offer) { self->ResetDataOffer(); - - // Clear Clipboard cache. - self->connection()->clipboard()->SetData({}, {}); - return; + } else { + DCHECK(self->data_offer()); + self->data_offer()->EnsureTextMimeTypeIfNeeded(); } - DCHECK(self->data_offer()); - self->data_offer()->EnsureTextMimeTypeIfNeeded(); + if (self->selection_delegate()) + self->selection_delegate()->OnSelectionOffer(self->data_offer()); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.h b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.h index 69a7ee2c397..c4d36a2ee51 100644 --- a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.h +++ b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device.h @@ -19,10 +19,11 @@ class WaylandConnection; class ZwpPrimarySelectionDevice : public WaylandDataDeviceBase { public: ZwpPrimarySelectionDevice(WaylandConnection* connection, - zwp_primary_selection_device_v1* data_device); + zwp_primary_selection_device_v1* data_device); ZwpPrimarySelectionDevice(const ZwpPrimarySelectionDevice&) = delete; - ZwpPrimarySelectionDevice& operator =(const ZwpPrimarySelectionDevice&) = delete; + ZwpPrimarySelectionDevice& operator=(const ZwpPrimarySelectionDevice&) = + delete; ~ZwpPrimarySelectionDevice() override; diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h index 4f48f897065..8fa4d2f74db 100644 --- a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h +++ b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h @@ -23,7 +23,8 @@ class ZwpPrimarySelectionDeviceManager { ZwpPrimarySelectionDeviceManager( zwp_primary_selection_device_manager_v1* manager, WaylandConnection* connection); - ZwpPrimarySelectionDeviceManager(const ZwpPrimarySelectionDeviceManager&) = delete; + ZwpPrimarySelectionDeviceManager(const ZwpPrimarySelectionDeviceManager&) = + delete; ZwpPrimarySelectionDeviceManager& operator=( const ZwpPrimarySelectionDeviceManager&) = delete; ~ZwpPrimarySelectionDeviceManager(); diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_offer.cc b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_offer.cc index 9a869e789b4..667cfd5abb4 100644 --- a/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_offer.cc +++ b/chromium/ui/ozone/platform/wayland/host/zwp_primary_selection_offer.cc @@ -10,8 +10,8 @@ #include <algorithm> #include "base/check.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" -#include "base/stl_util.h" #include "ui/base/clipboard/clipboard_constants.h" namespace ui { diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc b/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc index 8ae6849a3fe..456b19474f1 100644 --- a/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc +++ b/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc @@ -82,7 +82,21 @@ void ZWPTextInputWrapperV1::SetCursorRect(const gfx::Rect& rect) { void ZWPTextInputWrapperV1::SetSurroundingText( const base::string16& text, const gfx::Range& selection_range) { + static constexpr size_t kWaylandMessageDataMaxLength = 4000; const std::string text_utf8 = base::UTF16ToUTF8(text); + // The text length for set_surrounding_text can not be longer than the maximum + // length of wayland messages. The maximum length of the text is explicitly + // specified as 4000 in the protocol spec of text-input-unstable-v3. + // If the client is unware of the text around the cursor, we can skip sending + // set_surrounding_text requests. We fall back to this case when the text is + // too long. + // TODO(fukino): If the length of |text| doesn't fit into the 4000 bytes + // limitation, we should truncate the text and adjust indices of + // |selection_range| to make use of set_surrounding_text as much as possible. + // crbug.com/1173465. + if (text_utf8.size() > kWaylandMessageDataMaxLength) + return; + zwp_text_input_v1_set_surrounding_text(obj_.get(), text_utf8.c_str(), selection_range.start(), selection_range.end()); diff --git a/chromium/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc new file mode 100644 index 00000000000..f506f0af5f9 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc @@ -0,0 +1,262 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.h" + +#include <xdg-shell-unstable-v6-client-protocol.h> + +#include <memory> + +#include "base/environment.h" +#include "base/nix/xdg_util.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" +#include "ui/events/types/event_type.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_pointer.h" +#include "ui/ozone/platform/wayland/host/wayland_popup.h" +#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h" +#include "ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h" +#include "ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h" + +namespace ui { + +namespace { + +uint32_t TranslateAnchor(WlAnchor anchor) { + switch (anchor) { + case WlAnchor::None: + return ZXDG_POSITIONER_V6_ANCHOR_NONE; + case WlAnchor::Top: + return ZXDG_POSITIONER_V6_ANCHOR_TOP; + case WlAnchor::Bottom: + return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; + case WlAnchor::Left: + return ZXDG_POSITIONER_V6_ANCHOR_LEFT; + case WlAnchor::Right: + return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + case WlAnchor::TopLeft: + return ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_LEFT; + case WlAnchor::BottomLeft: + return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | ZXDG_POSITIONER_V6_ANCHOR_LEFT; + case WlAnchor::TopRight: + return ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + case WlAnchor::BottomRight: + return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + } +} + +uint32_t TranslateGravity(WlGravity gravity) { + switch (gravity) { + case WlGravity::None: + return ZXDG_POSITIONER_V6_GRAVITY_NONE; + case WlGravity::Top: + return ZXDG_POSITIONER_V6_GRAVITY_TOP; + case WlGravity::Bottom: + return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; + case WlGravity::Left: + return ZXDG_POSITIONER_V6_GRAVITY_LEFT; + case WlGravity::Right: + return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + case WlGravity::TopLeft: + return ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_LEFT; + case WlGravity::BottomLeft: + return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT; + case WlGravity::TopRight: + return ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + case WlGravity::BottomRight: + return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + } +} + +uint32_t TranslateConstraintAdjustment( + WlConstraintAdjustment constraint_adjustment) { + uint32_t res = 0; + if ((constraint_adjustment & WlConstraintAdjustment::SlideX) != + WlConstraintAdjustment::None) + res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; + if ((constraint_adjustment & WlConstraintAdjustment::SlideY) != + WlConstraintAdjustment::None) + res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + if ((constraint_adjustment & WlConstraintAdjustment::FlipX) != + WlConstraintAdjustment::None) + res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; + if ((constraint_adjustment & WlConstraintAdjustment::FlipY) != + WlConstraintAdjustment::None) + res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; + if ((constraint_adjustment & WlConstraintAdjustment::ResizeX) != + WlConstraintAdjustment::None) + res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; + if ((constraint_adjustment & WlConstraintAdjustment::ResizeY) != + WlConstraintAdjustment::None) + res |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + return res; +} + +} // namespace + +ZXDGPopupV6WrapperImpl::ZXDGPopupV6WrapperImpl( + std::unique_ptr<ZXDGSurfaceV6WrapperImpl> surface, + WaylandWindow* wayland_window) + : wayland_window_(wayland_window), + zxdg_surface_v6_wrapper_(std::move(surface)) { + DCHECK(zxdg_surface_v6_wrapper_); + DCHECK(wayland_window_ && wayland_window_->parent_window()); +} + +ZXDGPopupV6WrapperImpl::~ZXDGPopupV6WrapperImpl() = default; + +bool ZXDGPopupV6WrapperImpl::Initialize(WaylandConnection* connection, + const gfx::Rect& bounds) { + if (!connection->shell() && !connection->shell_v6()) { + NOTREACHED() << "Wrong shell protocol"; + return false; + } + + ZXDGSurfaceV6WrapperImpl* parent_xdg_surface = nullptr; + // If the parent window is a popup, the surface of that popup must be used as + // a parent. + if (wl::IsMenuType(wayland_window_->parent_window()->type())) { + auto* wayland_popup = + static_cast<WaylandPopup*>(wayland_window_->parent_window()); + ZXDGPopupV6WrapperImpl* popup = + static_cast<ZXDGPopupV6WrapperImpl*>(wayland_popup->shell_popup()); + parent_xdg_surface = popup->zxdg_surface_v6_wrapper(); + } else { + WaylandToplevelWindow* wayland_surface = + static_cast<WaylandToplevelWindow*>(wayland_window_->parent_window()); + parent_xdg_surface = static_cast<ZXDGToplevelV6WrapperImpl*>( + wayland_surface->shell_toplevel()) + ->zxdg_surface_v6_wrapper(); + } + + if (!zxdg_surface_v6_wrapper_ || !parent_xdg_surface) + return false; + + auto new_bounds = bounds; + // Wayland doesn't allow empty bounds. If a zero or negative size is set, the + // invalid_input error is raised. Thus, use the least possible one. + // WaylandPopup will update its bounds upon the following configure event. + if (new_bounds.IsEmpty()) + new_bounds.set_size({1, 1}); + + if (connection->shell_v6()) + return InitializeV6(connection, new_bounds, parent_xdg_surface); + + return false; +} + +bool ZXDGPopupV6WrapperImpl::InitializeV6( + WaylandConnection* connection, + const gfx::Rect& bounds, + ZXDGSurfaceV6WrapperImpl* parent_xdg_surface) { + static const struct zxdg_popup_v6_listener zxdg_popup_v6_listener = { + &ZXDGPopupV6WrapperImpl::Configure, + &ZXDGPopupV6WrapperImpl::PopupDone, + }; + + zxdg_positioner_v6* positioner = + CreatePositioner(connection, wayland_window_->parent_window(), bounds); + if (!positioner) + return false; + + zxdg_popup_v6_.reset(zxdg_surface_v6_get_popup( + zxdg_surface_v6_wrapper_->zxdg_surface(), + parent_xdg_surface->zxdg_surface(), positioner)); + if (!zxdg_popup_v6_) + return false; + + zxdg_positioner_v6_destroy(positioner); + + if (CanGrabPopup(connection)) { + zxdg_popup_v6_grab(zxdg_popup_v6_.get(), connection->seat(), + connection->serial()); + } + zxdg_popup_v6_add_listener(zxdg_popup_v6_.get(), &zxdg_popup_v6_listener, + this); + + wayland_window_->root_surface()->Commit(); + return true; +} + +void ZXDGPopupV6WrapperImpl::AckConfigure(uint32_t serial) { + DCHECK(zxdg_surface_v6_wrapper_); + zxdg_surface_v6_wrapper_->AckConfigure(serial); +} + +zxdg_positioner_v6* ZXDGPopupV6WrapperImpl::CreatePositioner( + WaylandConnection* connection, + WaylandWindow* parent_window, + const gfx::Rect& bounds) { + struct zxdg_positioner_v6* positioner; + positioner = zxdg_shell_v6_create_positioner(connection->shell_v6()); + if (!positioner) + return nullptr; + + auto menu_type = GetMenuTypeForPositioner(connection, parent_window); + + // The parent we got must be the topmost in the stack of the same family + // windows. + DCHECK_EQ(parent_window->GetTopMostChildWindow(), parent_window); + + // Place anchor to the end of the possible position. + gfx::Rect anchor_rect = GetAnchorRect( + menu_type, bounds, + gfx::ScaleToRoundedRect(parent_window->GetBounds(), + 1.0 / parent_window->buffer_scale())); + + zxdg_positioner_v6_set_anchor_rect(positioner, anchor_rect.x(), + anchor_rect.y(), anchor_rect.width(), + anchor_rect.height()); + zxdg_positioner_v6_set_size(positioner, bounds.width(), bounds.height()); + zxdg_positioner_v6_set_anchor(positioner, + TranslateAnchor(GetAnchor(menu_type, bounds))); + zxdg_positioner_v6_set_gravity( + positioner, TranslateGravity(GetGravity(menu_type, bounds))); + zxdg_positioner_v6_set_constraint_adjustment( + positioner, + TranslateConstraintAdjustment(GetConstraintAdjustment(menu_type))); + return positioner; +} + +// static +void ZXDGPopupV6WrapperImpl::Configure(void* data, + struct zxdg_popup_v6* zxdg_popup_v6, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + // As long as the Wayland compositor repositions/requires to position windows + // relative to their parents, do not propagate final bounds information to + // Chromium. The browser places windows in respect to screen origin, but + // Wayland requires doing so in respect to parent window's origin. To properly + // place windows, the bounds are translated and adjusted according to the + // Wayland compositor needs during WaylandWindow::CreateXdgPopup call. + WaylandWindow* window = + static_cast<ZXDGPopupV6WrapperImpl*>(data)->wayland_window_; + DCHECK(window); + window->HandlePopupConfigure({x, y, width, height}); +} + +// static +void ZXDGPopupV6WrapperImpl::PopupDone(void* data, + struct zxdg_popup_v6* zxdg_popup_v6) { + WaylandWindow* window = + static_cast<ZXDGPopupV6WrapperImpl*>(data)->wayland_window_; + DCHECK(window); + window->Hide(); + window->OnCloseRequest(); +} + +ZXDGSurfaceV6WrapperImpl* ZXDGPopupV6WrapperImpl::zxdg_surface_v6_wrapper() + const { + DCHECK(zxdg_surface_v6_wrapper_.get()); + return zxdg_surface_v6_wrapper_.get(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.h new file mode 100644 index 00000000000..4c32a1e8f51 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.h @@ -0,0 +1,64 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_POPUP_V6_WRAPPER_IMPL_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_POPUP_V6_WRAPPER_IMPL_H_ + +#include <memory> + +#include "base/macros.h" +#include "ui/ozone/platform/wayland/host/shell_popup_wrapper.h" + +namespace ui { + +class ZXDGSurfaceV6WrapperImpl; +class WaylandConnection; +class WaylandWindow; + +// Popup wrapper for xdg-shell-unstable-v6 +class ZXDGPopupV6WrapperImpl : public ShellPopupWrapper { + public: + ZXDGPopupV6WrapperImpl(std::unique_ptr<ZXDGSurfaceV6WrapperImpl> surface, + WaylandWindow* wayland_window); + ~ZXDGPopupV6WrapperImpl() override; + + // XDGPopupWrapper: + bool Initialize(WaylandConnection* connection, + const gfx::Rect& bounds) override; + void AckConfigure(uint32_t serial) override; + + private: + bool InitializeV6(WaylandConnection* connection, + const gfx::Rect& bounds, + ZXDGSurfaceV6WrapperImpl* parent_zxdg_surface_v6_wrapper); + struct zxdg_positioner_v6* CreatePositioner(WaylandConnection* connection, + WaylandWindow* parent_window, + const gfx::Rect& bounds); + + // zxdg_popup_v6_listener + static void Configure(void* data, + struct zxdg_popup_v6* zxdg_popup_v6, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + static void PopupDone(void* data, struct zxdg_popup_v6* zxdg_popup_v6); + + ZXDGSurfaceV6WrapperImpl* zxdg_surface_v6_wrapper() const; + + // Non-owned WaylandWindow that uses this popup. + WaylandWindow* const wayland_window_; + + // Ground surface for this popup. + std::unique_ptr<ZXDGSurfaceV6WrapperImpl> zxdg_surface_v6_wrapper_; + + // XDG Shell V6 object. + wl::Object<zxdg_popup_v6> zxdg_popup_v6_; + + DISALLOW_COPY_AND_ASSIGN(ZXDGPopupV6WrapperImpl); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_POPUP_V6_WRAPPER_IMPL_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.cc new file mode 100644 index 00000000000..385638d528b --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.cc @@ -0,0 +1,74 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h" + +#include <xdg-shell-unstable-v6-client-protocol.h> + +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" + +namespace ui { + +ZXDGSurfaceV6WrapperImpl::ZXDGSurfaceV6WrapperImpl( + WaylandWindow* wayland_window, + WaylandConnection* connection) + : wayland_window_(wayland_window), connection_(connection) {} + +ZXDGSurfaceV6WrapperImpl::~ZXDGSurfaceV6WrapperImpl() = default; + +bool ZXDGSurfaceV6WrapperImpl::Initialize() { + if (!connection_->shell_v6()) { + NOTREACHED() << "Wrong shell protocol"; + return false; + } + + static const zxdg_surface_v6_listener zxdg_surface_v6_listener = { + &ZXDGSurfaceV6WrapperImpl::Configure, + }; + + zxdg_surface_v6_.reset(zxdg_shell_v6_get_xdg_surface( + connection_->shell_v6(), wayland_window_->root_surface()->surface())); + if (!zxdg_surface_v6_) { + LOG(ERROR) << "Failed to create zxdg_surface"; + return false; + } + + zxdg_surface_v6_add_listener(zxdg_surface_v6_.get(), + &zxdg_surface_v6_listener, this); + connection_->ScheduleFlush(); + return true; +} + +void ZXDGSurfaceV6WrapperImpl::AckConfigure(uint32_t serial) { + DCHECK(zxdg_surface_v6_); + zxdg_surface_v6_ack_configure(zxdg_surface_v6_.get(), serial); + connection_->wayland_window_manager()->NotifyWindowConfigured( + wayland_window_); +} + +void ZXDGSurfaceV6WrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) { + DCHECK(zxdg_surface_v6_); + zxdg_surface_v6_set_window_geometry(zxdg_surface_v6_.get(), bounds.x(), + bounds.y(), bounds.width(), + bounds.height()); +} + +// static +void ZXDGSurfaceV6WrapperImpl::Configure( + void* data, + struct zxdg_surface_v6* zxdg_surface_v6, + uint32_t serial) { + auto* surface = static_cast<ZXDGSurfaceV6WrapperImpl*>(data); + DCHECK(surface); + + surface->wayland_window_->HandleSurfaceConfigure(serial); +} + +zxdg_surface_v6* ZXDGSurfaceV6WrapperImpl::zxdg_surface() const { + DCHECK(zxdg_surface_v6_); + return zxdg_surface_v6_.get(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h new file mode 100644 index 00000000000..8fa9a289b6b --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h @@ -0,0 +1,56 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_SURFACE_V6_WRAPPER_IMPL_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_SURFACE_V6_WRAPPER_IMPL_H_ + +#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" + +#include <cstdint> +#include <string> + +#include "base/strings/string16.h" +#include "ui/ozone/platform/wayland/common/wayland_object.h" + +namespace gfx { +class Rect; +} + +namespace ui { + +class WaylandConnection; +class WaylandWindow; + +// Surface wrapper for xdg-shell-unstable-v6 +class ZXDGSurfaceV6WrapperImpl : public ShellSurfaceWrapper { + public: + ZXDGSurfaceV6WrapperImpl(WaylandWindow* wayland_window, + WaylandConnection* connection); + ZXDGSurfaceV6WrapperImpl(const ZXDGSurfaceV6WrapperImpl&) = delete; + ZXDGSurfaceV6WrapperImpl& operator=(const ZXDGSurfaceV6WrapperImpl&) = delete; + ~ZXDGSurfaceV6WrapperImpl() override; + + // ShellSurfaceWrapper overrides: + bool Initialize() override; + void AckConfigure(uint32_t serial) override; + void SetWindowGeometry(const gfx::Rect& bounds) override; + + // zxdg_surface_v6_listener + static void Configure(void* data, + struct zxdg_surface_v6* zxdg_surface_v6, + uint32_t serial); + + zxdg_surface_v6* zxdg_surface() const; + + private: + // Non-owing WaylandWindow that uses this surface wrapper. + WaylandWindow* const wayland_window_; + WaylandConnection* const connection_; + + wl::Object<zxdg_surface_v6> zxdg_surface_v6_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_SURFACE_V6_WRAPPER_IMPL_H_ diff --git a/chromium/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc new file mode 100644 index 00000000000..ea31771a618 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.cc @@ -0,0 +1,166 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h" + +#include <xdg-decoration-unstable-v1-client-protocol.h> +#include <xdg-shell-client-protocol.h> +#include <xdg-shell-unstable-v6-client-protocol.h> + +#include "base/strings/utf_string_conversions.h" +#include "ui/base/hit_test.h" +#include "ui/ozone/platform/wayland/common/wayland_util.h" +#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h" +#include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/ozone/platform/wayland/host/zxdg_surface_v6_wrapper_impl.h" + +namespace ui { + +ZXDGToplevelV6WrapperImpl::ZXDGToplevelV6WrapperImpl( + std::unique_ptr<ZXDGSurfaceV6WrapperImpl> surface, + WaylandWindow* wayland_window, + WaylandConnection* connection) + : zxdg_surface_v6_wrapper_(std::move(surface)), + wayland_window_(wayland_window), + connection_(connection) {} + +ZXDGToplevelV6WrapperImpl::~ZXDGToplevelV6WrapperImpl() = default; + +bool ZXDGToplevelV6WrapperImpl::Initialize() { + if (!connection_->shell_v6()) { + NOTREACHED() << "Wrong shell protocol"; + return false; + } + + static const zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { + &ZXDGToplevelV6WrapperImpl::ConfigureTopLevel, + &ZXDGToplevelV6WrapperImpl::CloseTopLevel, + }; + + if (!zxdg_surface_v6_wrapper_) + return false; + + zxdg_toplevel_v6_.reset( + zxdg_surface_v6_get_toplevel(zxdg_surface_v6_wrapper_->zxdg_surface())); + if (!zxdg_toplevel_v6_) { + LOG(ERROR) << "Failed to create zxdg_toplevel"; + return false; + } + zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(), + &zxdg_toplevel_v6_listener, this); + + wayland_window_->root_surface()->Commit(); + connection_->ScheduleFlush(); + return true; +} + +void ZXDGToplevelV6WrapperImpl::SetMaximized() { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_maximized(zxdg_toplevel_v6_.get()); +} + +void ZXDGToplevelV6WrapperImpl::UnSetMaximized() { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_unset_maximized(zxdg_toplevel_v6_.get()); +} + +void ZXDGToplevelV6WrapperImpl::SetFullscreen() { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_fullscreen(zxdg_toplevel_v6_.get(), nullptr); +} + +void ZXDGToplevelV6WrapperImpl::UnSetFullscreen() { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_unset_fullscreen(zxdg_toplevel_v6_.get()); +} + +void ZXDGToplevelV6WrapperImpl::SetMinimized() { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_minimized(zxdg_toplevel_v6_.get()); +} + +void ZXDGToplevelV6WrapperImpl::SurfaceMove(WaylandConnection* connection) { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_move(zxdg_toplevel_v6_.get(), connection->seat(), + connection->serial()); +} + +void ZXDGToplevelV6WrapperImpl::SurfaceResize(WaylandConnection* connection, + uint32_t hittest) { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_resize(zxdg_toplevel_v6_.get(), connection->seat(), + connection->serial(), + wl::IdentifyDirection(*connection, hittest)); +} + +void ZXDGToplevelV6WrapperImpl::SetTitle(const base::string16& title) { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_title(zxdg_toplevel_v6_.get(), + base::UTF16ToUTF8(title).c_str()); +} + +void ZXDGToplevelV6WrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) { + zxdg_surface_v6_wrapper_->SetWindowGeometry(bounds); +} + +void ZXDGToplevelV6WrapperImpl::SetMinSize(int32_t width, int32_t height) { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_min_size(zxdg_toplevel_v6_.get(), width, height); +} + +void ZXDGToplevelV6WrapperImpl::SetMaxSize(int32_t width, int32_t height) { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_max_size(zxdg_toplevel_v6_.get(), width, height); +} + +void ZXDGToplevelV6WrapperImpl::SetAppId(const std::string& app_id) { + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_set_app_id(zxdg_toplevel_v6_.get(), app_id.c_str()); +} + +void ZXDGToplevelV6WrapperImpl::SetDecoration(DecorationMode decoration) {} + +void ZXDGToplevelV6WrapperImpl::AckConfigure(uint32_t serial) { + DCHECK(zxdg_surface_v6_wrapper_); + zxdg_surface_v6_wrapper_->AckConfigure(serial); +} + +// static +void ZXDGToplevelV6WrapperImpl::ConfigureTopLevel( + void* data, + struct zxdg_toplevel_v6* zxdg_toplevel_v6, + int32_t width, + int32_t height, + struct wl_array* states) { + auto* surface = static_cast<ZXDGToplevelV6WrapperImpl*>(data); + DCHECK(surface); + + bool is_maximized = + CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); + bool is_fullscreen = + CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); + bool is_activated = + CheckIfWlArrayHasValue(states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); + + surface->wayland_window_->HandleToplevelConfigure( + width, height, is_maximized, is_fullscreen, is_activated); +} + +// static +void ZXDGToplevelV6WrapperImpl::CloseTopLevel( + void* data, + struct zxdg_toplevel_v6* zxdg_toplevel_v6) { + auto* surface = static_cast<ZXDGToplevelV6WrapperImpl*>(data); + DCHECK(surface); + surface->wayland_window_->OnCloseRequest(); +} + +ZXDGSurfaceV6WrapperImpl* ZXDGToplevelV6WrapperImpl::zxdg_surface_v6_wrapper() + const { + DCHECK(zxdg_surface_v6_wrapper_.get()); + return zxdg_surface_v6_wrapper_.get(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h new file mode 100644 index 00000000000..7727bfec007 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/host/zxdg_toplevel_v6_wrapper_impl.h @@ -0,0 +1,72 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_TOPLEVEL_V6_WRAPPER_IMPL_H_ +#define UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_TOPLEVEL_V6_WRAPPER_IMPL_H_ + +#include <memory> + +#include "base/macros.h" +#include "ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h" + +namespace ui { + +class ZXDGSurfaceV6WrapperImpl; +class WaylandConnection; +class WaylandWindow; + +// Toplevel wrapper for xdg-shell-unstable-v6 +class ZXDGToplevelV6WrapperImpl : public ShellToplevelWrapper { + public: + ZXDGToplevelV6WrapperImpl(std::unique_ptr<ZXDGSurfaceV6WrapperImpl> surface, + WaylandWindow* wayland_window, + WaylandConnection* connection); + ZXDGToplevelV6WrapperImpl(const ZXDGToplevelV6WrapperImpl&) = delete; + ZXDGToplevelV6WrapperImpl& operator=(const ZXDGToplevelV6WrapperImpl&) = + delete; + ~ZXDGToplevelV6WrapperImpl() override; + + // ShellSurfaceWrapper overrides: + bool Initialize() override; + void SetMaximized() override; + void UnSetMaximized() override; + void SetFullscreen() override; + void UnSetFullscreen() override; + void SetMinimized() override; + void SurfaceMove(WaylandConnection* connection) override; + void SurfaceResize(WaylandConnection* connection, uint32_t hittest) override; + void SetTitle(const base::string16& title) override; + void AckConfigure(uint32_t serial) override; + void SetWindowGeometry(const gfx::Rect& bounds) override; + void SetMinSize(int32_t width, int32_t height) override; + void SetMaxSize(int32_t width, int32_t height) override; + void SetAppId(const std::string& app_id) override; + void SetDecoration(DecorationMode decoration) override; + + ZXDGSurfaceV6WrapperImpl* zxdg_surface_v6_wrapper() const; + + private: + // zxdg_toplevel_v6_listener + static void ConfigureTopLevel(void* data, + struct zxdg_toplevel_v6* zxdg_toplevel_v6, + int32_t width, + int32_t height, + struct wl_array* states); + static void CloseTopLevel(void* data, + struct zxdg_toplevel_v6* zxdg_toplevel_v6); + + // Ground surface for this toplevel wrapper. + std::unique_ptr<ZXDGSurfaceV6WrapperImpl> zxdg_surface_v6_wrapper_; + + // Non-owing WaylandWindow that uses this toplevel wrapper. + WaylandWindow* const wayland_window_; + WaylandConnection* const connection_; + + // XDG Shell V6 object. + wl::Object<zxdg_toplevel_v6> zxdg_toplevel_v6_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_ZXDG_TOPLEVEL_V6_WRAPPER_IMPL_H_ diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc index ab143cad1e2..b568018a8dc 100644 --- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -16,7 +16,6 @@ #include "base/threading/sequenced_task_runner_handle.h" #include "ui/base/buildflags.h" #include "ui/base/cursor/cursor_factory.h" -#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/base/ime/linux/input_method_auralinux.h" #include "ui/base/ui_base_features.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" @@ -31,6 +30,7 @@ #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_connector.h" #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" +#include "ui/ozone/platform/wayland/host/wayland_cursor_factory.h" #include "ui/ozone/platform/wayland/host/wayland_input_method_context_factory.h" #include "ui/ozone/platform/wayland/host/wayland_menu_utils.h" #include "ui/ozone/platform/wayland/host/wayland_output_manager.h" @@ -171,7 +171,11 @@ class OzonePlatformWayland : public OzonePlatform { buffer_manager_connector_ = std::make_unique<WaylandBufferManagerConnector>( connection_->buffer_manager_host()); +#if BUILDFLAG(IS_CHROMEOS_LACROS) cursor_factory_ = std::make_unique<BitmapCursorFactoryOzone>(); +#else + cursor_factory_ = std::make_unique<WaylandCursorFactory>(connection_.get()); +#endif input_controller_ = CreateStubInputController(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); @@ -224,6 +228,8 @@ class OzonePlatformWayland : public OzonePlatform { // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988 properties->custom_frame_pref_default = true; + properties->uses_external_vulkan_image_factory = true; + // Wayland doesn't provide clients with global screen coordinates. // Instead, it forces clients to position windows relative to their top // level windows if the have child-parent relationship. In case of @@ -250,7 +256,8 @@ class OzonePlatformWayland : public OzonePlatform { properties; static bool initialized = false; if (!initialized) { - properties->supports_overlays = ui::IsWaylandOverlayDelegationEnabled(); + properties->supports_overlays = + ui::IsWaylandOverlayDelegationEnabled() && connection_->viewporter(); initialized = true; } return *properties; diff --git a/chromium/ui/ozone/platform/wayland/test/mock_surface.cc b/chromium/ui/ozone/platform/wayland/test/mock_surface.cc index d7ec1ec227b..33b9ae348c0 100644 --- a/chromium/ui/ozone/platform/wayland/test/mock_surface.cc +++ b/chromium/ui/ozone/platform/wayland/test/mock_surface.cc @@ -4,6 +4,8 @@ #include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/test_region.h" + namespace wl { namespace { @@ -20,13 +22,13 @@ void Attach(wl_client* client, void SetOpaqueRegion(wl_client* client, wl_resource* resource, wl_resource* region) { - GetUserDataAs<MockSurface>(resource)->SetOpaqueRegion(region); + GetUserDataAs<MockSurface>(resource)->SetOpaqueRegionImpl(region); } void SetInputRegion(wl_client* client, wl_resource* resource, wl_resource* region) { - GetUserDataAs<MockSurface>(resource)->SetInputRegion(region); + GetUserDataAs<MockSurface>(resource)->SetInputRegionImpl(region); } void Damage(wl_client* client, @@ -100,6 +102,35 @@ MockSurface* MockSurface::FromResource(wl_resource* resource) { return GetUserDataAs<MockSurface>(resource); } +void MockSurface::SetOpaqueRegionImpl(wl_resource* region) { + if (!region) { + opaque_region_ = gfx::Rect(-1, -1, 0, 0); + return; + } + auto bounds = GetUserDataAs<TestRegion>(region)->getBounds(); + opaque_region_ = + gfx::Rect(bounds.fLeft, bounds.fTop, bounds.fRight - bounds.fLeft, + bounds.fBottom - bounds.fTop); + + SetOpaqueRegion(region); +} + +void MockSurface::SetInputRegionImpl(wl_resource* region) { + // It is unsafe to always treat |region| as a valid pointer. + // According to the protocol about wl_surface::set_input_region + // "A NULL wl_region cuases the input region to be set to infinite." + if (!region) { + input_region_ = gfx::Rect(-1, -1, 0, 0); + return; + } + auto bounds = GetUserDataAs<TestRegion>(region)->getBounds(); + input_region_ = + gfx::Rect(bounds.fLeft, bounds.fTop, bounds.fRight - bounds.fLeft, + bounds.fBottom - bounds.fTop); + + SetInputRegion(region); +} + void MockSurface::AttachNewBuffer(wl_resource* buffer_resource, int32_t x, int32_t y) { diff --git a/chromium/ui/ozone/platform/wayland/test/mock_surface.h b/chromium/ui/ozone/platform/wayland/test/mock_surface.h index 5c4e5cfb795..fd682b8fa87 100644 --- a/chromium/ui/ozone/platform/wayland/test/mock_surface.h +++ b/chromium/ui/ozone/platform/wayland/test/mock_surface.h @@ -56,6 +56,9 @@ class MockSurface : public ServerObject { void set_viewport(TestViewport* viewport) { viewport_ = viewport; } TestViewport* viewport() { return viewport_; } + gfx::Rect opaque_region() const { return opaque_region_; } + gfx::Rect input_region() const { return input_region_; } + void set_frame_callback(wl_resource* callback_resource) { DCHECK(!frame_callback_); frame_callback_ = callback_resource; @@ -66,6 +69,8 @@ class MockSurface : public ServerObject { bool has_role() const { return !!xdg_surface_ || !!sub_surface_; } + void SetOpaqueRegionImpl(wl_resource* region); + void SetInputRegionImpl(wl_resource* region); void AttachNewBuffer(wl_resource* buffer_resource, int32_t x, int32_t y); void DestroyPrevAttachedBuffer(); void ReleaseBuffer(wl_resource* buffer); @@ -75,6 +80,8 @@ class MockSurface : public ServerObject { MockXdgSurface* xdg_surface_ = nullptr; TestSubSurface* sub_surface_ = nullptr; TestViewport* viewport_ = nullptr; + gfx::Rect opaque_region_ = {-1, -1, 0, 0}; + gfx::Rect input_region_ = {-1, -1, 0, 0}; wl_resource* frame_callback_ = nullptr; diff --git a/chromium/ui/ozone/platform/wayland/test/test_compositor.cc b/chromium/ui/ozone/platform/wayland/test/test_compositor.cc index 9a6244b0cb3..80fab9ab4bb 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_compositor.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_compositor.cc @@ -12,9 +12,9 @@ namespace wl { -namespace { +constexpr uint32_t TestCompositor::kVersion; -constexpr uint32_t kCompositorVersion = 4; +namespace { void CreateSurface(wl_client* client, wl_resource* compositor_resource, @@ -42,11 +42,9 @@ const struct wl_compositor_interface kTestCompositorImpl = { }; TestCompositor::TestCompositor() - : GlobalObject(&wl_compositor_interface, - &kTestCompositorImpl, - kCompositorVersion) {} + : GlobalObject(&wl_compositor_interface, &kTestCompositorImpl, kVersion) {} -TestCompositor::~TestCompositor() {} +TestCompositor::~TestCompositor() = default; void TestCompositor::AddSurface(MockSurface* surface) { surfaces_.push_back(surface); diff --git a/chromium/ui/ozone/platform/wayland/test/test_compositor.h b/chromium/ui/ozone/platform/wayland/test/test_compositor.h index 1b54cfb4173..6cc6c0f5759 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_compositor.h +++ b/chromium/ui/ozone/platform/wayland/test/test_compositor.h @@ -17,6 +17,8 @@ class MockSurface; // Manage wl_compositor object. class TestCompositor : public GlobalObject { public: + static constexpr uint32_t kVersion = 4; + TestCompositor(); ~TestCompositor() override; diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc b/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc index 73736c7b002..861e685ae9a 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc @@ -54,7 +54,8 @@ void DataOfferSetActions(wl_client* client, wl_resource* resource, uint32_t dnd_actions, uint32_t preferred_action) { - NOTIMPLEMENTED(); + GetUserDataAs<TestDataOffer>(resource)->SetActions(dnd_actions, + preferred_action); } } // namespace @@ -85,4 +86,19 @@ void TestDataOffer::OnOffer(const std::string& mime_type, wl_data_offer_send_offer(resource(), mime_type.c_str()); } +void TestDataOffer::SetActions(uint32_t dnd_actions, + uint32_t preferred_action) { + client_supported_actions_ = dnd_actions; + client_preferred_action_ = preferred_action; + OnAction(client_preferred_action_); +} + +void TestDataOffer::OnSourceActions(uint32_t source_actions) { + wl_data_offer_send_source_actions(resource(), source_actions); +} + +void TestDataOffer::OnAction(uint32_t dnd_action) { + wl_data_offer_send_action(resource(), dnd_action); +} + } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_offer.h b/chromium/ui/ozone/platform/wayland/test/test_data_offer.h index 6b96582da7d..77c389c6919 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_data_offer.h +++ b/chromium/ui/ozone/platform/wayland/test/test_data_offer.h @@ -34,11 +34,21 @@ class TestDataOffer : public ServerObject { void Receive(const std::string& mime_type, base::ScopedFD fd); void OnOffer(const std::string& mime_type, ui::PlatformClipboard::Data data); + void SetActions(uint32_t dnd_actions, uint32_t preferred_action); + + void OnSourceActions(uint32_t source_actions); + void OnAction(uint32_t dnd_action); + + uint32_t supported_actions() const { return client_supported_actions_; } + uint32_t preferred_action() const { return client_preferred_action_; } private: const scoped_refptr<base::SequencedTaskRunner> task_runner_; ui::PlatformClipboard::DataMap data_to_offer_; + uint32_t client_supported_actions_ = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + uint32_t client_preferred_action_ = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + base::WeakPtrFactory<TestDataOffer> write_data_weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(TestDataOffer); diff --git a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc index 6cf79816b5c..7cf3193cbc2 100644 --- a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc +++ b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc @@ -36,6 +36,10 @@ TestWaylandServerThread::~TestWaylandServerThread() { if (client_) wl_client_destroy(client_); + // Stop watching the descriptor here to guarantee that no new events will come + // during or after the destruction of the display. + controller_.StopWatchingFileDescriptor(); + Resume(); Stop(); } @@ -146,7 +150,8 @@ TestWaylandServerThread::CreateMessagePump() { void TestWaylandServerThread::OnFileCanReadWithoutBlocking(int fd) { wl_event_loop_dispatch(event_loop_, 0); - wl_display_flush_clients(display_.get()); + if (display_) + wl_display_flush_clients(display_.get()); } void TestWaylandServerThread::OnFileCanWriteWithoutBlocking(int fd) {} diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_drag_drop_test.cc b/chromium/ui/ozone/platform/wayland/test/wayland_drag_drop_test.cc new file mode 100644 index 00000000000..259e8541c78 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/wayland_drag_drop_test.cc @@ -0,0 +1,127 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/wayland/test/wayland_drag_drop_test.h" + +#include <wayland-util.h> + +#include <cstdint> + +#include "base/callback.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/point.h" +#include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "ui/ozone/platform/wayland/test/mock_surface.h" +#include "ui/ozone/platform/wayland/test/test_data_device.h" +#include "ui/ozone/platform/wayland/test/test_data_device_manager.h" +#include "ui/ozone/platform/wayland/test/test_data_offer.h" +#include "ui/ozone/platform/wayland/test/test_data_source.h" + +namespace ui { + +WaylandDragDropTest::WaylandDragDropTest() = default; + +void WaylandDragDropTest::SendDndEnter(WaylandWindow* window, + const gfx::Point& location) { + auto* surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); + OfferAndEnter(surface, location); +} + +void WaylandDragDropTest::SendDndLeave() { + data_device_manager_->data_device()->OnLeave(); +} + +void WaylandDragDropTest::SendDndMotion(const gfx::Point& location) { + EXPECT_TRUE(data_source_); + wl_fixed_t x = wl_fixed_from_int(location.x()); + wl_fixed_t y = wl_fixed_from_int(location.y()); + data_device_manager_->data_device()->OnMotion(NextTime(), x, y); +} + +void WaylandDragDropTest::SendDndCancelled() { + EXPECT_TRUE(data_source_); + data_source_->OnCancelled(); +} + +void WaylandDragDropTest::ReadData( + const std::string& mime_type, + wl::TestDataSource::ReadDataCallback callback) { + ASSERT_TRUE(data_source_); + data_source_->ReadData(mime_type, std::move(callback)); +} + +void WaylandDragDropTest::SetUp() { + WaylandTest::SetUp(); + Sync(); + + data_device_manager_ = server_.data_device_manager(); + ASSERT_TRUE(data_device_manager_); + + data_source_ = nullptr; + data_device_manager_->data_device()->set_delegate(this); +} + +void WaylandDragDropTest::TearDown() { + data_device_manager_->data_device()->set_delegate(nullptr); + data_device_manager_ = nullptr; +} + +// wl::TestDataDevice::Delegate: +void WaylandDragDropTest::StartDrag(wl::TestDataSource* source, + wl::MockSurface* origin, + uint32_t serial) { + EXPECT_FALSE(data_source_); + data_source_ = source; + OfferAndEnter(origin, {}); +} + +uint32_t WaylandDragDropTest::NextSerial() const { + static uint32_t serial = 0; + return ++serial; +} + +uint32_t WaylandDragDropTest::NextTime() const { + static uint32_t timestamp = 0; + return ++timestamp; +} + +void WaylandDragDropTest::OfferAndEnter(wl::MockSurface* surface, + const gfx::Point& location) { + ASSERT_TRUE(data_source_); + auto* data_device = data_device_manager_->data_device(); + + // Emulate server sending an wl_data_device::offer event. + auto* data_offer = data_device->OnDataOffer(); + for (const auto& mime_type : data_source_->mime_types()) + data_offer->OnOffer(mime_type, {}); + + // Emulate server sending an wl_data_device::enter event. + wl_data_device_send_enter( + data_device->resource(), NextSerial(), surface->resource(), + wl_fixed_from_int(location.x()), wl_fixed_from_int(location.y()), + data_offer->resource()); +} + +void WaylandDragDropTest::ScheduleTestTask(base::OnceClosure test_task) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&WaylandDragDropTest::RunTestTask, + base::Unretained(this), std::move(test_task))); +} + +void WaylandDragDropTest::RunTestTask(base::OnceClosure test_task) { + Sync(); + + // The data source is created asynchronously by the drag controller. If it is + // null at this point, it means that the task for that has not yet executed, + // so try again a bit later. + if (!data_device_manager_->data_source()) { + ScheduleTestTask(std::move(test_task)); + return; + } + + std::move(test_task).Run(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_drag_drop_test.h b/chromium/ui/ozone/platform/wayland/test/wayland_drag_drop_test.h new file mode 100644 index 00000000000..3455909c765 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/test/wayland_drag_drop_test.h @@ -0,0 +1,68 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_WAYLAND_DRAG_DROP_TEST_H_ +#define UI_OZONE_PLATFORM_WAYLAND_TEST_WAYLAND_DRAG_DROP_TEST_H_ + +#include "base/callback_forward.h" +#include "ui/ozone/platform/wayland/test/test_data_device.h" +#include "ui/ozone/platform/wayland/test/test_data_source.h" +#include "ui/ozone/platform/wayland/test/wayland_test.h" + +namespace gfx { +class Point; +} + +namespace wl { +class MockSurface; +class TestDataDeviceManager; +} // namespace wl + +namespace ui { + +class WaylandWindow; + +// Base class for Wayland drag-and-drop tests. Public methods allow test code to +// emulate dnd-related events from the test compositor and can be used in both +// data and window dragging test cases. +class WaylandDragDropTest : public WaylandTest, + public wl::TestDataDevice::Delegate { + public: + WaylandDragDropTest(); + WaylandDragDropTest(const WaylandDragDropTest&) = delete; + WaylandDragDropTest& operator=(const WaylandDragDropTest&) = delete; + + // These are public for convenience, as they must be callable from lambda + // functions, usually posted to task queue while the drag loop runs. + void SendDndEnter(WaylandWindow* window, const gfx::Point& location); + void SendDndLeave(); + void SendDndMotion(const gfx::Point& location); + void SendDndCancelled(); + void ReadData(const std::string& mime_type, + wl::TestDataSource::ReadDataCallback callback); + + protected: + // WaylandTest: + void SetUp() override; + void TearDown() override; + + // wl::TestDataDevice::Delegate: + void StartDrag(wl::TestDataSource* source, + wl::MockSurface* origin, + uint32_t serial) override; + + void OfferAndEnter(wl::MockSurface* surface, const gfx::Point& location); + uint32_t NextSerial() const; + uint32_t NextTime() const; + void ScheduleTestTask(base::OnceClosure test_task); + void RunTestTask(base::OnceClosure test_task); + + // Server objects + wl::TestDataDeviceManager* data_device_manager_; + wl::TestDataSource* data_source_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_WAYLAND_DRAG_DROP_TEST_H_ diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc index 58e395d8e82..b226e31dbc3 100644 --- a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc +++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc @@ -69,6 +69,7 @@ void WaylandTest::SetUp() { properties.type = PlatformWindowType::kWindow; window_ = WaylandWindow::Create(&delegate_, connection_.get(), std::move(properties)); + window_->set_update_visual_size_immediately(true); ASSERT_NE(widget_, gfx::kNullAcceleratedWidget); window_->Show(false); @@ -118,17 +119,17 @@ void WaylandTest::SendConfigureEvent(wl::MockXdgSurface* xdg_surface, // Please note that toplevel surfaces may not exist if the surface was created // for the popup role. if (GetParam() == kXdgShellV6) { - zxdg_surface_v6_send_configure(xdg_surface->resource(), serial); if (xdg_surface->xdg_toplevel()) { zxdg_toplevel_v6_send_configure(xdg_surface->xdg_toplevel()->resource(), width, height, states); } + zxdg_surface_v6_send_configure(xdg_surface->resource(), serial); } else { - xdg_surface_send_configure(xdg_surface->resource(), serial); if (xdg_surface->xdg_toplevel()) { xdg_toplevel_send_configure(xdg_surface->xdg_toplevel()->resource(), width, height, states); } + xdg_surface_send_configure(xdg_surface->resource(), serial); } } diff --git a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc index 8721395d80b..a8767ae8265 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc @@ -84,7 +84,7 @@ class WaylandBufferManagerTest : public WaylandTest { // callback and bind the interface again if the manager failed. manager_host_->SetTerminateGpuCallback(callback_.Get()); auto interface_ptr = manager_host_->BindInterface(); - buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, false); + buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, false, false); } protected: @@ -119,7 +119,7 @@ class WaylandBufferManagerTest : public WaylandTest { // Recreate the gpu side manager (the production code does the // same). buffer_manager_gpu_ = std::make_unique<WaylandBufferManagerGpu>(); - buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, + buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, false, false); })); } @@ -341,7 +341,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds()); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds(), + window_->GetBounds()); CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/, kBufferId1); @@ -364,7 +365,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds()); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds(), + window_->GetBounds()); DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget, kBufferId1, true /*fail*/); @@ -385,7 +387,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) { CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); // Attach to a surface. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds()); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds(), + window_->GetBounds()); // Created non-attached buffer as well. CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, @@ -418,7 +421,32 @@ TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) { // Can't commit for non-existing buffer id. SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/); buffer_manager_gpu_->CommitBuffer(window_->GetWidget(), 5u, - window_->GetBounds()); + window_->GetBounds(), window_->GetBounds()); + + Sync(); +} + +TEST_P(WaylandBufferManagerTest, CommitOverlaysNonExistingBufferId) { + EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, 1u); + + // Can't commit for non-existing buffer id. + SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/); + + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs; + overlay_configs.push_back(ui::ozone::mojom::WaylandOverlayConfig::New( + INT32_MIN, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, 1u, + window_->GetBounds(), gfx::RectF(), window_->GetBounds(), false, + gfx::GpuFenceHandle())); + + // Non-existing buffer id + overlay_configs.push_back(ui::ozone::mojom::WaylandOverlayConfig::New( + 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, 2u, + window_->GetBounds(), gfx::RectF(), window_->GetBounds(), false, + gfx::GpuFenceHandle())); + + buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), + std::move(overlay_configs)); Sync(); } @@ -431,7 +459,7 @@ TEST_P(WaylandBufferManagerTest, CommitBufferNullWidget) { // Can't commit for non-existing widget. SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/); buffer_manager_gpu_->CommitBuffer(gfx::kNullAcceleratedWidget, kBufferId, - window_->GetBounds()); + window_->GetBounds(), window_->GetBounds()); Sync(); } @@ -474,7 +502,7 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) { ASSERT_TRUE(!connection_->presentation()); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -485,7 +513,7 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) { Sync(); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); Sync(); @@ -514,7 +542,7 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) { .Times(1); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -591,7 +619,7 @@ TEST_P(WaylandBufferManagerTest, EXPECT_CALL(mock_surface_gpu, OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK)) .Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); @@ -600,7 +628,7 @@ TEST_P(WaylandBufferManagerTest, mock_wp_presentation->set_presentation_callback(nullptr); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); @@ -621,7 +649,7 @@ TEST_P(WaylandBufferManagerTest, // Commit buffer 3 then send the presentation callback for it. This should // not call OnPresentation as OnSubmission hasn't been called yet. EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds, bounds); mock_surface->SendFrameCallback(); mock_wp_presentation->SendPresentationCallback(); Sync(); @@ -703,7 +731,7 @@ TEST_P(WaylandBufferManagerTest, EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); // Commit first buffer - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -721,7 +749,7 @@ TEST_P(WaylandBufferManagerTest, EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); // Commit second buffer - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); Sync(); @@ -741,7 +769,7 @@ TEST_P(WaylandBufferManagerTest, EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); // Commit third buffer - buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds, bounds); Sync(); @@ -826,7 +854,7 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { EXPECT_CALL(*mock_surface, Commit()).Times(0); buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId, - window_->GetBounds()); + window_->GetBounds(), window_->GetBounds()); Sync(); EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); @@ -859,7 +887,7 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) { EXPECT_CALL(*mock_surface, Commit()).Times(0); buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId2, - window_->GetBounds()); + window_->GetBounds(), window_->GetBounds()); Sync(); @@ -925,14 +953,16 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) { EXPECT_CALL(*mock_surface, Frame(_)).Times(0); EXPECT_CALL(*mock_surface, Commit()).Times(0); - buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId, - window_->GetBounds()); + buffer_manager_gpu_->CommitBuffer( + widget, kDmabufBufferId, window_->GetBounds(), window_->GetBounds()); Sync(); if (type != PlatformWindowType::kTooltip) { DCHECK(mock_surface->xdg_surface()); ActivateSurface(mock_surface->xdg_surface()); } else { + // WaylandAuxiliaryWindow uses the focused window as a parent. + window_->SetPointerFocus(true); // See the comment near Show() call above. temp_window->Show(false); } @@ -943,6 +973,7 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) { Sync(); + window_->SetPointerFocus(false); temp_window.reset(); DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId, false /*fail*/); @@ -991,7 +1022,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) { EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -1015,7 +1046,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) { EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); Sync(); @@ -1039,7 +1070,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) { .Times(0); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(0); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds, bounds); Sync(); @@ -1075,7 +1106,7 @@ TEST_P(WaylandBufferManagerTest, DestroyBufferForDestroyedWindow) { Sync(); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId, + buffer_manager_gpu_->CommitBuffer(widget, kBufferId, temp_window->GetBounds(), temp_window->GetBounds()); Sync(); @@ -1108,7 +1139,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionSingleBuffer) { temp_window.reset(); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId, bounds, bounds); Sync(); @@ -1149,7 +1180,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) { EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(1); EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -1169,7 +1200,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) { .Times(1); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); Sync(); @@ -1181,7 +1212,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) { EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); temp_window.reset(); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -1217,7 +1248,7 @@ TEST_P(WaylandBufferManagerTest, DestroyBufferCommittedTwiceInARow) { .Times(1); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); @@ -1226,11 +1257,11 @@ TEST_P(WaylandBufferManagerTest, DestroyBufferCommittedTwiceInARow) { EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0); EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); @@ -1281,7 +1312,7 @@ TEST_P(WaylandBufferManagerTest, ReleaseBufferCommittedTwiceInARow) { .Times(1); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); @@ -1291,11 +1322,11 @@ TEST_P(WaylandBufferManagerTest, ReleaseBufferCommittedTwiceInARow) { EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0); EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); @@ -1346,7 +1377,7 @@ TEST_P(WaylandBufferManagerTest, ReleaseOrderDifferentToCommitOrder) { EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu); @@ -1357,12 +1388,12 @@ TEST_P(WaylandBufferManagerTest, ReleaseOrderDifferentToCommitOrder) { EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0); EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(2); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); auto* wl_buffer2 = mock_surface->attached_buffer(); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds, bounds); mock_surface->SendFrameCallback(); Sync(); @@ -1432,7 +1463,7 @@ TEST_P(WaylandBufferManagerTest, .Times(1); EXPECT_CALL(*mock_surface, Commit()).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -1454,7 +1485,7 @@ TEST_P(WaylandBufferManagerTest, EXPECT_CALL(*mock_surface, Commit()).Times(1); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); Sync(); @@ -1496,7 +1527,7 @@ TEST_P(WaylandBufferManagerTest, EXPECT_CALL(*mock_surface, Commit()).Times(1); // Commit second buffer now. - buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds, bounds); Sync(); @@ -1530,7 +1561,7 @@ TEST_P(WaylandBufferManagerTest, .Times(1); EXPECT_CALL(*mock_surface, Commit()).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); @@ -1579,12 +1610,77 @@ TEST_P(WaylandBufferManagerTest, OnSubmissionCalledForSingleBuffer) { .Times(1); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); - buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds); + buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds, bounds); Sync(); DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/); } +// Tests that when CommitOverlays(), root_surface can only be committed once all +// overlays in the frame are committed. +TEST_P(WaylandBufferManagerTest, RootSurfaceIsCommittedLast) { + constexpr uint32_t kBufferId1 = 1; + constexpr uint32_t kBufferId2 = 2; + constexpr uint32_t kBufferId3 = 3; + + const gfx::AcceleratedWidget widget = window_->GetWidget(); + const gfx::Rect bounds = window_->GetBounds(); + + MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget); + + auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); + EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(3); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2); + CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3); + + Sync(); + + // Ack creation for only the first 2 wl_buffers. + LOG(ERROR) << linux_dmabuf->buffer_params().size(); + zwp_linux_buffer_params_v1_send_created( + linux_dmabuf->buffer_params()[0]->resource(), + linux_dmabuf->buffer_params()[0]->buffer_resource()); + zwp_linux_buffer_params_v1_send_created( + linux_dmabuf->buffer_params()[1]->resource(), + linux_dmabuf->buffer_params()[1]->buffer_resource()); + + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); + + // root_surface shall not be committed as one of its subsurface is not + // committed yet due to pending wl_buffer creation. + EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0); + EXPECT_CALL(*mock_surface, Frame(_)).Times(0); + EXPECT_CALL(*mock_surface, Commit()).Times(0); + + std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs; + overlay_configs.push_back(ui::ozone::mojom::WaylandOverlayConfig::New( + INT32_MIN, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, kBufferId1, + bounds, gfx::RectF(), bounds, false, gfx::GpuFenceHandle())); + overlay_configs.push_back(ui::ozone::mojom::WaylandOverlayConfig::New( + 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, kBufferId2, bounds, + gfx::RectF(), bounds, false, gfx::GpuFenceHandle())); + overlay_configs.push_back(ui::ozone::mojom::WaylandOverlayConfig::New( + 1, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, kBufferId3, bounds, + gfx::RectF(), bounds, false, gfx::GpuFenceHandle())); + buffer_manager_gpu_->CommitOverlays(window_->GetWidget(), + std::move(overlay_configs)); + Sync(); + testing::Mock::VerifyAndClearExpectations(mock_surface); + + // Once wl_buffer is created, all subsurfaces are committed, hence + // root_surface can be committed. + zwp_linux_buffer_params_v1_send_created( + linux_dmabuf->buffer_params()[0]->resource(), + linux_dmabuf->buffer_params()[0]->buffer_resource()); + EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); + EXPECT_CALL(*mock_surface, Frame(_)).Times(1); + EXPECT_CALL(*mock_surface, Commit()).Times(1); + + Sync(); +} + INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest, WaylandBufferManagerTest, ::testing::Values(kXdgShellStable)); diff --git a/chromium/ui/ozone/platform/x11/BUILD.gn b/chromium/ui/ozone/platform/x11/BUILD.gn index 731c7d7c0a0..7cf0bfdff6c 100644 --- a/chromium/ui/ozone/platform/x11/BUILD.gn +++ b/chromium/ui/ozone/platform/x11/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chromeos/ui_mode.gni") import("//build/config/linux/gtk/gtk.gni") import("//gpu/vulkan/features.gni") import("//ui/base/ui_features.gni") @@ -37,6 +38,7 @@ source_set("x11") { deps = [ "//base", "//build:chromecast_buildflags", + "//build:chromeos_buildflags", "//gpu/vulkan:buildflags", "//skia", "//ui/base:base", @@ -57,6 +59,8 @@ source_set("x11") { "//ui/events/x", "//ui/gfx", "//ui/gfx/geometry", + "//ui/gfx/linux:gbm", + "//ui/gfx/linux:gpu_memory_buffer_support_x11", "//ui/gfx/x", "//ui/gl", "//ui/ozone:ozone_base", @@ -66,7 +70,7 @@ source_set("x11") { "//ui/platform_window/x11", ] - if (is_chromeos) { + if (is_chromeos_ash) { deps += [ "//ui/base/ime/chromeos" ] } else { sources += [ diff --git a/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc b/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc index 13d2b2d9048..d7346528ff0 100644 --- a/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc +++ b/chromium/ui/ozone/platform/x11/client_native_pixmap_factory_x11.cc @@ -4,12 +4,12 @@ #include "ui/ozone/platform/x11/client_native_pixmap_factory_x11.h" -#include "ui/ozone/common/stub_client_native_pixmap_factory.h" +#include "ui/gfx/linux/client_native_pixmap_factory_dmabuf.h" namespace ui { gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryX11() { - return CreateStubClientNativePixmapFactory(); + return gfx::CreateClientNativePixmapFactoryDmabuf(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc index 3c8e0d04ce4..d5612033adc 100644 --- a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc +++ b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc @@ -38,4 +38,17 @@ bool GLEGLUtilityX11::X11DoesVisualHaveAlphaForTest() const { return ui::DoesVisualHaveAlphaForTest(); } +bool GLEGLUtilityX11::HasVisualManager() { + return true; +} + +bool GLEGLUtilityX11::UpdateVisualsOnGpuInfoChanged( + bool software_rendering, + uint32_t default_visual_id, + uint32_t transparent_visual_id) { + return ui::UpdateVisualsOnGpuInfoChanged( + software_rendering, static_cast<x11::VisualId>(default_visual_id), + static_cast<x11::VisualId>(transparent_visual_id)); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h index 807d3a7d20c..1ca2ddf3878 100644 --- a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h +++ b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h @@ -27,6 +27,10 @@ class GLEGLUtilityX11 : public PlatformGLEGLUtility { void CollectGpuExtraInfo(bool enable_native_gpu_memory_buffers, gfx::GpuExtraInfo& gpu_extra_info) const override; bool X11DoesVisualHaveAlphaForTest() const override; + bool HasVisualManager() override; + bool UpdateVisualsOnGpuInfoChanged(bool software_rendering, + uint32_t default_visual_id, + uint32_t transparent_visual_id) override; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc index 399bd0678bb..455f238ac68 100644 --- a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -9,10 +9,13 @@ #include "base/message_loop/message_pump_type.h" #include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/thread_pool.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "ui/base/buildflags.h" #include "ui/base/cursor/cursor_factory.h" #include "ui/base/dragdrop/os_exchange_data_provider_factory.h" @@ -25,6 +28,7 @@ #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/x11/x11_event_source.h" +#include "ui/gfx/linux/gpu_memory_buffer_support_x11.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/common/stub_overlay_manager.h" #include "ui/ozone/platform/x11/gl_egl_utility_x11.h" @@ -41,7 +45,7 @@ #include "ui/platform_window/platform_window_init_properties.h" #include "ui/platform_window/x11/x11_window.h" -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) #include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h" #include "ui/base/ime/chromeos/input_method_chromeos.h" #else @@ -123,13 +127,15 @@ class OzonePlatformX11 : public OzonePlatform, } PlatformGLEGLUtility* GetPlatformGLEGLUtility() override { + if (!gl_egl_utility_) + gl_egl_utility_ = std::make_unique<GLEGLUtilityX11>(); return gl_egl_utility_.get(); } std::unique_ptr<InputMethod> CreateInputMethod( internal::InputMethodDelegate* delegate, gfx::AcceleratedWidget) override { -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) return std::make_unique<InputMethodChromeOS>(delegate); #else // This method is used by upper layer components (e.g: GtkUi) to determine @@ -147,7 +153,7 @@ class OzonePlatformX11 : public OzonePlatform, } std::unique_ptr<OSExchangeDataProvider> CreateProvider() override { -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) return std::make_unique<OSExchangeDataProviderNonBacked>(); #else return std::make_unique<X11OSExchangeDataProviderOzone>(); @@ -169,9 +175,12 @@ class OzonePlatformX11 : public OzonePlatform, properties->message_pump_type_for_viz_compositor = base::MessagePumpType::UI; properties->supports_vulkan_swap_chain = true; + properties->uses_external_vulkan_image_factory = true; + properties->skia_can_fall_back_to_x11 = true; properties->platform_shows_drag_image = false; properties->supports_global_application_menus = true; properties->app_modal_dialogs_use_event_blocker = true; + properties->fetch_buffer_formats_for_gmb_on_gpu = true; initialised = true; } @@ -179,6 +188,13 @@ class OzonePlatformX11 : public OzonePlatform, return *properties; } + bool IsNativePixmapConfigSupported(gfx::BufferFormat format, + gfx::BufferUsage usage) const override { + // Native pixmap support is determined on gpu process via gpu extra info + // that gets this information from GpuMemoryBufferSupportX11. + return false; + } + void InitializeUI(const InitParams& params) override { InitializeCommon(params); CreatePlatformEventSource(); @@ -215,7 +231,13 @@ class OzonePlatformX11 : public OzonePlatform, void InitializeGPU(const InitParams& params) override { InitializeCommon(params); - + if (params.enable_native_gpu_memory_buffers) { + base::ThreadPool::PostTask( + FROM_HERE, base::BindOnce([]() { + SCOPED_UMA_HISTOGRAM_TIMER("Linux.X11.GbmSupportX11CreationTime"); + ui::GpuMemoryBufferSupportX11::GetInstance(); + })); + } // In single process mode either the UI thread will create an event source // or it's a test and an event source isn't desired. if (!params.single_process) @@ -227,7 +249,6 @@ class OzonePlatformX11 : public OzonePlatform, connection->DetachFromSequence(); surface_factory_ozone_ = std::make_unique<X11SurfaceFactory>(std::move(connection)); - gl_egl_utility_ = std::make_unique<GLEGLUtilityX11>(); } void PostMainMessageLoopStart( @@ -253,7 +274,7 @@ class OzonePlatformX11 : public OzonePlatform, // If opening the connection failed there is nothing we can do. Crash here // instead of crashing later. If you are crashing here, make sure there is // an X server running and $DISPLAY is set. - CHECK(x11::Connection::Get()) << "Missing X server or $DISPLAY"; + CHECK(x11::Connection::Get()->Ready()) << "Missing X server or $DISPLAY"; common_initialized_ = true; } diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc index 40945c6dbf1..5944973e00c 100644 --- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc @@ -7,11 +7,12 @@ #include <algorithm> #include <vector> +#include "base/containers/contains.h" #include "base/containers/span.h" #include "base/logging.h" -#include "base/stl_util.h" #include "ui/base/clipboard/clipboard_constants.h" #include "ui/base/x/x11_util.h" +#include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/x/extension_manager.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/xproto.h" @@ -78,12 +79,12 @@ struct X11ClipboardOzone::SelectionState { }; X11ClipboardOzone::X11ClipboardOzone() - : atom_clipboard_(gfx::GetAtom(kClipboard)), - atom_targets_(gfx::GetAtom(kTargets)), - atom_timestamp_(gfx::GetAtom(kTimestamp)), - x_property_(gfx::GetAtom(kChromeSelection)), + : atom_clipboard_(x11::GetAtom(kClipboard)), + atom_targets_(x11::GetAtom(kTargets)), + atom_timestamp_(x11::GetAtom(kTimestamp)), + x_property_(x11::GetAtom(kChromeSelection)), connection_(x11::Connection::Get()), - x_window_(CreateDummyWindow("Chromium Clipboard Window")) { + x_window_(x11::CreateDummyWindow("Chromium Clipboard Window")) { connection_->xfixes().QueryVersion( {x11::XFixes::major_version, x11::XFixes::minor_version}); if (!connection_->xfixes().present()) @@ -91,7 +92,7 @@ X11ClipboardOzone::X11ClipboardOzone() using_xfixes_ = true; // Register to receive standard X11 events. - X11EventSource::GetInstance()->AddXEventDispatcher(this); + connection_->AddEventObserver(this); for (auto atom : {atom_clipboard_, x11::Atom::PRIMARY}) { // Register the selection state. @@ -105,29 +106,31 @@ X11ClipboardOzone::X11ClipboardOzone() } X11ClipboardOzone::~X11ClipboardOzone() { - X11EventSource::GetInstance()->RemoveXEventDispatcher(this); + connection_->RemoveEventObserver(this); } -bool X11ClipboardOzone::DispatchXEvent(x11::Event* xev) { - if (auto* request = xev->As<x11::SelectionRequestEvent>()) - return request->owner == x_window_ && OnSelectionRequest(*request); - if (auto* notify = xev->As<x11::SelectionNotifyEvent>()) - return notify->requestor == x_window_ && OnSelectionNotify(*notify); - if (auto* notify = xev->As<x11::XFixes::SelectionNotifyEvent>()) - return notify->window == x_window_ && OnSetSelectionOwnerNotify(*notify); - - return false; +void X11ClipboardOzone::OnEvent(const x11::Event& xev) { + if (auto* request = xev.As<x11::SelectionRequestEvent>()) { + if (request->owner == x_window_) + OnSelectionRequest(*request); + } else if (auto* notify = xev.As<x11::SelectionNotifyEvent>()) { + if (notify->requestor == x_window_) + OnSelectionNotify(*notify); + } else if (auto* notify = xev.As<x11::XFixes::SelectionNotifyEvent>()) { + if (notify->window == x_window_) + OnSetSelectionOwnerNotify(*notify); + } } // We are the clipboard owner, and a remote peer has requested either: // TARGETS: List of mime types that we support for the clipboard. // TIMESTAMP: Time when we took ownership of the clipboard. // <mime-type>: Mime type to receive clipboard as. -bool X11ClipboardOzone::OnSelectionRequest( +void X11ClipboardOzone::OnSelectionRequest( const x11::SelectionRequestEvent& event) { // The property must be set. if (event.property == x11::Atom::None) - return false; + return; // target=TARGETS. auto& selection_state = @@ -145,14 +148,13 @@ bool X11ClipboardOzone::OnSelectionRequest( ExpandTypes(&targets); std::vector<x11::Atom> atoms; for (auto& entry : targets) - atoms.push_back(gfx::GetAtom(entry.c_str())); - ui::SetArrayProperty(event.requestor, event.property, x11::Atom::ATOM, - atoms); + atoms.push_back(x11::GetAtom(entry.c_str())); + SetArrayProperty(event.requestor, event.property, x11::Atom::ATOM, atoms); } else if (target == atom_timestamp_) { // target=TIMESTAMP. - ui::SetProperty(event.requestor, event.property, x11::Atom::INTEGER, - selection_state.acquired_selection_timestamp); + SetProperty(event.requestor, event.property, x11::Atom::INTEGER, + selection_state.acquired_selection_timestamp); } else { // Send clipboard data. std::string target_name; @@ -170,8 +172,8 @@ bool X11ClipboardOzone::OnSelectionRequest( } auto it = offer_data_map.find(key); if (it != offer_data_map.end()) { - ui::SetArrayProperty(event.requestor, event.property, event.target, - it->second->data()); + SetArrayProperty(event.requestor, event.property, event.target, + it->second->data()); } } @@ -185,20 +187,19 @@ bool X11ClipboardOzone::OnSelectionRequest( }; x11::SendEvent(selection_event, selection_event.requestor, x11::EventMask::NoEvent); - return true; } // A remote peer owns the clipboard. This event is received in response to // our request for TARGETS (GetAvailableMimeTypes), or a specific mime type // (RequestClipboardData). -bool X11ClipboardOzone::OnSelectionNotify( +void X11ClipboardOzone::OnSelectionNotify( const x11::SelectionNotifyEvent& event) { // GetAvailableMimeTypes. auto selection = static_cast<x11::Atom>(event.selection); auto& selection_state = GetSelectionState(selection); if (static_cast<x11::Atom>(event.target) == atom_targets_) { std::vector<x11::Atom> targets; - ui::GetArrayProperty(x_window_, x_property_, &targets); + GetArrayProperty(x_window_, x_property_, &targets); selection_state.mime_types.clear(); for (auto target : targets) { @@ -217,16 +218,14 @@ bool X11ClipboardOzone::OnSelectionNotify( selection_state.data_mime_type = kMimeTypeText; ReadRemoteClipboard(selection); } - - return true; } // RequestClipboardData. if (static_cast<x11::Atom>(event.property) == x_property_) { x11::Atom type; std::vector<uint8_t> data; - ui::GetArrayProperty(x_window_, x_property_, &data, &type); - ui::DeleteProperty(x_window_, x_property_); + GetArrayProperty(x_window_, x_property_, &data, &type); + x11::DeleteProperty(x_window_, x_property_); if (type != x11::Atom::None) selection_state.data = scoped_refptr<base::RefCountedBytes>( base::RefCountedBytes::TakeVector(&data)); @@ -240,20 +239,16 @@ bool X11ClipboardOzone::OnSelectionNotify( std::move(selection_state.request_clipboard_data_callback) .Run(selection_state.data); } - return true; } else if (static_cast<x11::Atom>(event.property) == x11::Atom::None && selection_state.request_clipboard_data_callback) { // If the remote peer could not send data in the format we requested, // or failed for any reason, we will send empty data. std::move(selection_state.request_clipboard_data_callback) .Run(selection_state.data); - return true; } - - return false; } -bool X11ClipboardOzone::OnSetSelectionOwnerNotify( +void X11ClipboardOzone::OnSetSelectionOwnerNotify( const x11::XFixes::SelectionNotifyEvent& event) { // Reset state and fetch remote clipboard if there is a new remote owner. x11::Atom selection = event.selection; @@ -268,8 +263,6 @@ bool X11ClipboardOzone::OnSetSelectionOwnerNotify( // Increase the sequence number if the callback is set. if (update_sequence_cb_) update_sequence_cb_.Run(BufferForSelectionAtom(selection)); - - return true; } x11::Atom X11ClipboardOzone::SelectionAtomForBuffer( @@ -320,7 +313,7 @@ void X11ClipboardOzone::ReadRemoteClipboard(x11::Atom selection) { } } - connection_->ConvertSelection({x_window_, selection, gfx::GetAtom(target), + connection_->ConvertSelection({x_window_, selection, x11::GetAtom(target), x_property_, x11::Time::CurrentTime}); } @@ -361,6 +354,14 @@ void X11ClipboardOzone::RequestClipboardData( std::move(callback).Run(selection_state.data); return; } + + // If we know the available mime types, and it is not this, send empty now. + if (!selection_state.mime_types.empty() && + !Contains(selection_state.mime_types, mime_type)) { + std::move(callback).Run(PlatformClipboard::Data()); + return; + } + selection_state.data_mime_type = mime_type; selection_state.request_data_map = data_map; DCHECK(selection_state.request_clipboard_data_callback.is_null()); diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h index 9592b8df86c..4802f02ee08 100644 --- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h @@ -10,7 +10,7 @@ #include "base/callback.h" #include "base/containers/flat_map.h" -#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/connection.h" #include "ui/gfx/x/event.h" #include "ui/gfx/x/xfixes.h" #include "ui/gfx/x/xproto.h" @@ -26,7 +26,7 @@ namespace ui { // text/plain. Otherwise GetAvailableMimeTypes and RequestClipboardData call // the appropriate X11 functions and invoke callbacks when the associated events // are received. -class X11ClipboardOzone : public PlatformClipboard, public XEventDispatcher { +class X11ClipboardOzone : public PlatformClipboard, public x11::EventObserver { public: X11ClipboardOzone(); ~X11ClipboardOzone() override; @@ -52,12 +52,12 @@ class X11ClipboardOzone : public PlatformClipboard, public XEventDispatcher { private: struct SelectionState; - // XEventDispatcher: - bool DispatchXEvent(x11::Event* xev) override; + // x11::EventObserver: + void OnEvent(const x11::Event& xev) override; - bool OnSelectionRequest(const x11::SelectionRequestEvent& event); - bool OnSelectionNotify(const x11::SelectionNotifyEvent& event); - bool OnSetSelectionOwnerNotify( + void OnSelectionRequest(const x11::SelectionRequestEvent& event); + void OnSelectionNotify(const x11::SelectionNotifyEvent& event); + void OnSetSelectionOwnerNotify( const x11::XFixes::SelectionNotifyEvent& event); // Returns an X atom for a clipboard buffer type. diff --git a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc index 4c3b56653dd..8409a940dbc 100644 --- a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc @@ -20,12 +20,12 @@ X11OSExchangeDataProviderOzone::X11OSExchangeDataProviderOzone( X11OSExchangeDataProviderOzone::X11OSExchangeDataProviderOzone() { DCHECK(own_window()); - X11EventSource::GetInstance()->AddXEventDispatcher(this); + x11::Connection::Get()->AddEventObserver(this); } X11OSExchangeDataProviderOzone::~X11OSExchangeDataProviderOzone() { if (own_window()) - X11EventSource::GetInstance()->RemoveXEventDispatcher(this); + x11::Connection::Get()->RemoveEventObserver(this); } std::unique_ptr<OSExchangeDataProvider> X11OSExchangeDataProviderOzone::Clone() @@ -36,13 +36,10 @@ std::unique_ptr<OSExchangeDataProvider> X11OSExchangeDataProviderOzone::Clone() return std::move(ret); } -bool X11OSExchangeDataProviderOzone::DispatchXEvent(x11::Event* xev) { - auto* selection_request = xev->As<x11::SelectionRequestEvent>(); - if (selection_request && selection_request->owner == x_window()) { - selection_owner().OnSelectionRequest(*xev); - return true; - } - return false; +void X11OSExchangeDataProviderOzone::OnEvent(const x11::Event& xev) { + auto* selection_request = xev.As<x11::SelectionRequestEvent>(); + if (selection_request && selection_request->owner == x_window()) + selection_owner().OnSelectionRequest(*selection_request); } } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h index 65542ea14d3..cc544e464a4 100644 --- a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.h @@ -6,14 +6,14 @@ #define UI_OZONE_PLATFORM_X11_X11_OS_EXCHANGE_DATA_PROVIDER_OZONE_H_ #include "ui/base/x/x11_os_exchange_data_provider.h" -#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/connection.h" #include "ui/gfx/x/event.h" namespace ui { // OSExchangeDataProvider implementation for Ozone/X11. class X11OSExchangeDataProviderOzone : public XOSExchangeDataProvider, - public XEventDispatcher { + public x11::EventObserver { public: X11OSExchangeDataProviderOzone(x11::Window x_window, const SelectionFormatMap& selection); @@ -27,8 +27,8 @@ class X11OSExchangeDataProviderOzone : public XOSExchangeDataProvider, // OSExchangeDataProvider: std::unique_ptr<OSExchangeDataProvider> Clone() const override; - // XEventDispatcher: - bool DispatchXEvent(x11::Event* xev) override; + // x11::EventObserver: + void OnEvent(const x11::Event& xev) override; }; } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc b/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc index d9ea9078d14..718cb627f4f 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone.cc @@ -42,17 +42,13 @@ X11ScreenOzone::X11ScreenOzone() } X11ScreenOzone::~X11ScreenOzone() { - if (x11_display_manager_->IsXrandrAvailable() && - X11EventSource::HasInstance()) { - X11EventSource::GetInstance()->RemoveXEventDispatcher(this); - } + if (x11_display_manager_->IsXrandrAvailable()) + x11::Connection::Get()->RemoveEventObserver(this); } void X11ScreenOzone::Init() { - if (x11_display_manager_->IsXrandrAvailable() && - X11EventSource::HasInstance()) { - X11EventSource::GetInstance()->AddXEventDispatcher(this); - } + if (x11_display_manager_->IsXrandrAvailable()) + x11::Connection::Get()->AddEventObserver(this); x11_display_manager_->Init(); } @@ -154,8 +150,8 @@ base::Value X11ScreenOzone::GetGpuExtraInfoAsListValue( gpu_extra_info.rgba_visual); } -bool X11ScreenOzone::DispatchXEvent(x11::Event* xev) { - return x11_display_manager_->ProcessEvent(xev); +void X11ScreenOzone::OnEvent(const x11::Event& xev) { + x11_display_manager_->OnEvent(xev); } gfx::Point X11ScreenOzone::GetCursorLocation() const { diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone.h b/chromium/ui/ozone/platform/x11/x11_screen_ozone.h index 2d61903d35f..4f498468610 100644 --- a/chromium/ui/ozone/platform/x11/x11_screen_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone.h @@ -12,7 +12,6 @@ #include "base/macros.h" #include "base/observer_list.h" #include "ui/base/x/x11_display_manager.h" -#include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/x/event.h" #include "ui/ozone/public/platform_screen.h" @@ -23,7 +22,7 @@ class X11WindowManager; // A PlatformScreen implementation for X11. class X11ScreenOzone : public PlatformScreen, - public XEventDispatcher, + public x11::EventObserver, public XDisplayManager::Delegate { public: X11ScreenOzone(); @@ -56,8 +55,8 @@ class X11ScreenOzone : public PlatformScreen, base::Value GetGpuExtraInfoAsListValue( const gfx::GpuExtraInfo& gpu_extra_info) override; - // Overridden from ui::XEventDispatcher: - bool DispatchXEvent(x11::Event* event) override; + // Overridden from x11::EventObserver: + void OnEvent(const x11::Event& event) override; private: friend class X11ScreenOzoneTest; diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc index e416f3fa817..e081380d06f 100644 --- a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc +++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc @@ -8,6 +8,9 @@ #include "gpu/vulkan/buildflags.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/linux/gbm_buffer.h" +#include "ui/gfx/linux/gpu_memory_buffer_support_x11.h" +#include "ui/gfx/linux/native_pixmap_dmabuf.h" #include "ui/gfx/x/connection.h" #include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_egl_x11_gles2.h" @@ -102,9 +105,10 @@ GLOzone* X11SurfaceFactory::GetGLOzone(gl::GLImplementation implementation) { #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> -X11SurfaceFactory::CreateVulkanImplementation(bool allow_protected_memory, +X11SurfaceFactory::CreateVulkanImplementation(bool use_swiftshader, + bool allow_protected_memory, bool enforce_protected_memory) { - return std::make_unique<gpu::VulkanImplementationX11>(); + return std::make_unique<gpu::VulkanImplementationX11>(use_swiftshader); } #endif @@ -121,4 +125,37 @@ std::unique_ptr<SurfaceOzoneCanvas> X11SurfaceFactory::CreateCanvasForWidget( return std::make_unique<X11CanvasSurface>(widget); } +scoped_refptr<gfx::NativePixmap> X11SurfaceFactory::CreateNativePixmap( + gfx::AcceleratedWidget widget, + VkDevice vk_device, + gfx::Size size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + base::Optional<gfx::Size> framebuffer_size) { + scoped_refptr<gfx::NativePixmapDmaBuf> pixmap; + auto buffer = ui::GpuMemoryBufferSupportX11::GetInstance()->CreateBuffer( + format, size, usage); + if (buffer) { + gfx::NativePixmapHandle handle = buffer->ExportHandle(); + pixmap = base::MakeRefCounted<gfx::NativePixmapDmaBuf>(size, format, + std::move(handle)); + } + + // CreateNativePixmap is non-blocking operation. Thus, it is safe to call it + // and return the result with the provided callback. + return pixmap; +} + +void X11SurfaceFactory::CreateNativePixmapAsync(gfx::AcceleratedWidget widget, + VkDevice vk_device, + gfx::Size size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + NativePixmapCallback callback) { + // CreateNativePixmap is non-blocking operation. Thus, it is safe to call it + // and return the result with the provided callback. + std::move(callback).Run( + CreateNativePixmap(widget, vk_device, size, format, usage)); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.h b/chromium/ui/ozone/platform/x11/x11_surface_factory.h index fb5431bf47a..70209cc94be 100644 --- a/chromium/ui/ozone/platform/x11/x11_surface_factory.h +++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.h @@ -28,11 +28,25 @@ class X11SurfaceFactory : public SurfaceFactoryOzone { GLOzone* GetGLOzone(gl::GLImplementation implementation) override; #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory) override; #endif std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget( gfx::AcceleratedWidget widget) override; + scoped_refptr<gfx::NativePixmap> CreateNativePixmap( + gfx::AcceleratedWidget widget, + VkDevice vk_device, + gfx::Size size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + base::Optional<gfx::Size> framebuffer_size = base::nullopt) override; + void CreateNativePixmapAsync(gfx::AcceleratedWidget widget, + VkDevice vk_device, + gfx::Size size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + NativePixmapCallback callback) override; private: std::unique_ptr<GLOzone> glx_implementation_; diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc index 07c43384e1a..63037c7c771 100644 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc +++ b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc @@ -70,8 +70,7 @@ class X11WindowOzoneTest : public testing::Test { ~X11WindowOzoneTest() override = default; void SetUp() override { - auto* connection = x11::Connection::Get(); - event_source_ = std::make_unique<X11EventSource>(connection); + event_source_ = std::make_unique<X11EventSource>(x11::Connection::Get()); test_screen_ = new TestScreen(); display::Screen::SetScreenInstance(test_screen_); @@ -96,7 +95,7 @@ class X11WindowOzoneTest : public testing::Test { auto* device_event = event->As<x11::Input::DeviceEvent>(); DCHECK(device_event); device_event->event = static_cast<x11::Window>(widget); - event_source_->ProcessXEvent(event); + x11::Connection::Get()->DispatchEvent(*event); } X11WindowManager* window_manager() const { diff --git a/chromium/ui/ozone/public/input_controller.cc b/chromium/ui/ozone/public/input_controller.cc index 43e5fca8d2e..35fdf8c9a06 100644 --- a/chromium/ui/ozone/public/input_controller.cc +++ b/chromium/ui/ozone/public/input_controller.cc @@ -50,6 +50,9 @@ class StubInputController : public InputController { void SuspendMouseAcceleration() override {} void EndMouseAccelerationSuspension() override {} void SetMouseScrollAcceleration(bool enabled) override {} + void SetPointingStickSensitivity(int value) override {} + void SetPointingStickPrimaryButtonRight(bool right) override {} + void SetPointingStickAcceleration(bool enabled) override {} void SetTouchpadAcceleration(bool enabled) override {} void SetTouchpadScrollAcceleration(bool enabled) override {} void SetTapToClickPaused(bool state) override {} diff --git a/chromium/ui/ozone/public/input_controller.h b/chromium/ui/ozone/public/input_controller.h index 27b5ad3acd8..c18ba9c72ef 100644 --- a/chromium/ui/ozone/public/input_controller.h +++ b/chromium/ui/ozone/public/input_controller.h @@ -71,6 +71,9 @@ class COMPONENT_EXPORT(OZONE_BASE) InputController { // Mouse settings. virtual void SetMouseSensitivity(int value) = 0; virtual void SetMouseScrollSensitivity(int value) = 0; + + // Sets the primary button for the mouse. Passing true sets the right button + // as primary, while false (the default) sets the left as primary. virtual void SetPrimaryButtonRight(bool right) = 0; virtual void SetMouseReverseScroll(bool enabled) = 0; virtual void SetMouseAcceleration(bool enabled) = 0; @@ -78,6 +81,14 @@ class COMPONENT_EXPORT(OZONE_BASE) InputController { virtual void EndMouseAccelerationSuspension() = 0; virtual void SetMouseScrollAcceleration(bool enabled) = 0; + // Pointing stick settings. + virtual void SetPointingStickSensitivity(int value) = 0; + + // Sets the primary button for the pointing stick. Passing true sets the right + // button as primary, while false (the default) sets the left as primary. + virtual void SetPointingStickPrimaryButtonRight(bool right) = 0; + virtual void SetPointingStickAcceleration(bool enabled) = 0; + // Touch log collection. virtual void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) = 0; virtual void GetTouchEventLog(const base::FilePath& out_dir, diff --git a/chromium/ui/ozone/public/mojom/device_cursor.mojom b/chromium/ui/ozone/public/mojom/device_cursor.mojom index a86e09970f0..f165e5eb833 100644 --- a/chromium/ui/ozone/public/mojom/device_cursor.mojom +++ b/chromium/ui/ozone/public/mojom/device_cursor.mojom @@ -15,11 +15,10 @@ interface DeviceCursor { // Sets the cursor |bitmaps| on |window| at |point| with // |frame_delay_ms|. SetCursor(gfx.mojom.AcceleratedWidget window, - array<skia.mojom.Bitmap> bitmaps, + array<skia.mojom.BitmapN32> bitmaps, gfx.mojom.Point point, int32 frame_delay_ms); // Moves the cursor in |window| to |point|. MoveCursor(gfx.mojom.AcceleratedWidget window, gfx.mojom.Point point); }; - diff --git a/chromium/ui/ozone/public/mojom/drm_device.mojom b/chromium/ui/ozone/public/mojom/drm_device.mojom index caf4a7e5a7b..cf0e17009a1 100644 --- a/chromium/ui/ozone/public/mojom/drm_device.mojom +++ b/chromium/ui/ozone/public/mojom/drm_device.mojom @@ -51,11 +51,11 @@ interface DrmDevice { // Instructs the GPU to abandon a DRM device. RemoveGraphicsDevice(mojo_base.mojom.FilePath path); - // Configures (Enables/Disables) DRM displays, returns a map: each configured - // display ID to its status, true on success. + // Configures (Enables/Disables) DRM displays, returns whether or not the + // configuration was successful. ConfigureNativeDisplays( array<display.mojom.DisplayConfigurationParams> config_requests) => - (map<int64, bool> statuses); + (bool config_success); // Gets high-definition content protection (HDCP) (DRM as in // digital rights management) state. diff --git a/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom b/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom index 37fcaa56ace..1a99bc0bb98 100644 --- a/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom +++ b/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom @@ -33,7 +33,7 @@ interface WaylandBufferManagerHost { // buffer, which is used to identify imported wl_buffers on the browser // process side and map them with the buffer objects on the gpu process side. // The buffer will be associated with an AcceleratedWidget as soon as the - // very first CommitBuffer request comes from viz to browser process. + // very first CommitOverlays request comes from viz to browser process. // If the buffer has been committed at least once, it is not possible to // reassign it to another AcceleratedWidget. CreateDmabufBasedBuffer(handle<platform> dmabuf_fd, @@ -50,7 +50,7 @@ interface WaylandBufferManagerHost { // The |length| is the length of the shared memory, |size| // is the size of buffer and |buffer_id| is the id of the buffer. // The buffer will be associated with an AcceleratedWidget as soon as the - // very first CommitBuffer request comes from viz to browser process. If + // very first CommitOverlays request comes from viz to browser process. If // the buffer has been committed at least once, it is not possible to // reassign it to another AcceleratedWidget. CreateShmBasedBuffer(handle<platform> shm_fd, @@ -69,14 +69,6 @@ interface WaylandBufferManagerHost { // |buffer_id| will result in the termination of the GPU process. DestroyBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id); - // Attaches a wl_buffer to a WaylandWindow's surface with the following - // |widget|. The |damage_region| describes the changed region of the buffer. - // The |buffer_id| is a unique id for the buffer, which is used to - // identify imported wl_buffers on the browser process side mapped with - // the ones on the gpu process. - CommitBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id, - gfx.mojom.Rect damage_region); - // Send overlay configurations for a frame to a WaylandWindow with the // following |widget|. CommitOverlays(gfx.mojom.AcceleratedWidget widget, @@ -95,10 +87,14 @@ interface WaylandBufferManagerGpu { // instance to avoid using zwp_linux_dmabuf protocol by setting // |supports_dma_buf| to false, which results in using wl_egl_surface in a // single process mode, and software rendering in a multiple process mode. + // |supports_acquire_fence| indicates whether acquire fences can be submitted + // with buffers for wayland servers to wait on before accessing buffer + // contents. Initialize(pending_remote<WaylandBufferManagerHost> remote_host, map<gfx.mojom.BufferFormat, array<uint64>> buffer_formats_with_modifiers, - bool supports_dma_buf); + bool supports_dma_buf, + bool supports_acquire_fence); // Signals about swap completion. OnSubmission(gfx.mojom.AcceleratedWidget widget, diff --git a/chromium/ui/ozone/public/overlay_manager_ozone.h b/chromium/ui/ozone/public/overlay_manager_ozone.h index 54e73e58933..c72edc50752 100644 --- a/chromium/ui/ozone/public/overlay_manager_ozone.h +++ b/chromium/ui/ozone/public/overlay_manager_ozone.h @@ -22,6 +22,15 @@ class OverlayManagerOzone { // Get the hal struct to check for overlay support. virtual std::unique_ptr<OverlayCandidatesOzone> CreateOverlayCandidates( gfx::AcceleratedWidget w) = 0; + + bool allow_sync_and_real_buffer_page_flip_testing() const { + return allow_sync_and_real_buffer_page_flip_testing_; + } + + protected: + // TODO(fangzhoug): Some Chrome OS boards still use the legacy video decoder. + // Remove this once ChromeOSVideoDecoder is on everywhere. + bool allow_sync_and_real_buffer_page_flip_testing_ = false; }; } // namespace ui diff --git a/chromium/ui/ozone/public/overlay_surface_candidate.h b/chromium/ui/ozone/public/overlay_surface_candidate.h index ebd82e96706..46b7e85d7f6 100644 --- a/chromium/ui/ozone/public/overlay_surface_candidate.h +++ b/chromium/ui/ozone/public/overlay_surface_candidate.h @@ -67,6 +67,8 @@ class COMPONENT_EXPORT(OZONE_BASE) OverlaySurfaceCandidate { // To be modified by the implementer if this candidate can go into // an overlay. bool overlay_handled = false; + // If this candidate requires an overlay for proper display. + bool requires_overlay = false; }; using OverlaySurfaceCandidateList = std::vector<OverlaySurfaceCandidate>; diff --git a/chromium/ui/ozone/public/ozone_platform.cc b/chromium/ui/ozone/public/ozone_platform.cc index 898a9c5c1b2..29fd78fa4c7 100644 --- a/chromium/ui/ozone/public/ozone_platform.cc +++ b/chromium/ui/ozone/public/ozone_platform.cc @@ -87,7 +87,7 @@ OzonePlatform* OzonePlatform::GetInstance() { } // static -const char* OzonePlatform::GetPlatformName() { +std::string OzonePlatform::GetPlatformNameForTest() { return GetOzonePlatformName(); } diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h index 739ffae079d..274915a23e7 100644 --- a/chromium/ui/ozone/public/ozone_platform.h +++ b/chromium/ui/ozone/public/ozone_platform.h @@ -6,6 +6,7 @@ #define UI_OZONE_PUBLIC_OZONE_PLATFORM_H_ #include <memory> +#include <string> #include <vector> #include "base/callback.h" @@ -69,6 +70,18 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // operate as a single process for platforms (i.e. drm) that are usually // split between a host and viz specific portion. bool single_process = false; + + // Setting this to true indicates the the platform can do additional + // initialization for the GpuMemoryBuffer framework. + bool enable_native_gpu_memory_buffers = false; + + // The direct VideoDecoder is disallowed on some particular SoC/platforms. + // This flag is a reflection of whatever the ChromeOS command line builder + // says. If false, overlay manager will not use synchronous pageflip + // testing with real buffer. + // TODO(fangzhoug): Some Chrome OS boards still use the legacy video + // decoder. Remove this once ChromeOSVideoDecoder is on everywhere. + bool allow_sync_and_real_buffer_page_flip_testing = false; }; // Struct used to indicate platform properties. @@ -103,6 +116,13 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // Determines if the platform supports vulkan swap chain. bool supports_vulkan_swap_chain = false; + // Linux only: determines if the platform uses the external Vulkan image + // factory. + bool uses_external_vulkan_image_factory = false; + + // Linux only: determines if Skia can fall back to the X11 output device. + bool skia_can_fall_back_to_x11 = false; + // Wayland only: determines if the client must ignore the screen bounds when // calculating bounds of menu windows. bool ignore_screen_bounds_for_menus = false; @@ -121,6 +141,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // Determines if the application modal dialogs should use the event blocker // to allow the only browser window receiving UI events. bool app_modal_dialogs_use_event_blocker = false; + + // Determines whether buffer formats should be fetched on GPU and passed + // back via gpu extra info. + bool fetch_buffer_formats_for_gmb_on_gpu = false; }; // Properties available in the host process after initialization. @@ -168,12 +192,8 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { static OzonePlatform* GetInstance(); // Returns the current ozone platform name. - // TODO(crbug.com/1002674): This is temporary and meant to make it possible - // for higher level components to take run-time actions depending on the - // current ozone platform selected. Which implies in layering violations, - // which are tolerated during the X11 migration to Ozone and must be fixed - // once it is done. - static const char* GetPlatformName(); + // Some tests may skip based on the platform name. + static std::string GetPlatformNameForTest(); // Factory getters to override in subclasses. The returned objects will be // injected into the appropriate layer at startup. Subclasses should not diff --git a/chromium/ui/ozone/public/ozone_switches.cc b/chromium/ui/ozone/public/ozone_switches.cc index b9754475467..2d308f9ee30 100644 --- a/chromium/ui/ozone/public/ozone_switches.cc +++ b/chromium/ui/ozone/public/ozone_switches.cc @@ -15,6 +15,9 @@ const char kOzoneDumpFile[] = "ozone-dump-file"; // Try to enable wayland input method editor. const char kEnableWaylandIme[] = "enable-wayland-ime"; +// Disable wayland input method editor. +const char kDisableWaylandIme[] = "disable-wayland-ime"; + // Disable explicit DMA-fences const char kDisableExplicitDmaFences[] = "disable-explicit-dma-fences"; @@ -22,4 +25,7 @@ const char kDisableExplicitDmaFences[] = "disable-explicit-dma-fences"; const char kDisableRunningAsSystemCompositor[] = "disable-running-as-system-compositor"; +// Disable buffer bandwidth compression +const char kDisableBufferBWCompression[] = "disable-buffer-bw-compression"; + } // namespace switches diff --git a/chromium/ui/ozone/public/ozone_switches.h b/chromium/ui/ozone/public/ozone_switches.h index 12dd34e2b51..baabdfdd8fe 100644 --- a/chromium/ui/ozone/public/ozone_switches.h +++ b/chromium/ui/ozone/public/ozone_switches.h @@ -16,11 +16,15 @@ COMPONENT_EXPORT(OZONE_BASE) extern const char kOzoneDumpFile[]; COMPONENT_EXPORT(OZONE_BASE) extern const char kEnableWaylandIme[]; +COMPONENT_EXPORT(OZONE_BASE) extern const char kDisableWaylandIme[]; + COMPONENT_EXPORT(OZONE_BASE) extern const char kDisableExplicitDmaFences[]; COMPONENT_EXPORT(OZONE_BASE) extern const char kDisableRunningAsSystemCompositor[]; +COMPONENT_EXPORT(OZONE_BASE) extern const char kDisableBufferBWCompression[]; + } // namespace switches #endif // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_ diff --git a/chromium/ui/ozone/public/platform_gl_egl_utility.cc b/chromium/ui/ozone/public/platform_gl_egl_utility.cc new file mode 100644 index 00000000000..38a135e4a0b --- /dev/null +++ b/chromium/ui/ozone/public/platform_gl_egl_utility.cc @@ -0,0 +1,29 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/public/platform_gl_egl_utility.h" + +#include "base/logging.h" +#include "base/notreached.h" + +namespace ui { + +PlatformGLEGLUtility::PlatformGLEGLUtility() = default; + +PlatformGLEGLUtility::~PlatformGLEGLUtility() = default; + +bool PlatformGLEGLUtility::HasVisualManager() { + return false; +} + +bool PlatformGLEGLUtility::UpdateVisualsOnGpuInfoChanged( + bool software_rendering, + uint32_t default_visual_id, + uint32_t transparent_visual_id) { + NOTREACHED() << "This must not be called if the platform does not support " + "X11 visuals."; + return false; +} + +} // namespace ui diff --git a/chromium/ui/ozone/public/platform_gl_egl_utility.h b/chromium/ui/ozone/public/platform_gl_egl_utility.h index 96acbaf1ad9..568ea5644a7 100644 --- a/chromium/ui/ozone/public/platform_gl_egl_utility.h +++ b/chromium/ui/ozone/public/platform_gl_egl_utility.h @@ -18,7 +18,8 @@ namespace ui { // Provides platform specific EGL attributes/configs. class COMPONENT_EXPORT(OZONE_BASE) PlatformGLEGLUtility { public: - virtual ~PlatformGLEGLUtility() = default; + PlatformGLEGLUtility(); + virtual ~PlatformGLEGLUtility(); // Gets additional display attributes based on |platform_type|. virtual void GetAdditionalEGLAttributes( @@ -41,6 +42,17 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformGLEGLUtility { // X11 specific; returns whether the test configuration supports alpha for // window visuals. virtual bool X11DoesVisualHaveAlphaForTest() const = 0; + + // X11 specific; returns whether the platform supports visuals. + virtual bool HasVisualManager(); + + // X11 specific; sets new visuals. + // Must be called only if the X11 visual manager is available. + // Should be called when the updated GPU info is available. + // Returns whether the visuals provided were valid. + virtual bool UpdateVisualsOnGpuInfoChanged(bool software_rendering, + uint32_t default_visual_id, + uint32_t transparent_visual_id); }; } // namespace ui diff --git a/chromium/ui/ozone/public/surface_factory_ozone.cc b/chromium/ui/ozone/public/surface_factory_ozone.cc index e297254340f..c42cea806b1 100644 --- a/chromium/ui/ozone/public/surface_factory_ozone.cc +++ b/chromium/ui/ozone/public/surface_factory_ozone.cc @@ -35,7 +35,8 @@ GLOzone* SurfaceFactoryOzone::GetGLOzone(gl::GLImplementation implementation) { #if BUILDFLAG(ENABLE_VULKAN) std::unique_ptr<gpu::VulkanImplementation> -SurfaceFactoryOzone::CreateVulkanImplementation(bool allow_protected_memory, +SurfaceFactoryOzone::CreateVulkanImplementation(bool use_swiftshader, + bool allow_protected_memory, bool enforce_protected_memory) { return nullptr; } diff --git a/chromium/ui/ozone/public/surface_factory_ozone.h b/chromium/ui/ozone/public/surface_factory_ozone.h index 773537d40e4..c68f421852a 100644 --- a/chromium/ui/ozone/public/surface_factory_ozone.h +++ b/chromium/ui/ozone/public/surface_factory_ozone.h @@ -78,11 +78,14 @@ class COMPONENT_EXPORT(OZONE_BASE) SurfaceFactoryOzone { #if BUILDFLAG(ENABLE_VULKAN) // Creates the vulkan implementation. This object should be capable of // creating surfaces that swap to a platform window. + // |use_swiftshader| suggests using Swiftshader. The actual support depends + // on the platform. // |allow_protected_memory| suggests that the vulkan implementation should // create protected-capable resources, such as VkQueue. // |enforce_protected_memory| suggests that the vulkan implementation should // always use protected memory and resources, such as CommandBuffers. virtual std::unique_ptr<gpu::VulkanImplementation> CreateVulkanImplementation( + bool use_swiftshader, bool allow_protected_memory, bool enforce_protected_memory); |