diff --git a/ui/ozone/platform/wayland/fake_server.cc b/ui/ozone/platform/wayland/fake_server.cc index 62e7f8c16c118..405716c3cdba8 100644 --- a/ui/ozone/platform/wayland/fake_server.cc +++ b/ui/ozone/platform/wayland/fake_server.cc @@ -1196,6 +1196,16 @@ void FakeServer::Resume() { resume_event_.Signal(); } +MockOutput* FakeServer::CreateAndInitializeOutput() { + auto output = std::make_unique(); + output->Initialize(display()); + + MockOutput* output_ptr = output.get(); + globals_.push_back(std::move(output)); + + return output_ptr; +} + void FakeServer::DoPause() { base::RunLoop().RunUntilIdle(); pause_event_.Signal(); diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h index ddda5748a1966..4531958f6ff26 100644 --- a/ui/ozone/platform/wayland/fake_server.h +++ b/ui/ozone/platform/wayland/fake_server.h @@ -453,11 +453,7 @@ class FakeServer : public base::Thread, base::MessagePumpLibevent::FdWatcher { return resource ? T::FromResource(resource) : nullptr; } - void CreateAndInitializeOutput() { - auto output = std::make_unique(); - output->Initialize(display()); - globals_.push_back(std::move(output)); - } + MockOutput* CreateAndInitializeOutput(); MockDataDeviceManager* data_device_manager() { return &data_device_manager_; } MockSeat* seat() { return &seat_; } diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc index ddddd8615275c..e2c6c135ff5fb 100644 --- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -111,7 +111,8 @@ class OzonePlatformWayland : public OzonePlatform { // The WaylandConnection and the WaylandOutputManager must be created before // PlatformScreen. DCHECK(connection_ && connection_->wayland_output_manager()); - return connection_->wayland_output_manager()->CreateWaylandScreen(); + return connection_->wayland_output_manager()->CreateWaylandScreen( + connection_.get()); } void InitializeUI(const InitParams& args) override { diff --git a/ui/ozone/platform/wayland/wayland_output.cc b/ui/ozone/platform/wayland/wayland_output.cc index fd14f45d63324..0ab6ebaccfbd1 100644 --- a/ui/ozone/platform/wayland/wayland_output.cc +++ b/ui/ozone/platform/wayland/wayland_output.cc @@ -34,7 +34,6 @@ void WaylandOutput::Initialize(Delegate* delegate) { } void WaylandOutput::TriggerDelegateNotification() const { - DCHECK(!rect_in_physical_pixels_.IsEmpty()); delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_, device_scale_factor_); } @@ -51,8 +50,10 @@ void WaylandOutput::OutputHandleGeometry(void* data, const char* model, int32_t output_transform) { WaylandOutput* wayland_output = static_cast(data); - if (wayland_output) + if (wayland_output) { wayland_output->rect_in_physical_pixels_.set_origin(gfx::Point(x, y)); + wayland_output->TriggerDelegateNotification(); + } } // static diff --git a/ui/ozone/platform/wayland/wayland_output.h b/ui/ozone/platform/wayland/wayland_output.h index ddf7946b194f1..ed3968b1e0562 100644 --- a/ui/ozone/platform/wayland/wayland_output.h +++ b/ui/ozone/platform/wayland/wayland_output.h @@ -34,6 +34,9 @@ class WaylandOutput { void TriggerDelegateNotification() const; + // Says whether this WaylandOutput is a wrapper of the passed |output|. + bool has_output(wl_output* output) const { return output_.get() == output; } + uint32_t output_id() const { return output_id_; } // Tells if the output has already received physical screen dimensions in the diff --git a/ui/ozone/platform/wayland/wayland_output_manager.cc b/ui/ozone/platform/wayland/wayland_output_manager.cc index c9db25c35b40e..3a9c337be8837 100644 --- a/ui/ozone/platform/wayland/wayland_output_manager.cc +++ b/ui/ozone/platform/wayland/wayland_output_manager.cc @@ -65,8 +65,19 @@ void WaylandOutputManager::RemoveWaylandOutput(const uint32_t output_id) { OnWaylandOutputRemoved(output_id); } -std::unique_ptr WaylandOutputManager::CreateWaylandScreen() { - auto wayland_screen = std::make_unique(); +uint32_t WaylandOutputManager::GetIdForOutput(wl_output* output) const { + auto output_it = std::find_if(output_list_.begin(), output_list_.end(), + [output](const auto& wayland_output) { + return wayland_output->has_output(output); + }); + // This is unlikely to happen, but let's be explicit here. + DCHECK(output_it != output_list_.end()); + return output_it->get()->output_id(); +} + +std::unique_ptr WaylandOutputManager::CreateWaylandScreen( + WaylandConnection* connection) { + auto wayland_screen = std::make_unique(connection); wayland_screen_ = wayland_screen->GetWeakPtr(); // As long as |wl_output| sends geometry and other events asynchronously (that diff --git a/ui/ozone/platform/wayland/wayland_output_manager.h b/ui/ozone/platform/wayland/wayland_output_manager.h index 5fcdced8a0428..50215f612955b 100644 --- a/ui/ozone/platform/wayland/wayland_output_manager.h +++ b/ui/ozone/platform/wayland/wayland_output_manager.h @@ -19,6 +19,7 @@ struct wl_output; namespace ui { +class WaylandConnection; class WaylandOutput; class WaylandOutputManager : public WaylandOutput::Delegate { @@ -32,8 +33,11 @@ class WaylandOutputManager : public WaylandOutput::Delegate { void AddWaylandOutput(const uint32_t output_id, wl_output* output); void RemoveWaylandOutput(const uint32_t output_id); + uint32_t GetIdForOutput(wl_output* output) const; + // Creates a platform screen and feeds it with existing outputs. - std::unique_ptr CreateWaylandScreen(); + std::unique_ptr CreateWaylandScreen( + WaylandConnection* connection); private: void OnWaylandOutputAdded(uint32_t output_id); diff --git a/ui/ozone/platform/wayland/wayland_screen.cc b/ui/ozone/platform/wayland/wayland_screen.cc index 98ef35e3f6141..4763ed3568867 100644 --- a/ui/ozone/platform/wayland/wayland_screen.cc +++ b/ui/ozone/platform/wayland/wayland_screen.cc @@ -9,10 +9,13 @@ #include "ui/display/display_observer.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { -WaylandScreen::WaylandScreen() : weak_factory_(this) {} +WaylandScreen::WaylandScreen(WaylandConnection* connection) + : connection_(connection), weak_factory_(this) {} WaylandScreen::~WaylandScreen() = default; @@ -59,12 +62,23 @@ display::Display WaylandScreen::GetPrimaryDisplay() const { display::Display WaylandScreen::GetDisplayForAcceleratedWidget( gfx::AcceleratedWidget widget) const { - // TODO(msisov): implement wl_surface_listener::enter and - // wl_surface_listener::leave for a wl_surface to know what surface the window - // is located on. - // - // https://crbug.com/890271 - NOTIMPLEMENTED_LOG_ONCE(); + auto* wayland_window = connection_->GetWindow(widget); + if (!wayland_window) + return GetPrimaryDisplay(); + + const std::vector entered_outputs_ids = + wayland_window->entered_outputs_ids(); + if (entered_outputs_ids.empty()) + return GetPrimaryDisplay(); + + // A widget can be located on two displays. Return the one, which was the very + // first used. + for (const auto& display : display_list_.displays()) { + if (display.id() == entered_outputs_ids.front()) + return display; + } + + NOTREACHED(); return GetPrimaryDisplay(); } @@ -75,25 +89,61 @@ gfx::Point WaylandScreen::GetCursorScreenPoint() const { gfx::AcceleratedWidget WaylandScreen::GetAcceleratedWidgetAtScreenPoint( const gfx::Point& point) const { - // TODO(msisov): implement this once wl_surface_listener::enter and ::leave - // are used. - // - // https://crbug.com/890271 - NOTIMPLEMENTED_LOG_ONCE(); - return gfx::kNullAcceleratedWidget; + // This is a tricky one. To ensure right functionality, a widget under a + // cursor must be returned. But, Wayland clients cannot know where the windows + // are located in the global space coordinate system. Instead, it's possible + // to know widgets located on a surface local coordinate system (remember that + // clients cannot also know the position of the pointer in the global space + // coordinate system, but rather on a local surface coordinate system). That + // is, we will have to pretend that a single surface is a "display", where + // other widgets (child widgets are located in the surface local coordinate + // system, where the main surface has 0,0 origin) are shown. Whenever that + // surface is focused (the cursor is located under that widget), we will use + // it to determine if the point is on that main surface, a menu surface and + // etc. + + // This call comes only when a cursor is under a certain window (see how + // Wayland sends pointer events for better understanding + comment above). + auto* window = connection_->GetCurrentFocusedWindow(); + if (!window) + return gfx::kNullAcceleratedWidget; + + // If |point| is at origin and the focused window does not contain that point, + // it must be the root parent, which contains that |point|. + if (point.IsOrigin() && !window->GetBounds().Contains(point)) { + WaylandWindow* parent_window = nullptr; + do { + parent_window = window->parent_window(); + if (parent_window) + window = parent_window; + } while (parent_window); + DCHECK(!window->parent_window()); + } + + DCHECK(window->GetBounds().Contains(point)); + return window->GetWidget(); } display::Display WaylandScreen::GetDisplayNearestPoint( const gfx::Point& point) const { - NOTIMPLEMENTED_LOG_ONCE(); - return GetPrimaryDisplay(); + if (display_list_.displays().size() <= 1) + return GetPrimaryDisplay(); + for (const auto& display : display_list_.displays()) { + if (display.bounds().Contains(point)) + return display; + } + + return *FindDisplayNearestPoint(display_list_.displays(), point); } display::Display WaylandScreen::GetDisplayMatching( const gfx::Rect& match_rect) const { - // TODO(msisov): https://crbug.com/890272 - NOTIMPLEMENTED_LOG_ONCE(); - return GetPrimaryDisplay(); + if (match_rect.IsEmpty()) + return GetDisplayNearestPoint(match_rect.origin()); + + const display::Display* match = + FindDisplayWithBiggestIntersection(display_list_.displays(), match_rect); + return match ? *match : GetPrimaryDisplay(); } void WaylandScreen::AddObserver(display::DisplayObserver* observer) { diff --git a/ui/ozone/platform/wayland/wayland_screen.h b/ui/ozone/platform/wayland/wayland_screen.h index f2de8d4dbb959..ec32b5f6ca7b9 100644 --- a/ui/ozone/platform/wayland/wayland_screen.h +++ b/ui/ozone/platform/wayland/wayland_screen.h @@ -17,10 +17,12 @@ namespace ui { +class WaylandConnection; + // A PlatformScreen implementation for Wayland. class WaylandScreen : public PlatformScreen { public: - WaylandScreen(); + explicit WaylandScreen(WaylandConnection* connection); ~WaylandScreen() override; void OnOutputAdded(uint32_t output_id, bool is_primary); @@ -52,6 +54,9 @@ class WaylandScreen : public PlatformScreen { base::ObserverList observers_; + // Non-owned pointer. + WaylandConnection* connection_ = nullptr; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(WaylandScreen); diff --git a/ui/ozone/platform/wayland/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/wayland_screen_unittest.cc index 7eb9350f2987c..aa44a1bb3bdb0 100644 --- a/ui/ozone/platform/wayland/wayland_screen_unittest.cc +++ b/ui/ozone/platform/wayland/wayland_screen_unittest.cc @@ -5,12 +5,18 @@ #include #include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/display.h" #include "ui/display/display_observer.h" #include "ui/ozone/platform/wayland/fake_server.h" #include "ui/ozone/platform/wayland/wayland_connection.h" #include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_screen.h" #include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/test/mock_platform_window_delegate.h" +#include "ui/platform_window/platform_window_init_properties.h" + +using ::testing::SaveArg; +using ::testing::_; namespace ui { @@ -69,12 +75,74 @@ class WaylandScreenTest : public WaylandTest { output_manager_ = connection_->wayland_output_manager(); ASSERT_TRUE(output_manager_); + + EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); + platform_screen_ = output_manager_->CreateWaylandScreen(connection_.get()); } protected: + void ValidateTheDisplayForWidget(gfx::AcceleratedWidget widget, + int64_t expected_display_id, + const gfx::Point& expected_origin) { + display::Display display_for_widget = + platform_screen_->GetDisplayForAcceleratedWidget(widget); + EXPECT_EQ(display_for_widget.id(), expected_display_id); + EXPECT_EQ(display_for_widget.bounds().origin(), expected_origin); + } + + wl::MockOutput* CreateOutputWithRectAndSync(const gfx::Rect& rect) { + wl::MockOutput* output = server_.CreateAndInitializeOutput(); + if (!rect.IsEmpty()) + output->SetRect(rect); + + Sync(); + + // The geometry changes are automatically sent only after the client is + // bound to the output. Sync one more time to enqueue the geometry events. + Sync(); + + return output; + } + + void SendGeometryAndModeChangesAndSync(const gfx::Rect& new_rect, + wl_resource* resource) { + wl_output_send_geometry(output_->resource(), new_rect.x(), new_rect.y(), + 0 /* physical_width */, 0 /* physical_height */, + 0 /* subpixel */, "unkown_make", "unknown_model", + 0 /* transform */); + wl_output_send_mode(output_->resource(), WL_OUTPUT_MODE_CURRENT, + new_rect.width(), new_rect.height(), 0 /* refresh */); + + Sync(); + } + + std::unique_ptr CreateWaylandWindowAndSync( + const gfx::Rect& bounds, + PlatformWindowType window_type, + gfx::AcceleratedWidget parent_widget, + MockPlatformWindowDelegate* delegate) { + auto window = std::make_unique(delegate, connection_.get()); + gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_)) + .WillOnce(SaveArg<0>(&widget)); + + PlatformWindowInitProperties properties; + properties.bounds = bounds; + properties.type = window_type; + properties.parent_widget = parent_widget; + EXPECT_TRUE(window->Initialize(std::move(properties))); + EXPECT_NE(widget, gfx::kNullAcceleratedWidget); + + Sync(); + + return window; + } + wl::MockOutput* output_ = nullptr; WaylandOutputManager* output_manager_ = nullptr; + std::unique_ptr platform_screen_; + private: DISALLOW_COPY_AND_ASSIGN(WaylandScreenTest); }; @@ -82,36 +150,32 @@ class WaylandScreenTest : public WaylandTest { // Tests whether a primary output has been initialized before PlatformScreen is // created. TEST_P(WaylandScreenTest, OutputBaseTest) { - EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); - - std::unique_ptr platform_screen = - output_manager_->CreateWaylandScreen(); + // IsPrimaryOutputReady and PlatformScreen creation is done in the + // initialization part of the tests. // Ensure there is only one display, which is the primary one. - auto& all_displays = platform_screen->GetAllDisplays(); + auto& all_displays = platform_screen_->GetAllDisplays(); EXPECT_EQ(all_displays.size(), kNumberOfDisplays); // Ensure the size property of the primary display. - EXPECT_EQ(platform_screen->GetPrimaryDisplay().bounds(), + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds(), gfx::Rect(0, 0, kOutputWidth, kOutputHeight)); } TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { - EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); - std::unique_ptr platform_screen = - output_manager_->CreateWaylandScreen(); - TestDisplayObserver observer; - platform_screen->AddObserver(&observer); + platform_screen_->AddObserver(&observer); // Add a second display. - server_.CreateAndInitializeOutput(); + const gfx::Rect rect(gfx::Point(output_->GetRect().width(), 0), + output_->GetRect().size()); + CreateOutputWithRectAndSync(rect); Sync(); // Ensure that second display is not a primary one and have a different id. int64_t added_display_id = observer.GetDisplay().id(); - EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id); + EXPECT_NE(platform_screen_->GetPrimaryDisplay().id(), added_display_id); // Remove the second output. output_manager_->RemoveWaylandOutput(added_display_id); @@ -123,41 +187,30 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { EXPECT_EQ(added_display_id, removed_display_id); // Create another display again. - server_.CreateAndInitializeOutput(); - - Sync(); + CreateOutputWithRectAndSync(rect); // The newly added display is not a primary yet. added_display_id = observer.GetDisplay().id(); - EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id); + EXPECT_NE(platform_screen_->GetPrimaryDisplay().id(), added_display_id); // Make sure the geometry changes are sent by syncing one more time again. Sync(); - int64_t old_primary_display_id = platform_screen->GetPrimaryDisplay().id(); + int64_t old_primary_display_id = platform_screen_->GetPrimaryDisplay().id(); output_manager_->RemoveWaylandOutput(old_primary_display_id); // Ensure that previously added display is now a primary one. - EXPECT_EQ(platform_screen->GetPrimaryDisplay().id(), added_display_id); + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().id(), added_display_id); // Ensure that the removed display was the one, which was a primary display. EXPECT_EQ(observer.GetDisplay().id(), old_primary_display_id); } TEST_P(WaylandScreenTest, OutputPropertyChanges) { - std::unique_ptr platform_screen = - output_manager_->CreateWaylandScreen(); TestDisplayObserver observer; - platform_screen->AddObserver(&observer); + platform_screen_->AddObserver(&observer); const gfx::Rect new_rect(0, 0, 800, 600); - wl_output_send_geometry(output_->resource(), new_rect.x(), new_rect.y(), - 0 /* physical_width */, 0 /* physical_height */, - 0 /* subpixel */, "unkown_make", "unknown_model", - 0 /* transform */); - wl_output_send_mode(output_->resource(), WL_OUTPUT_MODE_CURRENT, - new_rect.width(), new_rect.height(), 0 /* refresh */); - - Sync(); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); uint32_t changed_values = 0; changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS; @@ -177,6 +230,203 @@ TEST_P(WaylandScreenTest, OutputPropertyChanges) { EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value); } +TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + // Add a test window and get an accelerated widget for it. + MockPlatformWindowDelegate delegate; + std::unique_ptr window = CreateWaylandWindowAndSync( + gfx::Rect(0, 0, 640, 480), PlatformWindowType::kWindow, + gfx::kNullAcceleratedWidget, &delegate); + + gfx::AcceleratedWidget widget = window->GetWidget(); + + // There must be a primary display used if the window has not entered a + // surface yet (very unlikely in the production). + int64_t expected_display_id = platform_screen_->GetPrimaryDisplay().id(); + gfx::Point expected_origin = + platform_screen_->GetPrimaryDisplay().bounds().origin(); + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); + + Sync(); + + // Get the surface of that window. + wl::MockSurface* surface = server_.GetObject(widget); + ASSERT_TRUE(surface); + + // Now, send enter event for the surface, which was created before. + wl_surface_send_enter(surface->resource(), output_->resource()); + + Sync(); + + // The id of the entered display must still correspond to the primary output. + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); + + // Create one more output. Make sure two output are placed next to each other. + const gfx::Rect new_rect(gfx::Point(output_->GetRect().width(), 0), + output_->GetRect().size()); + wl::MockOutput* output2 = CreateOutputWithRectAndSync(new_rect); + + Sync(); + + const display::Display display2 = observer.GetDisplay(); + + // Enter the second output now. + wl_surface_send_enter(surface->resource(), output2->resource()); + + Sync(); + + // The id of the entered display must still correspond to the primary output. + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); + + // Leave the first output. + wl_surface_send_leave(surface->resource(), output_->resource()); + + Sync(); + + // The id and the origin of the display for widget must corresponds to the + // second display now. + expected_display_id = display2.id(); + expected_origin = display2.bounds().origin(); + ValidateTheDisplayForWidget(widget, expected_display_id, expected_origin); +} + +TEST_P(WaylandScreenTest, GetAcceleratedWidgetAtScreenPoint) { + MockPlatformWindowDelegate delegate; + std::unique_ptr menu_window = CreateWaylandWindowAndSync( + gfx::Rect(window_->GetBounds().width() - 10, + window_->GetBounds().height() - 10, 100, 100), + PlatformWindowType::kPopup, window_->GetWidget(), &delegate); + + // If there is no focused window (focus is set whenever a pointer enters any + // of the windows), there must be kNullAcceleratedWidget returned. There is no + // real way to determine what window is located on a certain screen point in + // Wayland. + gfx::AcceleratedWidget widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point(10, 10)); + EXPECT_EQ(widget_at_screen_point, gfx::kNullAcceleratedWidget); + + // Set a focus to the main window. + window_->set_pointer_focus(true); + widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point(10, 10)); + EXPECT_EQ(widget_at_screen_point, window_->GetWidget()); + + // Imagine the mouse enters a menu window, which is located on top of the main + // window, and gathers focus. + window_->set_pointer_focus(false); + menu_window->set_pointer_focus(true); + widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point( + menu_window->GetBounds().x() + 1, menu_window->GetBounds().y() + 1)); + EXPECT_EQ(widget_at_screen_point, menu_window->GetWidget()); + + // Despite the menu window being focused, the accelerated widget at origin + // must be the parent widget. + widget_at_screen_point = + platform_screen_->GetAcceleratedWidgetAtScreenPoint(gfx::Point(0, 0)); + EXPECT_EQ(widget_at_screen_point, window_->GetWidget()); + + menu_window->set_pointer_focus(false); +} + +TEST_P(WaylandScreenTest, DefaultDisplayForNonExistingWidget) { + display::Display default_display = + platform_screen_->GetDisplayForAcceleratedWidget( + gfx::kNullAcceleratedWidget); + EXPECT_EQ(default_display.id(), platform_screen_->GetPrimaryDisplay().id()); +} + +TEST_P(WaylandScreenTest, GetDisplayNearestPoint) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + const int64_t first_display_id = platform_screen_->GetPrimaryDisplay().id(); + + // Prepare the first output. + const gfx::Rect new_rect(gfx::Rect(0, 0, 640, 480)); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); + + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds().ToString(), + new_rect.ToString()); + + const gfx::Rect output2_new_rect(640, 0, 1024, 768); + CreateOutputWithRectAndSync(output2_new_rect); + + const display::Display second_display = observer.GetDisplay(); + const int64_t second_display_id = second_display.id(); + EXPECT_EQ(second_display.bounds().ToString(), output2_new_rect.ToString()); + + EXPECT_EQ(first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(630, 10)).id()); + EXPECT_EQ(second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(650, 10)).id()); + EXPECT_EQ(first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(10, 10)).id()); + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(10000, 10000)).id()); + EXPECT_EQ( + first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(639, -10)).id()); + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(641, -20)).id()); + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(600, 760)).id()); + EXPECT_EQ( + first_display_id, + platform_screen_->GetDisplayNearestPoint(gfx::Point(-1000, 760)).id()); +} + +TEST_P(WaylandScreenTest, GetDisplayMatchingBasic) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + // Prepare the first output. + const gfx::Rect new_rect(gfx::Rect(0, 0, 640, 480)); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); + + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds().ToString(), + new_rect.ToString()); + + const gfx::Rect output2_new_rect(640, 0, 1024, 768); + CreateOutputWithRectAndSync(output2_new_rect); + + const display::Display second_display = observer.GetDisplay(); + const int64_t second_display_id = second_display.id(); + EXPECT_EQ(second_display.bounds().ToString(), output2_new_rect.ToString()); + + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayMatching(gfx::Rect(700, 20, 100, 100)).id()); +} + +TEST_P(WaylandScreenTest, GetDisplayMatchingOverlap) { + TestDisplayObserver observer; + platform_screen_->AddObserver(&observer); + + // Prepare the first output. + const gfx::Rect new_rect(gfx::Rect(0, 0, 640, 480)); + SendGeometryAndModeChangesAndSync(new_rect, output_->resource()); + + EXPECT_EQ(platform_screen_->GetPrimaryDisplay().bounds().ToString(), + new_rect.ToString()); + + const gfx::Rect output2_new_rect(640, 0, 1024, 768); + CreateOutputWithRectAndSync(output2_new_rect); + + const display::Display second_display = observer.GetDisplay(); + const int64_t second_display_id = second_display.id(); + EXPECT_EQ(second_display.bounds().ToString(), output2_new_rect.ToString()); + + EXPECT_EQ( + second_display_id, + platform_screen_->GetDisplayMatching(gfx::Rect(630, 20, 100, 100)).id()); +} + INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, WaylandScreenTest, ::testing::Values(kXdgShellV5)); diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc index c6b89e43d0d5b..3127fe2293e80 100644 --- a/ui/ozone/platform/wayland/wayland_window.cc +++ b/ui/ozone/platform/wayland/wayland_window.cc @@ -15,6 +15,7 @@ #include "ui/events/ozone/events_ozone.h" #include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_pointer.h" #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v5.h" #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v6.h" @@ -87,7 +88,7 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, WaylandWindow::~WaylandWindow() { PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - connection_->RemoveWindow(surface_.id()); + connection_->RemoveWindow(GetWidget()); if (parent_window_) parent_window_->set_child_window(nullptr); @@ -115,6 +116,8 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { } wl_surface_set_user_data(surface_.get(), this); + AddSurfaceListener(); + ui::PlatformWindowType ui_window_type = properties.type; switch (ui_window_type) { case ui::PlatformWindowType::kMenu: @@ -135,13 +138,19 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { connection_->ScheduleFlush(); - connection_->AddWindow(surface_.id(), this); + connection_->AddWindow(GetWidget(), this); PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - delegate_->OnAcceleratedWidgetAvailable(surface_.id()); + delegate_->OnAcceleratedWidgetAvailable(GetWidget()); return true; } +gfx::AcceleratedWidget WaylandWindow::GetWidget() { + if (!surface_) + return gfx::kNullAcceleratedWidget; + return surface_.id(); +} + void WaylandWindow::CreateXdgPopup() { if (bounds_.IsEmpty()) return; @@ -645,4 +654,49 @@ WmDragHandler* WaylandWindow::AsWmDragHandler() { return static_cast(this); } +void WaylandWindow::AddSurfaceListener() { + static struct wl_surface_listener surface_listener = { + &WaylandWindow::Enter, &WaylandWindow::Leave, + }; + wl_surface_add_listener(surface_.get(), &surface_listener, this); +} + +void WaylandWindow::SetEnteredOutputId(struct wl_output* output) { + const uint32_t entered_output_id = + connection_->wayland_output_manager()->GetIdForOutput(output); + DCHECK_NE(entered_output_id, 0u); + entered_outputs_ids_.push_back(entered_output_id); +} + +void WaylandWindow::RemoveEnteredOutputId(struct wl_output* output) { + const uint32_t left_output_id = + connection_->wayland_output_manager()->GetIdForOutput(output); + auto entered_output_id_it = std::find( + entered_outputs_ids_.begin(), entered_outputs_ids_.end(), left_output_id); + DCHECK(entered_output_id_it != entered_outputs_ids_.end()); + entered_outputs_ids_.erase(entered_output_id_it); +} + +// static +void WaylandWindow::Enter(void* data, + struct wl_surface* wl_surface, + struct wl_output* output) { + WaylandWindow* window = static_cast(data); + if (window) { + DCHECK(window->surface() == wl_surface); + window->SetEnteredOutputId(output); + } +} + +// static +void WaylandWindow::Leave(void* data, + struct wl_surface* wl_surface, + struct wl_output* output) { + WaylandWindow* window = static_cast(data); + if (window) { + DCHECK(window->surface() == wl_surface); + window->RemoveEnteredOutputId(output); + } +} + } // namespace ui diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h index bd0f4d2635477..537ffa8ad4b17 100644 --- a/ui/ozone/platform/wayland/wayland_window.h +++ b/ui/ozone/platform/wayland/wayland_window.h @@ -51,6 +51,8 @@ class WaylandWindow : public PlatformWindow, XDGSurfaceWrapper* xdg_surface() const { return xdg_surface_.get(); } XDGPopupWrapper* xdg_popup() const { return xdg_popup_.get(); } + gfx::AcceleratedWidget GetWidget(); + // Apply the bounds specified in the most recent configure event. This should // be called after processing all pending events in the wayland connection. void ApplyPendingBounds(); @@ -71,6 +73,8 @@ class WaylandWindow : public PlatformWindow, // xdg_popups as long as they must be destroyed in the back order. void set_child_window(WaylandWindow* window) { child_window_ = window; } + WaylandWindow* parent_window() const { return parent_window_; } + // Set whether this window has an implicit grab (often referred to as capture // in Chrome code). Implicit grabs happen while a pointer is down. void set_has_implicit_grab(bool value) { has_implicit_grab_ = value; } @@ -82,6 +86,10 @@ class WaylandWindow : public PlatformWindow, void DispatchHostWindowDragMovement( int hittest, const gfx::Point& pointer_location) override; + + const std::vector& entered_outputs_ids() const { + return entered_outputs_ids_; + } // WmDragHandler void StartDrag(const ui::OSExchangeData& data, @@ -153,6 +161,20 @@ class WaylandWindow : public PlatformWindow, WmMoveResizeHandler* AsWmMoveResizeHandler(); WmDragHandler* AsWmDragHandler(); + + // Starts getting update when the current surface enters or leaves wl_outputs. + void AddSurfaceListener(); + + void SetEnteredOutputId(struct wl_output* output); + void RemoveEnteredOutputId(struct wl_output* output); + + // wl_surface_listener + static void Enter(void* data, + struct wl_surface* wl_surface, + struct wl_output* output); + static void Leave(void* data, + struct wl_surface* wl_surface, + struct wl_output* output); PlatformWindowDelegate* delegate_; WaylandConnection* connection_; @@ -193,6 +215,9 @@ class WaylandWindow : public PlatformWindow, bool is_tooltip_ = false; + // The ids of the outputs this surface is shown on. + std::vector entered_outputs_ids_; + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); };