Commit 8c755bb0 authored by Fergus Dall's avatar Fergus Dall Committed by Commit Bot

Don't start a drag operation if the drag is released

Drag operations start asynchronously, both because applications may
take time before requesting one, and because we wait to read the drag
data before starting. It is therefore possible that the drag that
triggered the request will be released by the user before the request
starts.

We avoid this in two ways. Firstly, the wayland layer of exo will
reject start_drag requests that have a serial number corresponding to
a mouse or touchscreen press that has been released. Secondly, if a
release event occurs after a DragDropOperation is queued, but before it
starts, the operation will be aborted.

Bug: 927324
Change-Id: Ie84131df54f4b5a445c341b35b15d4afadf8fe3c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1715889
Commit-Queue: Fergus Dall <sidereal@google.com>
Reviewed-by: default avatarMitsuru Oshima (OOO 8/5,6) <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683900}
parent 685bf015
......@@ -129,6 +129,11 @@ DragDropOperation::~DragDropOperation() {
drag_drop_controller_->DragCancel();
}
void DragDropOperation::AbortIfPending() {
if (!started_by_this_object_)
delete this;
}
void DragDropOperation::OnTextRead(const std::string& mime_type,
base::string16 data) {
DCHECK(os_exchange_data_);
......
......@@ -52,6 +52,9 @@ class DragDropOperation : public DataSourceObserver,
Surface* icon,
ui::DragDropTypes::DragEventSource event_source);
// Abort the operation if it hasn't been started yet, otherwise do nothing.
void AbortIfPending();
// DataSourceObserver:
void OnDataSourceDestroying(DataSource* source) override;
......
......@@ -12,6 +12,7 @@
#include "components/exo/pointer_delegate.h"
#include "components/exo/pointer_gesture_pinch_delegate.h"
#include "components/exo/relative_pointer_delegate.h"
#include "components/exo/seat.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
......@@ -95,9 +96,10 @@ int GetContainerIdForMouseCursor() {
////////////////////////////////////////////////////////////////////////////////
// Pointer, public:
Pointer::Pointer(PointerDelegate* delegate)
Pointer::Pointer(PointerDelegate* delegate, Seat* seat)
: SurfaceTreeHost("ExoPointer"),
delegate_(delegate),
seat_(seat),
cursor_(ui::CursorType::kNull),
capture_scale_(GetCaptureDisplayInfo().device_scale_factor()),
capture_ratio_(GetCaptureDisplayInfo().GetDensityRatio()),
......@@ -365,10 +367,11 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
delegate_->OnPointerFrame();
}
}
switch (event->type()) {
case ui::ET_MOUSE_PRESSED:
case ui::ET_MOUSE_RELEASED: {
case ui::ET_MOUSE_RELEASED:
seat_->AbortPendingDragOperation();
FALLTHROUGH;
case ui::ET_MOUSE_PRESSED: {
delegate_->OnPointerButton(event->time_stamp(),
event->changed_button_flags(),
event->type() == ui::ET_MOUSE_PRESSED);
......
......@@ -38,6 +38,7 @@ namespace exo {
class PointerDelegate;
class PointerGesturePinchDelegate;
class RelativePointerDelegate;
class Seat;
class Surface;
class SurfaceTreeHost;
......@@ -50,7 +51,7 @@ class Pointer : public SurfaceTreeHost,
public aura::client::CursorClientObserver,
public aura::client::FocusChangeObserver {
public:
explicit Pointer(PointerDelegate* delegate);
Pointer(PointerDelegate* delegate, Seat* seat);
~Pointer() override;
PointerDelegate* delegate() const { return delegate_; }
......@@ -142,6 +143,8 @@ class Pointer : public SurfaceTreeHost,
// The delegate instance that all events are dispatched to.
PointerDelegate* const delegate_;
Seat* const seat_;
// The delegate instance that all pinch related events are dispatched to.
PointerGesturePinchDelegate* pinch_delegate_ = nullptr;
......
......@@ -11,7 +11,10 @@
#include "base/bind.h"
#include "base/run_loop.h"
#include "components/exo/buffer.h"
#include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h"
#include "components/exo/pointer_delegate.h"
#include "components/exo/seat.h"
#include "components/exo/shell_surface.h"
#include "components/exo/sub_surface.h"
#include "components/exo/surface.h"
......@@ -46,6 +49,22 @@ class MockPointerDelegate : public PointerDelegate {
MOCK_METHOD0(OnPointerFrame, void());
};
class TestDataSourceDelegate : public DataSourceDelegate {
public:
TestDataSourceDelegate() {}
// Overridden from DataSourceDelegate:
void OnDataSourceDestroying(DataSource* device) override {}
void OnTarget(const std::string& mime_type) override {}
void OnSend(const std::string& mime_type, base::ScopedFD fd) override {}
void OnCancelled() override {}
void OnDndDropPerformed() override {}
void OnDndFinished() override {}
void OnAction(DndAction dnd_action) override {}
DISALLOW_COPY_AND_ASSIGN(TestDataSourceDelegate);
};
class PointerTest : public test::ExoTestBase {
public:
PointerTest() = default;
......@@ -72,7 +91,8 @@ TEST_F(PointerTest, SetCursor) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -134,7 +154,8 @@ TEST_F(PointerTest, SetCursorNull) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -165,7 +186,8 @@ TEST_F(PointerTest, SetCursorType) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -223,7 +245,8 @@ TEST_F(PointerTest, SetCursorTypeOutsideOfSurface) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -255,7 +278,8 @@ TEST_F(PointerTest, SetCursorAndSetCursorType) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -328,7 +352,8 @@ TEST_F(PointerTest, SetCursorNullAndSetCursorType) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -372,7 +397,8 @@ TEST_F(PointerTest, OnPointerEnter) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -395,7 +421,8 @@ TEST_F(PointerTest, OnPointerLeave) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -428,7 +455,8 @@ TEST_F(PointerTest, OnPointerMotion) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -501,7 +529,8 @@ TEST_F(PointerTest, OnPointerButton) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -531,7 +560,8 @@ TEST_F(PointerTest, OnPointerScroll) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
gfx::Point location = surface->window()->GetBoundsInScreen().origin();
......@@ -566,7 +596,8 @@ TEST_F(PointerTest, OnPointerScrollWithThreeFinger) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
gfx::Point location = surface->window()->GetBoundsInScreen().origin();
......@@ -601,7 +632,8 @@ TEST_F(PointerTest, OnPointerScrollDiscrete) {
surface->Commit();
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
......@@ -636,7 +668,8 @@ TEST_F(PointerTest, RegisterPointerEventsOnModal) {
EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen());
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber());
......@@ -694,7 +727,8 @@ TEST_F(PointerTest, IgnorePointerEventsOnNonModalWhenModalIsOpen) {
EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen());
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber());
......@@ -755,7 +789,8 @@ TEST_F(PointerTest, IgnorePointerLeaveOnModal) {
EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen());
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber());
......@@ -802,7 +837,8 @@ TEST_F(PointerTest, RegisterPointerEventsOnNonModal) {
ash::CenterWindow(shell_surface2->GetWidget()->GetNativeWindow());
MockPointerDelegate delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
Seat seat;
std::unique_ptr<Pointer> pointer(new Pointer(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnPointerFrame()).Times(testing::AnyNumber());
......@@ -846,5 +882,41 @@ TEST_F(PointerTest, RegisterPointerEventsOnNonModal) {
pointer.reset();
}
TEST_F(PointerTest, DragDropAbort) {
Seat seat;
MockPointerDelegate pointer_delegate;
std::unique_ptr<Pointer> pointer(new Pointer(&pointer_delegate, &seat));
TestDataSourceDelegate data_source_delegate;
DataSource source(&data_source_delegate);
Surface origin, icon;
// Make origin into a real window so the pointer can click it
ShellSurface shell_surface(&origin);
Buffer buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(10, 10)));
origin.Attach(&buffer);
origin.Commit();
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(pointer_delegate, CanAcceptPointerEventsForSurface(&origin))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(pointer_delegate, OnPointerFrame()).Times(3);
EXPECT_CALL(pointer_delegate, OnPointerEnter(&origin, gfx::PointF(), 0));
generator.MoveMouseTo(origin.window()->GetBoundsInScreen().origin());
seat.StartDrag(&source, &origin, &icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_MOUSE);
EXPECT_TRUE(seat.get_drag_drop_operation_for_testing());
EXPECT_CALL(pointer_delegate, OnPointerButton).Times(2);
generator.PressLeftButton();
EXPECT_TRUE(seat.get_drag_drop_operation_for_testing());
generator.ReleaseLeftButton();
EXPECT_FALSE(seat.get_drag_drop_operation_for_testing());
EXPECT_CALL(pointer_delegate, OnPointerDestroying(pointer.get()));
pointer.reset();
}
} // namespace
} // namespace exo
......@@ -89,7 +89,13 @@ void Seat::StartDrag(DataSource* source,
Surface* icon,
ui::DragDropTypes::DragEventSource event_source) {
// DragDropOperation manages its own lifetime.
DragDropOperation::Create(source, origin, icon, event_source);
drag_drop_operation_ =
DragDropOperation::Create(source, origin, icon, event_source);
}
void Seat::AbortPendingDragOperation() {
if (drag_drop_operation_)
drag_drop_operation_->AbortIfPending();
}
void Seat::SetSelection(DataSource* source) {
......
......@@ -24,6 +24,7 @@ class KeyEvent;
} // namespace ui
namespace exo {
class DragDropOperation;
class ScopedDataSource;
class SeatObserver;
class Surface;
......@@ -71,6 +72,9 @@ class Seat : public aura::client::FocusChangeObserver,
Surface* icon,
ui::DragDropTypes::DragEventSource event_source);
// Abort any drag operations that haven't been started yet.
void AbortPendingDragOperation();
// Overridden from aura::client::FocusChangeObserver:
void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) override;
......@@ -94,6 +98,10 @@ class Seat : public aura::client::FocusChangeObserver,
physical_code_for_currently_processing_event;
}
base::WeakPtr<DragDropOperation> get_drag_drop_operation_for_testing() {
return drag_drop_operation_;
}
private:
class RefCountedScopedClipboardWriter
: public ui::ScopedClipboardWriter,
......@@ -146,6 +154,8 @@ class Seat : public aura::client::FocusChangeObserver,
// Data source being used as a clipboard content.
std::unique_ptr<ScopedDataSource> selection_source_;
base::WeakPtr<DragDropOperation> drag_drop_operation_;
// True while Seat is updating clipboard data to selection source.
bool changing_clipboard_data_to_selection_source_;
......
......@@ -11,6 +11,7 @@
#include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h"
#include "components/exo/seat_observer.h"
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
......@@ -443,5 +444,21 @@ TEST_F(SeatTest, PressedKeys) {
EXPECT_TRUE(seat.pressed_keys().empty());
}
TEST_F(SeatTest, DragDropAbort) {
Seat seat;
TestDataSourceDelegate delegate;
DataSource source(&delegate);
Surface origin, icon;
// Give origin a root window for DragDropOperation.
CurrentContext()->AddChild(origin.window());
seat.StartDrag(&source, &origin, &icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_MOUSE);
EXPECT_TRUE(seat.get_drag_drop_operation_for_testing());
seat.AbortPendingDragOperation();
EXPECT_FALSE(seat.get_drag_drop_operation_for_testing());
}
} // namespace
} // namespace exo
......@@ -5,6 +5,7 @@
#include "components/exo/touch.h"
#include "components/exo/input_trace.h"
#include "components/exo/seat.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "components/exo/touch_delegate.h"
......@@ -47,7 +48,8 @@ gfx::PointF EventLocationInWindow(ui::TouchEvent* event, aura::Window* window) {
////////////////////////////////////////////////////////////////////////////////
// Touch, public:
Touch::Touch(TouchDelegate* delegate) : delegate_(delegate) {
Touch::Touch(TouchDelegate* delegate, Seat* seat)
: delegate_(delegate), seat_(seat) {
WMHelper::GetInstance()->AddPreTargetHandler(this);
}
......@@ -127,6 +129,7 @@ void Touch::OnTouchEvent(ui::TouchEvent* event) {
}
delegate_->OnTouchUp(event->time_stamp(), touch_pointer_id);
seat_->AbortPendingDragOperation();
} break;
case ui::ET_TOUCH_MOVED: {
auto it = FindVectorItem(touch_points_, touch_pointer_id);
......@@ -155,6 +158,7 @@ void Touch::OnTouchEvent(ui::TouchEvent* event) {
// Cancel the full set of touch sequences as soon as one is canceled.
touch_points_.clear();
delegate_->OnTouchCancel();
seat_->AbortPendingDragOperation();
} break;
default:
NOTREACHED();
......
......@@ -18,6 +18,7 @@ class TouchEvent;
}
namespace exo {
class Seat;
class TouchDelegate;
class TouchStylusDelegate;
......@@ -25,7 +26,7 @@ class TouchStylusDelegate;
// touch devices.
class Touch : public ui::EventHandler, public SurfaceObserver {
public:
explicit Touch(TouchDelegate* delegate);
Touch(TouchDelegate* delegate, Seat* seat);
~Touch() override;
TouchDelegate* delegate() const { return delegate_; }
......@@ -47,6 +48,8 @@ class Touch : public ui::EventHandler, public SurfaceObserver {
// The delegate instance that all events are dispatched to.
TouchDelegate* const delegate_;
Seat* const seat_;
// The delegate instance that all stylus related events are dispatched to.
TouchStylusDelegate* stylus_delegate_ = nullptr;
......
......@@ -9,6 +9,9 @@
#include "ash/wm/window_positioner.h"
#include "ash/wm/window_positioning_utils.h"
#include "components/exo/buffer.h"
#include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h"
#include "components/exo/seat.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h"
......@@ -52,9 +55,26 @@ class MockTouchStylusDelegate : public TouchStylusDelegate {
MOCK_METHOD3(OnTouchTilt, void(base::TimeTicks, int, const gfx::Vector2dF&));
};
class TestDataSourceDelegate : public DataSourceDelegate {
public:
TestDataSourceDelegate() {}
// Overridden from DataSourceDelegate:
void OnDataSourceDestroying(DataSource* device) override {}
void OnTarget(const std::string& mime_type) override {}
void OnSend(const std::string& mime_type, base::ScopedFD fd) override {}
void OnCancelled() override {}
void OnDndDropPerformed() override {}
void OnDndFinished() override {}
void OnAction(DndAction dnd_action) override {}
DISALLOW_COPY_AND_ASSIGN(TestDataSourceDelegate);
};
TEST_F(TouchTest, OnTouchDown) {
MockTouchDelegate delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
auto bottom_window = exo_test_helper()->CreateWindow(10, 10, false);
......@@ -89,7 +109,8 @@ TEST_F(TouchTest, OnTouchUp) {
auto window = exo_test_helper()->CreateWindow(10, 10, false);
MockTouchDelegate delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnTouchShape(testing::_, testing::_, testing::_))
......@@ -119,7 +140,8 @@ TEST_F(TouchTest, OnTouchMotion) {
auto window = exo_test_helper()->CreateWindow(10, 10, false);
MockTouchDelegate delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnTouchShape(testing::_, testing::_, testing::_))
......@@ -154,7 +176,8 @@ TEST_F(TouchTest, OnTouchShape) {
auto window = exo_test_helper()->CreateWindow(10, 10, false);
MockTouchDelegate delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, CanAcceptTouchEventsForSurface(window.surface()))
......@@ -191,7 +214,8 @@ TEST_F(TouchTest, OnTouchCancel) {
auto window = exo_test_helper()->CreateWindow(10, 10, false);
MockTouchDelegate delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(delegate, OnTouchShape(testing::_, testing::_, testing::_))
......@@ -223,7 +247,8 @@ TEST_F(TouchTest, IgnoreTouchEventDuringModal) {
auto modal = exo_test_helper()->CreateWindow(5, 5, true);
MockTouchDelegate delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
// Make the window modal.
......@@ -295,7 +320,8 @@ TEST_F(TouchTest, OnTouchTool) {
MockTouchDelegate delegate;
MockTouchStylusDelegate stylus_delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
touch->SetStylusDelegate(&stylus_delegate);
......@@ -329,7 +355,8 @@ TEST_F(TouchTest, OnTouchForce) {
MockTouchDelegate delegate;
MockTouchStylusDelegate stylus_delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
touch->SetStylusDelegate(&stylus_delegate);
......@@ -365,7 +392,8 @@ TEST_F(TouchTest, OnTouchTilt) {
MockTouchDelegate delegate;
MockTouchStylusDelegate stylus_delegate;
std::unique_ptr<Touch> touch(new Touch(&delegate));
Seat seat;
std::unique_ptr<Touch> touch(new Touch(&delegate, &seat));
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
touch->SetStylusDelegate(&stylus_delegate);
......@@ -397,5 +425,42 @@ TEST_F(TouchTest, OnTouchTilt) {
touch.reset();
}
TEST_F(TouchTest, DragDropAbort) {
Seat seat;
MockTouchDelegate touch_delegate;
std::unique_ptr<Touch> touch(new Touch(&touch_delegate, &seat));
TestDataSourceDelegate data_source_delegate;
DataSource source(&data_source_delegate);
Surface origin, icon;
// Make origin into a real window so the touch can click it
ShellSurface shell_surface(&origin);
Buffer buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(10, 10)));
origin.Attach(&buffer);
origin.Commit();
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
EXPECT_CALL(touch_delegate, CanAcceptTouchEventsForSurface(&origin))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(touch_delegate, OnTouchFrame()).Times(2);
generator.MoveTouch(origin.window()->GetBoundsInScreen().origin());
seat.StartDrag(&source, &origin, &icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_MOUSE);
EXPECT_TRUE(seat.get_drag_drop_operation_for_testing());
EXPECT_CALL(touch_delegate, OnTouchDown).Times(1);
EXPECT_CALL(touch_delegate, OnTouchUp).Times(1);
EXPECT_CALL(touch_delegate, OnTouchShape).Times(1);
generator.PressTouch();
EXPECT_TRUE(seat.get_drag_drop_operation_for_testing());
generator.ReleaseTouch();
EXPECT_FALSE(seat.get_drag_drop_operation_for_testing());
EXPECT_CALL(touch_delegate, OnTouchDestroying(touch.get()));
touch.reset();
}
} // namespace
} // namespace exo
......@@ -28,6 +28,23 @@ uint32_t SerialTracker::GetNextSerial(EventType type) {
if ((max_event_ - min_event_) > kMaxEventsTracked)
min_event_ = max_event_ - kMaxEventsTracked;
switch (type) {
case EventType::POINTER_BUTTON_DOWN:
pointer_down_serial_ = serial;
break;
case EventType::POINTER_BUTTON_UP:
pointer_down_serial_ = base::nullopt;
break;
case EventType::TOUCH_DOWN:
touch_down_serial_ = serial;
break;
case EventType::TOUCH_UP:
touch_down_serial_ = base::nullopt;
break;
default:
break;
}
return serial;
}
......@@ -49,5 +66,17 @@ base::Optional<SerialTracker::EventType> SerialTracker::GetEventType(
return events_[serial % kMaxEventsTracked];
}
base::Optional<uint32_t> SerialTracker::GetPointerDownSerial() {
return pointer_down_serial_;
}
base::Optional<uint32_t> SerialTracker::GetTouchDownSerial() {
return touch_down_serial_;
}
void SerialTracker::ResetTouchDownSerial() {
touch_down_serial_ = base::nullopt;
}
} // namespace wayland
} // namespace exo
......@@ -22,7 +22,8 @@ class SerialTracker {
enum EventType {
POINTER_ENTER,
POINTER_LEAVE,
POINTER_BUTTON,
POINTER_BUTTON_DOWN,
POINTER_BUTTON_UP,
TOUCH_DOWN,
TOUCH_UP,
......@@ -35,6 +36,15 @@ class SerialTracker {
uint32_t GetNextSerial(EventType type);
// Get the serial number of the last {pointer,touch} pressed event, or nullopt
// if the press has since been released.
base::Optional<uint32_t> GetPointerDownSerial();
base::Optional<uint32_t> GetTouchDownSerial();
// Needed because wl_touch::cancel doesn't send a serial number, so we can't
// test for it in GetNextSerial.
void ResetTouchDownSerial();
// Get the EventType for a serial number, or nullopt if the serial number was
// never sent or is too old.
base::Optional<EventType> GetEventType(uint32_t serial) const;
......@@ -54,6 +64,9 @@ class SerialTracker {
uint32_t min_event_ = 1;
uint32_t max_event_ = 1;
base::Optional<uint32_t> pointer_down_serial_;
base::Optional<uint32_t> touch_down_serial_;
DISALLOW_COPY_AND_ASSIGN(SerialTracker);
};
......
......@@ -81,9 +81,11 @@ void WaylandPointerDelegate::OnPointerButton(base::TimeTicks time_stamp,
for (auto button : buttons) {
if (button_flags & button.flag) {
SendTimestamp(time_stamp);
SerialTracker::EventType event_type =
pressed ? SerialTracker::EventType::POINTER_BUTTON_DOWN
: SerialTracker::EventType::POINTER_BUTTON_UP;
wl_pointer_send_button(pointer_resource_,
serial_tracker_->GetNextSerial(
SerialTracker::EventType::POINTER_BUTTON),
serial_tracker_->GetNextSerial(event_type),
TimeTicksToMilliseconds(time_stamp), button.value,
pressed ? WL_POINTER_BUTTON_STATE_PRESSED
: WL_POINTER_BUTTON_STATE_RELEASED);
......
......@@ -74,6 +74,7 @@ void WaylandTouchDelegate::OnTouchFrame() {
}
void WaylandTouchDelegate::OnTouchCancel() {
wl_touch_send_cancel(touch_resource_);
serial_tracker_->ResetTouchDownSerial();
}
wl_client* WaylandTouchDelegate::client() const {
......
......@@ -295,11 +295,13 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate {
uint32_t serial) {
base::Optional<wayland::SerialTracker::EventType> event_type =
serial_tracker_->GetEventType(serial);
if (event_type == wayland::SerialTracker::EventType::POINTER_BUTTON) {
if (event_type == wayland::SerialTracker::EventType::POINTER_BUTTON_DOWN &&
serial_tracker_->GetPointerDownSerial() == serial) {
data_device->StartDrag(
source, origin, icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_MOUSE);
} else if (event_type == wayland::SerialTracker::EventType::TOUCH_DOWN) {
} else if (event_type == wayland::SerialTracker::EventType::TOUCH_DOWN &&
serial_tracker_->GetTouchDownSerial() == serial) {
data_device->StartDrag(
source, origin, icon,
ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_TOUCH);
......
......@@ -78,9 +78,11 @@ void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) {
wl_resource* pointer_resource = wl_resource_create(
client, &wl_pointer_interface, wl_resource_get_version(resource), id);
SetImplementation(pointer_resource, &pointer_implementation,
std::make_unique<Pointer>(new WaylandPointerDelegate(
pointer_resource, data->serial_tracker)));
SetImplementation(
pointer_resource, &pointer_implementation,
std::make_unique<Pointer>(
new WaylandPointerDelegate(pointer_resource, data->serial_tracker),
data->seat));
}
void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) {
......@@ -117,9 +119,11 @@ void seat_get_touch(wl_client* client, wl_resource* resource, uint32_t id) {
wl_resource* touch_resource = wl_resource_create(
client, &wl_touch_interface, wl_resource_get_version(resource), id);
SetImplementation(touch_resource, &touch_implementation,
std::make_unique<Touch>(new WaylandTouchDelegate(
touch_resource, data->serial_tracker)));
SetImplementation(
touch_resource, &touch_implementation,
std::make_unique<Touch>(
new WaylandTouchDelegate(touch_resource, data->serial_tracker),
data->seat));
}
void seat_release(wl_client* client, wl_resource* resource) {
......
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