Handle custom/bitmap cursors in ws2.
Go through CursorFactoryOzone to load the PlatformCursor (which
confusingly could be CursorData, but for now is X11CursorOzone). Also
set fields on the cross-platform Cursor type so that code which relies
on it such as CursorWindowController::UpdateCursorImage will work.
That code path can be exercised with --ash-enable-cursor-motion-blur
although there is currently no use of custom cursors with ws2.
Bug: 837705
Change-Id: I9f725c5a5350e28cf8bded6b53bf48759cb644f8
Reviewed-on: https://chromium-review.googlesource.com/1105562
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#571213}
diff --git a/ash/cursor_unittest.cc b/ash/cursor_unittest.cc
index c251828..27356b1 100644
--- a/ash/cursor_unittest.cc
+++ b/ash/cursor_unittest.cc
@@ -13,6 +13,7 @@
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/test/event_generator.h"
+#include "ui/gfx/image/image_unittest_util.h"
namespace ash {
@@ -85,4 +86,28 @@
ash::Shell::Get()->cursor_manager()->GetCursor().native_type());
}
+TEST_F(CursorTest, Custom) {
+ // Create and hover a window.
+ std::unique_ptr<aura::Window> window =
+ CreateTestWindow(gfx::Rect(0, 0, 100, 100));
+ EXPECT_EQ(0U, GetTestWindowTreeClient()->input_events().size());
+ ui::test::EventGenerator generator(window.get());
+ generator.MoveMouseToInHost(50, 50);
+
+ // Set a custom cursor.
+ SkBitmap bitmap = gfx::test::CreateBitmap(10, 10);
+ const ui::CursorData image_cursor(gfx::Point(1, 4), {bitmap}, 1.f,
+ base::TimeDelta());
+ GetWindowTreeTestHelper()->SetCursor(window.get(), image_cursor);
+
+ // Make sure it worked.
+ EXPECT_EQ(ui::CursorType::kCustom,
+ window->delegate()->GetCursor({}).native_type());
+ aura::client::CursorClient* cursor_client =
+ aura::client::GetCursorClient(window->GetRootWindow());
+ EXPECT_EQ(ui::CursorType::kCustom, cursor_client->GetCursor().native_type());
+ EXPECT_EQ(bitmap.getGenerationID(),
+ cursor_client->GetCursor().GetBitmap().getGenerationID());
+}
+
} // namespace ash
diff --git a/services/ui/public/interfaces/cursor/cursor.mojom b/services/ui/public/interfaces/cursor/cursor.mojom
index ae4e453..0a267bc 100644
--- a/services/ui/public/interfaces/cursor/cursor.mojom
+++ b/services/ui/public/interfaces/cursor/cursor.mojom
@@ -69,7 +69,7 @@
// A description of a cursor.
struct CursorData {
- // The type of cursor. If CUSTOM, the rest of the fields are relevant.
+ // The type of cursor. If kCustom, the rest of the fields are relevant.
CursorType cursor_type;
// The delay between cursor frames.
@@ -78,7 +78,7 @@
// The hotspot in pixels in the source cursor frames.
gfx.mojom.Point hotspot_in_pixels;
- // The frames of the cursor.
+ // The frames of the cursor. Must be non-empty if |cursor_type| is kCustom.
array<skia.mojom.Bitmap?> cursor_frames;
// This is the image scale of this cursor.
diff --git a/services/ui/public/interfaces/cursor/cursor_struct_traits.cc b/services/ui/public/interfaces/cursor/cursor_struct_traits.cc
index f3a3888..17f0aff5 100644
--- a/services/ui/public/interfaces/cursor/cursor_struct_traits.cc
+++ b/services/ui/public/interfaces/cursor/cursor_struct_traits.cc
@@ -334,15 +334,21 @@
gfx::Point hotspot_in_pixels;
std::vector<SkBitmap> cursor_frames;
- float scale_factor = data.scale_factor();
base::TimeDelta frame_delay;
if (!data.ReadHotspotInPixels(&hotspot_in_pixels) ||
- !data.ReadCursorFrames(&cursor_frames) ||
+ !data.ReadCursorFrames(&cursor_frames) || cursor_frames.empty() ||
!data.ReadFrameDelay(&frame_delay)) {
return false;
}
+ // Clamp the scale factor to a reasonable value. TODO(estade): do we even need
+ // this field? It doesn't appear to be used anywhere and is a property of the
+ // display, not the cursor.
+ float scale_factor = data.scale_factor();
+ if (scale_factor < 1.f || scale_factor > 3.f)
+ return false;
+
*out = ui::CursorData(hotspot_in_pixels, cursor_frames, scale_factor,
frame_delay);
return true;
diff --git a/services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc b/services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc
index 0b26d67..f844eed 100644
--- a/services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc
+++ b/services/ui/public/interfaces/cursor/cursor_struct_traits_unittest.cc
@@ -114,4 +114,16 @@
EXPECT_TRUE(output.cursor_frames().front().empty());
}
+// For custom cursors, the frame image vector must be non-empty.
+TEST_F(CursorStructTraitsTest, TestMissingFrames) {
+ const base::TimeDelta kFrameDelay = base::TimeDelta::FromMilliseconds(15);
+ const gfx::Point kHotspot = gfx::Point(5, 2);
+ const float kScale = 2.0f;
+
+ ui::CursorData input(kHotspot, {}, kScale, kFrameDelay);
+
+ ui::CursorData output;
+ EXPECT_FALSE(EchoCursorData(input, &output));
+}
+
} // namespace ui
diff --git a/services/ui/ws/threaded_image_cursors.cc b/services/ui/ws/threaded_image_cursors.cc
index aaab5ca..f5178d7 100644
--- a/services/ui/ws/threaded_image_cursors.cc
+++ b/services/ui/ws/threaded_image_cursors.cc
@@ -91,10 +91,8 @@
scoped_refptr<base::SingleThreadTaskRunner> ui_service_task_runner_,
base::WeakPtr<ThreadedImageCursors> threaded_image_cursors_weak_ptr) {
if (image_cursors_weak_ptr) {
- ui::PlatformCursor platform_cursor = cursor_factory->CreateAnimatedCursor(
- cursor_data->cursor_frames(), cursor_data->hotspot_in_pixels(),
- cursor_data->frame_delay().InMilliseconds(),
- cursor_data->scale_factor());
+ ui::PlatformCursor platform_cursor =
+ cursor_data->ToNativeCursor().platform();
// |platform_window| is owned by the UI Service thread, so setting the
// cursor on it also needs to happen on that thread.
ui_service_task_runner_->PostTask(
diff --git a/services/ui/ws2/window_tree.cc b/services/ui/ws2/window_tree.cc
index 1ddc3f7d..4e91f5f0 100644
--- a/services/ui/ws2/window_tree.cc
+++ b/services/ui/ws2/window_tree.cc
@@ -947,18 +947,13 @@
return false;
}
if (!IsClientCreatedWindow(window) && !IsClientRootWindow(window)) {
- DVLOG(1) << "SerCursor failed (access denied)";
+ DVLOG(1) << "SetCursor failed (access denied)";
return false;
}
auto* server_window = ServerWindow::GetMayBeNull(window);
- // Convert from CursorData to Cursor. TODO(estade): remove this conversion.
- // See class level comment on CursorData. Also support kCustom, i.e. image
- // cursors.
- ui::Cursor old_cursor_type(cursor.cursor_type());
- if (cursor.cursor_type() == ui::CursorType::kCustom)
- NOTIMPLEMENTED_LOG_ONCE();
+ ui::Cursor old_cursor_type = cursor.ToNativeCursor();
// Ask our delegate to set the cursor. This will save the cursor for toplevels
// and also update the active cursor if appropriate (i.e. if |window| is the
diff --git a/ui/base/cursor/cursor_data.cc b/ui/base/cursor/cursor_data.cc
index 6c39f5a..27847f17 100644
--- a/ui/base/cursor/cursor_data.cc
+++ b/ui/base/cursor/cursor_data.cc
@@ -5,7 +5,15 @@
#include "ui/base/cursor/cursor_data.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/cursor/cursor_type.h"
+
+#if defined(USE_AURA)
#include "ui/base/cursor/cursor.h"
+#endif
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#endif
namespace ui {
@@ -48,4 +56,34 @@
generator_ids_ == rhs.generator_ids_;
}
+#if defined(USE_AURA)
+gfx::NativeCursor CursorData::ToNativeCursor() const {
+ Cursor cursor(cursor_type_);
+
+#if defined(USE_OZONE)
+ PlatformCursor platform;
+ auto* factory = CursorFactoryOzone::GetInstance();
+ if (cursor_type_ != CursorType::kCustom) {
+ platform = factory->GetDefaultCursor(cursor_type_);
+ } else if (cursor_frames_.size() > 1U) {
+ platform = factory->CreateAnimatedCursor(
+ cursor_frames_, hotspot_, frame_delay_.InMilliseconds(), scale_factor_);
+ } else {
+ DCHECK_EQ(1U, cursor_frames_.size());
+ platform =
+ factory->CreateImageCursor(cursor_frames_[0], hotspot_, scale_factor_);
+ }
+ cursor.SetPlatformCursor(platform);
+#else
+ NOTIMPLEMENTED();
+#endif
+
+ if (!cursor_frames_.empty()) {
+ cursor.set_custom_bitmap(cursor_frames_[0]);
+ cursor.set_custom_hotspot(hotspot_);
+ }
+ return cursor;
+}
+#endif
+
} // namespace ui
diff --git a/ui/base/cursor/cursor_data.h b/ui/base/cursor/cursor_data.h
index 6b3f5d2..ada9dd6 100644
--- a/ui/base/cursor/cursor_data.h
+++ b/ui/base/cursor/cursor_data.h
@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "ui/base/ui_base_export.h"
#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/native_widget_types.h"
class SkBitmap;
@@ -72,6 +73,11 @@
// data, but different generation ids.
bool IsSameAs(const CursorData& rhs) const;
+ // Until CursorData replaces Cursor, it's necessary to convert back to Cursor.
+ // Some code such as CursorWindowController still uses Cursor (and not its
+ // PlatformCursor).
+ gfx::NativeCursor ToNativeCursor() const;
+
private:
// A native type constant from cursor.h.
CursorType cursor_type_;