Commit 81881913 authored by Alexander Dunaev's avatar Alexander Dunaev Committed by Commit Bot

[ozone/wayland] Fixed buffer handling in the WaylandCursor class.

The visual shape of a pointer cursor is rendered at a surface attached
to the wl_pointer object and backed by a wl_buffer.  The WaylandCursor
class tried to reuse the buffer regardless of whether the compositor
released it, which resulted in that the cursor shape didn't change
when a new bitmap had been submitted.

This CL simplifies the process: a new buffer is created and attached
each time the new bitmap is submitted, and the old ones are held until
the compositor releases them.

R=msisov@igalia.com

Bug: 928260
Change-Id: I54cf679568276eddbd5c8042d05fe05de25f61d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1503374
Commit-Queue: Alexander Dunaev <adunaev@igalia.com>
Reviewed-by: default avatarMaksim Sisov <msisov@igalia.com>
Cr-Commit-Position: refs/heads/master@{#638521}
parent 9797662f
...@@ -158,6 +158,8 @@ source_set("test_support") { ...@@ -158,6 +158,8 @@ source_set("test_support") {
"test/global_object.h", "test/global_object.h",
"test/mock_buffer.cc", "test/mock_buffer.cc",
"test/mock_buffer.h", "test/mock_buffer.h",
"test/mock_pointer.cc",
"test/mock_pointer.h",
"test/mock_surface.cc", "test/mock_surface.cc",
"test/mock_surface.h", "test/mock_surface.h",
"test/mock_xdg_popup.cc", "test/mock_xdg_popup.cc",
...@@ -188,8 +190,6 @@ source_set("test_support") { ...@@ -188,8 +190,6 @@ source_set("test_support") {
"test/test_keyboard.h", "test/test_keyboard.h",
"test/test_output.cc", "test/test_output.cc",
"test/test_output.h", "test/test_output.h",
"test/test_pointer.cc",
"test/test_pointer.h",
"test/test_positioner.cc", "test/test_positioner.cc",
"test/test_positioner.h", "test/test_positioner.h",
"test/test_region.cc", "test/test_region.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
namespace wl {
namespace {
void SetCursor(wl_client* client,
wl_resource* pointer_resource,
uint32_t serial,
wl_resource* surface_resource,
int32_t hotspot_x,
int32_t hotspot_y) {
GetUserDataAs<MockPointer>(pointer_resource)
->SetCursor(surface_resource, hotspot_x, hotspot_y);
}
} // namespace
const struct wl_pointer_interface kMockPointerImpl = {
&SetCursor, // set_cursor
&DestroyResource, // release
};
MockPointer::MockPointer(wl_resource* resource) : ServerObject(resource) {}
MockPointer::~MockPointer() = default;
} // namespace wl
...@@ -2,27 +2,33 @@ ...@@ -2,27 +2,33 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POINTER_H_ #ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_POINTER_H_
#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_POINTER_H_ #define UI_OZONE_PLATFORM_WAYLAND_TEST_MOCK_POINTER_H_
#include <wayland-server-protocol.h> #include <wayland-server-protocol.h>
#include "base/macros.h" #include "base/macros.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/ozone/platform/wayland/test/server_object.h" #include "ui/ozone/platform/wayland/test/server_object.h"
struct wl_resource; struct wl_resource;
namespace wl { namespace wl {
extern const struct wl_pointer_interface kTestPointerImpl; extern const struct wl_pointer_interface kMockPointerImpl;
class TestPointer : public ServerObject { class MockPointer : public ServerObject {
public: public:
explicit TestPointer(wl_resource* resource); explicit MockPointer(wl_resource* resource);
~TestPointer() override; ~MockPointer() override;
MOCK_METHOD3(SetCursor,
void(wl_resource* surface_resource,
int32_t hotspot_x,
int32_t hotspot_y));
private: private:
DISALLOW_COPY_AND_ASSIGN(TestPointer); DISALLOW_COPY_AND_ASSIGN(MockPointer);
}; };
} // namespace wl } // namespace wl
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/wayland/test/test_pointer.h"
namespace wl {
const struct wl_pointer_interface kTestPointerImpl = {
nullptr, // set_cursor
&DestroyResource, // release
};
TestPointer::TestPointer(wl_resource* resource) : ServerObject(resource) {}
TestPointer::~TestPointer() = default;
} // namespace wl
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
#include "ui/ozone/platform/wayland/test/test_seat.h" #include "ui/ozone/platform/wayland/test/test_seat.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/test_keyboard.h" #include "ui/ozone/platform/wayland/test/test_keyboard.h"
#include "ui/ozone/platform/wayland/test/test_pointer.h"
#include "ui/ozone/platform/wayland/test/test_touch.h" #include "ui/ozone/platform/wayland/test/test_touch.h"
namespace wl { namespace wl {
...@@ -15,11 +15,11 @@ namespace { ...@@ -15,11 +15,11 @@ namespace {
constexpr uint32_t kSeatVersion = 4; constexpr uint32_t kSeatVersion = 4;
void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) { void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) {
wl_resource* pointer_resource = CreateResourceWithImpl<TestPointer>( wl_resource* pointer_resource = CreateResourceWithImpl<MockPointer>(
client, &wl_pointer_interface, wl_resource_get_version(resource), client, &wl_pointer_interface, wl_resource_get_version(resource),
&kTestPointerImpl, id); &kMockPointerImpl, id);
GetUserDataAs<TestSeat>(resource)->set_pointer( GetUserDataAs<TestSeat>(resource)->set_pointer(
GetUserDataAs<TestPointer>(pointer_resource)); GetUserDataAs<MockPointer>(pointer_resource));
} }
void GetKeyboard(wl_client* client, wl_resource* resource, uint32_t id) { void GetKeyboard(wl_client* client, wl_resource* resource, uint32_t id) {
......
...@@ -14,8 +14,8 @@ namespace wl { ...@@ -14,8 +14,8 @@ namespace wl {
extern const struct wl_seat_interface kTestSeatImpl; extern const struct wl_seat_interface kTestSeatImpl;
class MockPointer;
class TestKeyboard; class TestKeyboard;
class TestPointer;
class TestTouch; class TestTouch;
// Manages a global wl_seat object. // Manages a global wl_seat object.
...@@ -29,8 +29,8 @@ class TestSeat : public GlobalObject { ...@@ -29,8 +29,8 @@ class TestSeat : public GlobalObject {
TestSeat(); TestSeat();
~TestSeat() override; ~TestSeat() override;
void set_pointer(TestPointer* pointer) { pointer_ = pointer; } void set_pointer(MockPointer* pointer) { pointer_ = pointer; }
TestPointer* pointer() const { return pointer_; } MockPointer* pointer() const { return pointer_; }
void set_keyboard(TestKeyboard* keyboard) { keyboard_ = keyboard; } void set_keyboard(TestKeyboard* keyboard) { keyboard_ = keyboard; }
TestKeyboard* keyboard() const { return keyboard_; } TestKeyboard* keyboard() const { return keyboard_; }
...@@ -39,7 +39,7 @@ class TestSeat : public GlobalObject { ...@@ -39,7 +39,7 @@ class TestSeat : public GlobalObject {
TestTouch* touch() const { return touch_; } TestTouch* touch() const { return touch_; }
private: private:
TestPointer* pointer_; MockPointer* pointer_;
TestKeyboard* keyboard_; TestKeyboard* keyboard_;
TestTouch* touch_; TestTouch* touch_;
......
...@@ -16,80 +16,71 @@ ...@@ -16,80 +16,71 @@
namespace ui { namespace ui {
WaylandCursor::WaylandCursor() : shared_memory_(new base::SharedMemory()) {} WaylandCursor::WaylandCursor() = default;
WaylandCursor::~WaylandCursor() = default;
// static
void WaylandCursor::OnBufferRelease(void* data, wl_buffer* buffer) {
auto* cursor = static_cast<WaylandCursor*>(data);
DCHECK(cursor->buffers_.count(buffer) > 0);
cursor->buffers_.erase(buffer);
}
void WaylandCursor::Init(wl_pointer* pointer, WaylandConnection* connection) { void WaylandCursor::Init(wl_pointer* pointer, WaylandConnection* connection) {
if (input_pointer_ == pointer) DCHECK(connection);
return;
input_pointer_ = pointer; input_pointer_ = pointer;
DCHECK(connection);
shm_ = connection->shm(); shm_ = connection->shm();
pointer_surface_.reset( pointer_surface_.reset(
wl_compositor_create_surface(connection->compositor())); wl_compositor_create_surface(connection->compositor()));
} }
WaylandCursor::~WaylandCursor() {
pointer_surface_.reset();
buffer_.reset();
if (shared_memory_->handle().GetHandle()) {
shared_memory_->Unmap();
shared_memory_->Close();
}
}
void WaylandCursor::UpdateBitmap(const std::vector<SkBitmap>& cursor_image, void WaylandCursor::UpdateBitmap(const std::vector<SkBitmap>& cursor_image,
const gfx::Point& location, const gfx::Point& hotspot,
uint32_t serial) { uint32_t serial) {
if (!input_pointer_) if (!input_pointer_)
return; return;
if (!cursor_image.size()) { if (!cursor_image.size())
HideCursor(serial); return HideCursor(serial);
return;
}
const SkBitmap& image = cursor_image[0]; const SkBitmap& image = cursor_image[0];
SkISize size = image.dimensions(); SkISize size = image.dimensions();
if (size.isEmpty()) { if (size.isEmpty())
HideCursor(serial); return HideCursor(serial);
return;
}
gfx::Size image_size = gfx::SkISizeToSize(size); gfx::Size image_size = gfx::SkISizeToSize(size);
if (image_size != size_) { auto shared_memory = std::make_unique<base::SharedMemory>();
wl_buffer* buffer = wl::Object<wl_buffer> buffer(
wl::CreateSHMBuffer(image_size, shared_memory_.get(), shm_); wl::CreateSHMBuffer(image_size, shared_memory.get(), shm_));
if (!buffer) { if (!buffer) {
LOG(ERROR) << "Failed to create SHM buffer for Cursor Bitmap."; LOG(ERROR) << "Failed to create SHM buffer for Cursor Bitmap.";
wl_pointer_set_cursor(input_pointer_, serial, nullptr, 0, 0); return HideCursor(serial);
return;
}
buffer_.reset(buffer);
size_ = image_size;
} }
wl::DrawBitmapToSHMB(size_, *shared_memory_, image);
static const struct wl_buffer_listener wl_buffer_listener {
&WaylandCursor::OnBufferRelease
};
wl_buffer_add_listener(buffer.get(), &wl_buffer_listener, this);
wl::DrawBitmapToSHMB(image_size, *shared_memory, image);
wl_pointer_set_cursor(input_pointer_, serial, pointer_surface_.get(), wl_pointer_set_cursor(input_pointer_, serial, pointer_surface_.get(),
location.x(), location.y()); hotspot.x(), hotspot.y());
wl_surface_attach(pointer_surface_.get(), buffer_.get(), 0, 0); wl_surface_attach(pointer_surface_.get(), buffer.get(), 0, 0);
wl_surface_damage(pointer_surface_.get(), 0, 0, size_.width(),
size_.height());
wl_surface_commit(pointer_surface_.get()); wl_surface_commit(pointer_surface_.get());
buffers_.emplace(
buffer.get(),
std::pair<wl::Object<wl_buffer>, std::unique_ptr<base::SharedMemory>>(
std::move(buffer), std::move(shared_memory)));
} }
void WaylandCursor::HideCursor(uint32_t serial) { void WaylandCursor::HideCursor(uint32_t serial) {
size_ = gfx::Size(); DCHECK(input_pointer_);
wl_pointer_set_cursor(input_pointer_, serial, nullptr, 0, 0); wl_pointer_set_cursor(input_pointer_, serial, nullptr, 0, 0);
buffer_.reset();
if (shared_memory_->handle().GetHandle()) {
shared_memory_->Unmap();
shared_memory_->Close();
}
} }
} // namespace ui } // namespace ui
...@@ -6,12 +6,14 @@ ...@@ -6,12 +6,14 @@
#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CURSOR_H_ #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_CURSOR_H_
#include <wayland-client.h> #include <wayland-client.h>
#include <memory>
#include <utility>
#include <vector> #include <vector>
#include "base/containers/flat_map.h"
#include "base/macros.h" #include "base/macros.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurface.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/wayland/wayland_object.h" #include "ui/ozone/platform/wayland/wayland_object.h"
namespace base { namespace base {
...@@ -26,10 +28,14 @@ namespace ui { ...@@ -26,10 +28,14 @@ namespace ui {
class WaylandConnection; class WaylandConnection;
// The WaylandCursor class wraps the actual visual representation // Manages the actual visual representation (what users see drawn) of the
// (what users see drawn) of a wl_pointer. // 'pointer' (which is the Wayland term for mouse/mice).
// //
// 'pointer' is the Wayland terminology 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 { class WaylandCursor {
public: public:
WaylandCursor(); WaylandCursor();
...@@ -38,25 +44,29 @@ class WaylandCursor { ...@@ -38,25 +44,29 @@ class WaylandCursor {
void Init(wl_pointer* pointer, WaylandConnection* connection); void Init(wl_pointer* pointer, WaylandConnection* connection);
// Updates wl_pointer's visual representation with the given bitmap // Updates wl_pointer's visual representation with the given bitmap
// image set, at the hotspot specified by 'location'. // image set and hotspot.
void UpdateBitmap(const std::vector<SkBitmap>& bitmaps, void UpdateBitmap(const std::vector<SkBitmap>& bitmaps,
const gfx::Point& location, const gfx::Point& hotspot,
uint32_t serial); uint32_t serial);
private: private:
// wl_buffer_listener:
static void OnBufferRelease(void* data, wl_buffer* wl_buffer);
void HideCursor(uint32_t serial); void HideCursor(uint32_t serial);
wl_shm* shm_ = nullptr; // Owned by WaylandConnection. wl_shm* shm_ = nullptr; // Owned by WaylandConnection.
wl_pointer* input_pointer_ = nullptr; // Owned by WaylandPointer. wl_pointer* input_pointer_ = nullptr; // Owned by WaylandPointer.
wl::Object<wl_buffer> buffer_; // Holds the buffers and their memory until the compositor releases them.
base::flat_map<
wl_buffer*,
std::pair<wl::Object<wl_buffer>, std::unique_ptr<base::SharedMemory>>>
buffers_;
wl::Object<wl_surface> pointer_surface_; wl::Object<wl_surface> pointer_surface_;
std::unique_ptr<base::SharedMemory> shared_memory_;
sk_sp<SkSurface> sk_surface_; sk_sp<SkSurface> sk_surface_;
gfx::Size size_;
DISALLOW_COPY_AND_ASSIGN(WaylandCursor); DISALLOW_COPY_AND_ASSIGN(WaylandCursor);
}; };
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <memory>
#include "ui/events/base_event_utils.h" #include "ui/events/base_event_utils.h"
#include "ui/events/event.h" #include "ui/events/event.h"
...@@ -41,7 +42,7 @@ WaylandPointer::WaylandPointer(wl_pointer* pointer, ...@@ -41,7 +42,7 @@ WaylandPointer::WaylandPointer(wl_pointer* pointer,
wl_pointer_add_listener(obj_.get(), &listener, this); wl_pointer_add_listener(obj_.get(), &listener, this);
cursor_.reset(new WaylandCursor); cursor_ = std::make_unique<WaylandCursor>();
} }
WaylandPointer::~WaylandPointer() { WaylandPointer::~WaylandPointer() {
......
...@@ -16,6 +16,10 @@ namespace ui { ...@@ -16,6 +16,10 @@ namespace ui {
class WaylandWindow; class WaylandWindow;
// Wraps the wl_pointer object and transmits events to the dispatcher callback.
//
// Exposes an aggregated WaylandCursor that manages the visual shape of the
// pointer.
class WaylandPointer { class WaylandPointer {
public: public:
WaylandPointer(wl_pointer* pointer, const EventDispatchCallback& callback); WaylandPointer(wl_pointer* pointer, const EventDispatchCallback& callback);
......
...@@ -8,16 +8,19 @@ ...@@ -8,16 +8,19 @@
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_pointer.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/wayland_cursor.h"
#include "ui/ozone/platform/wayland/wayland_test.h" #include "ui/ozone/platform/wayland/wayland_test.h"
#include "ui/ozone/platform/wayland/wayland_window.h" #include "ui/ozone/platform/wayland/wayland_window.h"
#include "ui/ozone/test/mock_platform_window_delegate.h" #include "ui/ozone/test/mock_platform_window_delegate.h"
#include "ui/platform_window/platform_window_init_properties.h" #include "ui/platform_window/platform_window_init_properties.h"
using ::testing::SaveArg;
using ::testing::_; using ::testing::_;
using ::testing::Mock;
using ::testing::Ne;
using ::testing::SaveArg;
namespace ui { namespace ui {
...@@ -38,7 +41,7 @@ class WaylandPointerTest : public WaylandTest { ...@@ -38,7 +41,7 @@ class WaylandPointerTest : public WaylandTest {
} }
protected: protected:
wl::TestPointer* pointer_; wl::MockPointer* pointer_;
private: private:
DISALLOW_COPY_AND_ASSIGN(WaylandPointerTest); DISALLOW_COPY_AND_ASSIGN(WaylandPointerTest);
...@@ -312,6 +315,24 @@ TEST_P(WaylandPointerTest, AxisHorizontal) { ...@@ -312,6 +315,24 @@ TEST_P(WaylandPointerTest, AxisHorizontal) {
EXPECT_EQ(gfx::PointF(50, 75), mouse_wheel_event->root_location_f()); EXPECT_EQ(gfx::PointF(50, 75), mouse_wheel_event->root_location_f());
} }
TEST_P(WaylandPointerTest, SetBitmap) {
SkBitmap dummy_cursor;
dummy_cursor.setInfo(
SkImageInfo::Make(16, 16, kUnknown_SkColorType, kUnknown_SkAlphaType));
EXPECT_CALL(*pointer_, SetCursor(nullptr, 0, 0));
connection_->SetCursorBitmap({}, {});
connection_->ScheduleFlush();
Sync();
Mock::VerifyAndClearExpectations(pointer_);
EXPECT_CALL(*pointer_, SetCursor(Ne(nullptr), 5, 8));
connection_->SetCursorBitmap({dummy_cursor}, gfx::Point(5, 8));
connection_->ScheduleFlush();
Sync();
Mock::VerifyAndClearExpectations(pointer_);
}
INSTANTIATE_TEST_SUITE_P(XdgVersionV5Test, INSTANTIATE_TEST_SUITE_P(XdgVersionV5Test,
WaylandPointerTest, WaylandPointerTest,
::testing::Values(kXdgShellV5)); ::testing::Values(kXdgShellV5));
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display_observer.h" #include "ui/display/display_observer.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_pointer.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/wayland_connection.h" #include "ui/ozone/platform/wayland/wayland_connection.h"
#include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_output_manager.h"
...@@ -450,7 +450,7 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) { ...@@ -450,7 +450,7 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) {
Sync(); Sync();
wl::TestPointer* pointer = server_.seat()->pointer(); wl::MockPointer* pointer = server_.seat()->pointer();
ASSERT_TRUE(pointer); ASSERT_TRUE(pointer);
uint32_t serial = 0; uint32_t serial = 0;
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
#include "ui/base/hit_test.h" #include "ui/base/hit_test.h"
#include "ui/events/base_event_utils.h" #include "ui/events/base_event_utils.h"
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h" #include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_pointer.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/wayland_test.h" #include "ui/ozone/platform/wayland/wayland_test.h"
#include "ui/ozone/platform/wayland/wayland_util.h" #include "ui/ozone/platform/wayland/wayland_util.h"
...@@ -597,7 +597,7 @@ TEST_P(WaylandWindowTest, HasCaptureUpdatedOnPointerEvents) { ...@@ -597,7 +597,7 @@ TEST_P(WaylandWindowTest, HasCaptureUpdatedOnPointerEvents) {
Sync(); Sync();
wl::TestPointer* pointer = server_.seat()->pointer(); wl::MockPointer* pointer = server_.seat()->pointer();
ASSERT_TRUE(pointer); ASSERT_TRUE(pointer);
wl_pointer_send_enter(pointer->resource(), 1, surface_->resource(), 0, 0); wl_pointer_send_enter(pointer->resource(), 1, surface_->resource(), 0, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment