Commit 5ae5e597 authored by Nicholas Hollingum's avatar Nicholas Hollingum Committed by Commit Bot

Propagate ordinal motion to Exo clients (if it is available)

On some platforms (including CrOS, as of crrev.com/c/2277694) ordinal
motion will be available as part of mouse motion events.

We use ordinal motion to provide unaccelerated motion in the
zwp_relative_pointer interface. In the event that ordinal motion is not
available, we default to the old behaviour (i.e. using accelerated
motion).

This behaviour is guarded by a flag, as the units it provides on CrOS
are disproportionately small (compared with the standard cursor
acceleration).

Bug: b/161755250
Change-Id: I774db6ef7db7460f1c5674fffdaca65e7fb170fa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2371088
Commit-Queue: Nic Hollingum <hollingum@google.com>
Reviewed-by: default avatarJun Mukai <mukai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811137}
parent 83ded747
......@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/optional.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/exo/input_trace.h"
#include "components/exo/pointer_constraint_delegate.h"
......@@ -33,7 +34,9 @@
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/widget/widget.h"
......@@ -409,9 +412,23 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
? SameLocation(location_in_root, location_)
: gfx::ToFlooredPoint(location_in_root) ==
gfx::ToFlooredPoint(location_);
// Ordinal motion is sent only on platforms that support it, which is
// indicated by the presence of a flag.
//
// TODO(b/161755250): the ifdef is only necessary because of the feature
// flag. This code should work fine on non-cros.
base::Optional<gfx::Vector2dF> ordinal_motion = base::nullopt;
#if defined(OS_CHROMEOS)
if (event->flags() & ui::EF_UNADJUSTED_MOUSE &&
base::FeatureList::IsEnabled(chromeos::features::kExoOrdinalMotion)) {
ordinal_motion = event->movement();
}
#endif
if (!same_location) {
bool needs_frame =
HandleRelativePointerMotion(event->time_stamp(), location_in_root);
bool needs_frame = HandleRelativePointerMotion(
event->time_stamp(), location_in_root, ordinal_motion);
if (capture_window_) {
if (ShouldMoveToCenter())
MoveCursorToCenterOfActiveDisplay();
......@@ -803,8 +820,10 @@ void Pointer::MoveCursorToCenterOfActiveDisplay() {
root->MoveCursorTo(p);
}
bool Pointer::HandleRelativePointerMotion(base::TimeTicks time_stamp,
gfx::PointF location_in_root) {
bool Pointer::HandleRelativePointerMotion(
base::TimeTicks time_stamp,
gfx::PointF location_in_root,
const base::Optional<gfx::Vector2dF>& ordinal_motion) {
if (!relative_pointer_delegate_)
return false;
......@@ -821,9 +840,10 @@ bool Pointer::HandleRelativePointerMotion(base::TimeTicks time_stamp,
}
}
gfx::PointF delta(location_in_root.x() - location_.x(),
location_in_root.y() - location_.y());
relative_pointer_delegate_->OnPointerRelativeMotion(time_stamp, delta);
gfx::Vector2dF delta = location_in_root - location_;
relative_pointer_delegate_->OnPointerRelativeMotion(
time_stamp, delta,
ordinal_motion.has_value() ? ordinal_motion.value() : delta);
return true;
}
......
......@@ -154,9 +154,13 @@ class Pointer : public SurfaceTreeHost,
void MoveCursorToCenterOfActiveDisplay();
// Process the delta for relative pointer motion. Returns true if relative
// motion was sent to the delegate, false otherwise.
bool HandleRelativePointerMotion(base::TimeTicks time_stamp,
gfx::PointF location_in_target);
// motion was sent to the delegate, false otherwise. If |ordinal_motion| is
// supplied, it will be used for determining physical motion, otherwise
// physical motion will be the relative delta.
bool HandleRelativePointerMotion(
base::TimeTicks time_stamp,
gfx::PointF location_in_target,
const base::Optional<gfx::Vector2dF>& ordinal_motion);
// The delegate instance that all events are dispatched to.
PointerDelegate* const delegate_;
......
......@@ -32,8 +32,12 @@
#include "ui/aura/client/focus_client.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/views/widget/widget.h"
#if defined(OS_CHROMEOS)
......@@ -67,8 +71,10 @@ class MockRelativePointerDelegate : public RelativePointerDelegate {
// Overridden from RelativePointerDelegate:
MOCK_METHOD1(OnPointerDestroying, void(Pointer*));
MOCK_METHOD2(OnPointerRelativeMotion,
void(base::TimeTicks, const gfx::PointF&));
MOCK_METHOD3(OnPointerRelativeMotion,
void(base::TimeTicks,
const gfx::Vector2dF&,
const gfx::Vector2dF&));
};
class MockPointerConstraintDelegate : public PointerConstraintDelegate {
......@@ -987,14 +993,16 @@ TEST_F(PointerTest, OnPointerRelativeMotion) {
generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin());
EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1)));
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::PointF(1, 1)));
EXPECT_CALL(
relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(1, 1), testing::_));
generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() +
gfx::Vector2d(1, 1));
EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(2, 2)));
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::PointF(1, 1)));
EXPECT_CALL(
relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(1, 1), testing::_));
generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() +
gfx::Vector2d(2, 2));
......@@ -1016,13 +1024,15 @@ TEST_F(PointerTest, OnPointerRelativeMotion) {
// OnPointerMotion will not be called, because the pointer location is already
// sent with OnPointerEnter, but we should still receive
// OnPointerRelativeMotion.
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::PointF(3, 3)));
EXPECT_CALL(
relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(3, 3), testing::_));
generator.MoveMouseTo(sub_surface->window()->GetBoundsInScreen().origin());
EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(1, 1)));
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::PointF(1, 1)));
EXPECT_CALL(
relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(1, 1), testing::_));
generator.MoveMouseTo(sub_surface->window()->GetBoundsInScreen().origin() +
gfx::Vector2d(1, 1));
......@@ -1049,13 +1059,15 @@ TEST_F(PointerTest, OnPointerRelativeMotion) {
// OnPointerMotion will not be called, because the pointer location is already
// sent with OnPointerEnter, but we should still receive
// OnPointerRelativeMotion.
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::PointF(9, 9)));
EXPECT_CALL(
relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(9, 9), testing::_));
generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin());
EXPECT_CALL(delegate, OnPointerMotion(testing::_, gfx::PointF(10, 10)));
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::PointF(10, 10)));
EXPECT_CALL(
relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(10, 10), testing::_));
generator.MoveMouseTo(child_surface->window()->GetBoundsInScreen().origin() +
gfx::Vector2d(10, 10));
......@@ -1064,6 +1076,60 @@ TEST_F(PointerTest, OnPointerRelativeMotion) {
pointer.reset();
}
TEST_F(PointerTest, OrdinalMotionOverridesRelativeMotion) {
auto surface = std::make_unique<Surface>();
auto shell_surface = std::make_unique<ShellSurface>(surface.get());
gfx::Size buffer_size(10, 10);
std::unique_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
surface->Attach(buffer.get());
surface->Commit();
// Set up the pointer and move it to the origin.
testing::NiceMock<MockPointerDelegate> delegate;
Seat seat;
auto pointer = std::make_unique<Pointer>(&delegate, &seat);
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
.WillRepeatedly(testing::Return(true));
gfx::Point origin = surface->window()->GetBoundsInScreen().origin();
generator.MoveMouseTo(origin);
// Start sending relative motion events.
testing::StrictMock<MockRelativePointerDelegate> relative_delegate;
pointer->RegisterRelativePointerDelegate(&relative_delegate);
// By default, ordinal and relative are the same.
gfx::Point new_location = origin + gfx::Vector2d(1, 1);
ui::MouseEvent ev1(ui::ET_MOUSE_MOVED, new_location, new_location,
ui::EventTimeForNow(), generator.flags(), 0);
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(1, 1),
gfx::Vector2dF(1, 1)));
generator.Dispatch(&ev1);
// When set, ordinal overrides the relative motion.
//
// TODO(b/161755250): the ifdef is only necessary because of the feature
// flag. This code should work fine on non-cros.
#if defined(OS_CHROMEOS)
new_location = new_location + gfx::Vector2d(1, 1);
ui::MouseEvent ev2(ui::ET_MOUSE_MOVED, new_location, new_location,
ui::EventTimeForNow(), generator.flags(), 0);
ui::MouseEvent::DispatcherApi(&ev2).set_movement(gfx::Vector2dF(99, 99));
EXPECT_CALL(relative_delegate,
OnPointerRelativeMotion(testing::_, gfx::Vector2dF(1, 1),
gfx::Vector2dF(99, 99)));
// This feature is gated behind a flag.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
chromeos::features::kExoOrdinalMotion);
generator.Dispatch(&ev2);
#endif
pointer->UnregisterRelativePointerDelegate(&relative_delegate);
}
#if defined(OS_CHROMEOS)
TEST_F(PointerTest, ConstrainPointer) {
auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
......
......@@ -6,6 +6,7 @@
#define COMPONENTS_EXO_RELATIVE_POINTER_DELEGATE_H_
#include "base/time/time.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace exo {
class Pointer;
......@@ -17,8 +18,12 @@ class RelativePointerDelegate {
// chance to remove themselves.
virtual void OnPointerDestroying(Pointer* pointer) = 0;
virtual void OnPointerRelativeMotion(base::TimeTicks time_stamp,
const gfx::PointF& relativeMotion) = 0;
// Called to indicate motion. |relative_motion| is given in screen-terms,
// whereas |ordinal_motion| comes from hardware and e.g. is not accelerated.
virtual void OnPointerRelativeMotion(
base::TimeTicks time_stamp,
const gfx::Vector2dF& relative_motion,
const gfx::Vector2dF& ordinal_motion) = 0;
protected:
virtual ~RelativePointerDelegate() {}
......
......@@ -35,15 +35,16 @@ class WaylandRelativePointerDelegate : public RelativePointerDelegate {
}
void OnPointerDestroying(Pointer* pointer) override { pointer_ = nullptr; }
void OnPointerRelativeMotion(base::TimeTicks time_stamp,
const gfx::PointF& relativeMotion) override {
const gfx::Vector2dF& relative_motion,
const gfx::Vector2dF& ordinal_motion) override {
zwp_relative_pointer_v1_send_relative_motion(
resource_, // resource
0, // utime_hi
TimeTicksToMilliseconds(time_stamp), // utime_lo
wl_fixed_from_double(relativeMotion.x()), // dx
wl_fixed_from_double(relativeMotion.y()), // dy
wl_fixed_from_double(relativeMotion.x()), // dx_unaccel
wl_fixed_from_double(relativeMotion.y())); // dy_unaccel
wl_fixed_from_double(relative_motion.x()), // dx
wl_fixed_from_double(relative_motion.y()), // dy
wl_fixed_from_double(ordinal_motion.x()), // dx_unaccel
wl_fixed_from_double(ordinal_motion.y())); // dy_unaccel
}
private:
......
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