blob: 6a8494b131f7272645488f68e07f143fc5fc3b09 [file] [log] [blame]
// Copyright 2014 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 <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <vector>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event.h"
#include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/events/platform/platform_event_source.h"
namespace {
static int SetNonBlocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
const char kTestDevicePath[] = "/dev/input/test-device";
} // namespace
namespace ui {
class MockTouchEventConverterEvdev : public TouchEventConverterEvdev {
public:
MockTouchEventConverterEvdev(int fd, base::FilePath path);
virtual ~MockTouchEventConverterEvdev() {};
void ConfigureReadMock(struct input_event* queue,
long read_this_many,
long queue_index);
unsigned size() { return dispatched_events_.size(); }
TouchEvent* event(unsigned index) {
DCHECK_GT(dispatched_events_.size(), index);
Event* ev = dispatched_events_[index];
DCHECK(ev->IsTouchEvent());
return static_cast<TouchEvent*>(ev);
}
// Actually dispatch the event reader code.
void ReadNow() {
OnFileCanReadWithoutBlocking(read_pipe_);
base::RunLoop().RunUntilIdle();
}
void DispatchCallback(scoped_ptr<Event> event) {
dispatched_events_.push_back(event.release());
}
virtual bool Reinitialize() override { return true; }
private:
int read_pipe_;
int write_pipe_;
ScopedVector<Event> dispatched_events_;
DISALLOW_COPY_AND_ASSIGN(MockTouchEventConverterEvdev);
};
MockTouchEventConverterEvdev::MockTouchEventConverterEvdev(int fd,
base::FilePath path)
: TouchEventConverterEvdev(
fd,
path,
1,
EventDeviceInfo(),
base::Bind(&MockTouchEventConverterEvdev::DispatchCallback,
base::Unretained(this))) {
pressure_min_ = 30;
pressure_max_ = 60;
// TODO(rjkroege): Check test axes.
x_min_pixels_ = x_min_tuxels_ = 0;
x_num_pixels_ = x_num_tuxels_ = std::numeric_limits<int>::max();
y_min_pixels_ = y_min_tuxels_ = 0;
y_num_pixels_ = y_num_tuxels_ = std::numeric_limits<int>::max();
int fds[2];
if (pipe(fds))
PLOG(FATAL) << "failed pipe";
DCHECK(SetNonBlocking(fds[0]) == 0)
<< "SetNonBlocking for pipe fd[0] failed, errno: " << errno;
DCHECK(SetNonBlocking(fds[1]) == 0)
<< "SetNonBlocking for pipe fd[0] failed, errno: " << errno;
read_pipe_ = fds[0];
write_pipe_ = fds[1];
}
void MockTouchEventConverterEvdev::ConfigureReadMock(struct input_event* queue,
long read_this_many,
long queue_index) {
int nwrite = HANDLE_EINTR(write(write_pipe_,
queue + queue_index,
sizeof(struct input_event) * read_this_many));
DCHECK(nwrite ==
static_cast<int>(sizeof(struct input_event) * read_this_many))
<< "write() failed, errno: " << errno;
}
} // namespace ui
// Test fixture.
class TouchEventConverterEvdevTest : public testing::Test {
public:
TouchEventConverterEvdevTest() {}
// Overridden from testing::Test:
virtual void SetUp() override {
// Set up pipe to satisfy message pump (unused).
int evdev_io[2];
if (pipe(evdev_io))
PLOG(FATAL) << "failed pipe";
events_in_ = evdev_io[0];
events_out_ = evdev_io[1];
loop_ = new base::MessageLoopForUI;
device_ = new ui::MockTouchEventConverterEvdev(
events_in_, base::FilePath(kTestDevicePath));
}
virtual void TearDown() override {
delete device_;
delete loop_;
}
ui::MockTouchEventConverterEvdev* device() { return device_; }
private:
base::MessageLoop* loop_;
ui::MockTouchEventConverterEvdev* device_;
int events_out_;
int events_in_;
DISALLOW_COPY_AND_ASSIGN(TouchEventConverterEvdevTest);
};
// TODO(rjkroege): Test for valid handling of time stamps.
TEST_F(TouchEventConverterEvdevTest, TouchDown) {
ui::MockTouchEventConverterEvdev* dev = device();
struct input_event mock_kernel_queue[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 684},
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
dev->ConfigureReadMock(mock_kernel_queue, 1, 0);
dev->ReadNow();
EXPECT_EQ(0u, dev->size());
dev->ConfigureReadMock(mock_kernel_queue, 2, 1);
dev->ReadNow();
EXPECT_EQ(0u, dev->size());
dev->ConfigureReadMock(mock_kernel_queue, 3, 3);
dev->ReadNow();
EXPECT_EQ(1u, dev->size());
ui::TouchEvent* event = dev->event(0);
EXPECT_FALSE(event == NULL);
EXPECT_EQ(ui::ET_TOUCH_PRESSED, event->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), event->time_stamp());
EXPECT_EQ(42, event->x());
EXPECT_EQ(51, event->y());
EXPECT_EQ(0, event->touch_id());
EXPECT_FLOAT_EQ(1.5f, event->radius_x());
EXPECT_FLOAT_EQ(.5f, event->force());
EXPECT_FLOAT_EQ(0.f, event->rotation_angle());
}
TEST_F(TouchEventConverterEvdevTest, NoEvents) {
ui::MockTouchEventConverterEvdev* dev = device();
dev->ConfigureReadMock(NULL, 0, 0);
EXPECT_EQ(0u, dev->size());
}
TEST_F(TouchEventConverterEvdevTest, TouchMove) {
ui::MockTouchEventConverterEvdev* dev = device();
struct input_event mock_kernel_queue_press[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 684},
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
struct input_event mock_kernel_queue_move1[] = {
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 50},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 43}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
struct input_event mock_kernel_queue_move2[] = {
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 42}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Setup and discard a press.
dev->ConfigureReadMock(mock_kernel_queue_press, 6, 0);
dev->ReadNow();
EXPECT_EQ(1u, dev->size());
dev->ConfigureReadMock(mock_kernel_queue_move1, 4, 0);
dev->ReadNow();
EXPECT_EQ(2u, dev->size());
ui::TouchEvent* event = dev->event(1);
EXPECT_FALSE(event == NULL);
EXPECT_EQ(ui::ET_TOUCH_MOVED, event->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), event->time_stamp());
EXPECT_EQ(42, event->x());
EXPECT_EQ(43, event->y());
EXPECT_EQ(0, event->touch_id());
EXPECT_FLOAT_EQ(2.f / 3.f, event->force());
EXPECT_FLOAT_EQ(0.f, event->rotation_angle());
dev->ConfigureReadMock(mock_kernel_queue_move2, 2, 0);
dev->ReadNow();
EXPECT_EQ(3u, dev->size());
event = dev->event(2);
EXPECT_FALSE(event == NULL);
EXPECT_EQ(ui::ET_TOUCH_MOVED, event->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), event->time_stamp());
EXPECT_EQ(42, event->x());
EXPECT_EQ(42, event->y());
EXPECT_EQ(0, event->touch_id());
EXPECT_FLOAT_EQ(2.f / 3.f, event->force());
EXPECT_FLOAT_EQ(0.f, event->rotation_angle());
}
TEST_F(TouchEventConverterEvdevTest, TouchRelease) {
ui::MockTouchEventConverterEvdev* dev = device();
struct input_event mock_kernel_queue_press[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 684},
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
struct input_event mock_kernel_queue_release[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, -1}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Setup and discard a press.
dev->ConfigureReadMock(mock_kernel_queue_press, 6, 0);
dev->ReadNow();
EXPECT_EQ(1u, dev->size());
ui::TouchEvent* event = dev->event(0);
EXPECT_FALSE(event == NULL);
dev->ConfigureReadMock(mock_kernel_queue_release, 2, 0);
dev->ReadNow();
EXPECT_EQ(2u, dev->size());
event = dev->event(1);
EXPECT_FALSE(event == NULL);
EXPECT_EQ(ui::ET_TOUCH_RELEASED, event->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), event->time_stamp());
EXPECT_EQ(42, event->x());
EXPECT_EQ(51, event->y());
EXPECT_EQ(0, event->touch_id());
EXPECT_FLOAT_EQ(.5f, event->force());
EXPECT_FLOAT_EQ(0.f, event->rotation_angle());
}
TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) {
ui::MockTouchEventConverterEvdev* dev = device();
ui::TouchEvent* ev0;
ui::TouchEvent* ev1;
struct input_event mock_kernel_queue_press0[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 684},
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Setup and discard a press.
dev->ConfigureReadMock(mock_kernel_queue_press0, 6, 0);
dev->ReadNow();
EXPECT_EQ(1u, dev->size());
struct input_event mock_kernel_queue_move0[] = {
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 40}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Setup and discard a move.
dev->ConfigureReadMock(mock_kernel_queue_move0, 2, 0);
dev->ReadNow();
EXPECT_EQ(2u, dev->size());
struct input_event mock_kernel_queue_move0press1[] = {
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 40}, {{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_ABS, ABS_MT_SLOT, 1}, {{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 686},
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 101},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 102}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Move on 0, press on 1.
dev->ConfigureReadMock(mock_kernel_queue_move0press1, 9, 0);
dev->ReadNow();
EXPECT_EQ(4u, dev->size());
ev0 = dev->event(2);
ev1 = dev->event(3);
// Move
EXPECT_EQ(ui::ET_TOUCH_MOVED, ev0->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev0->time_stamp());
EXPECT_EQ(40, ev0->x());
EXPECT_EQ(51, ev0->y());
EXPECT_EQ(0, ev0->touch_id());
EXPECT_FLOAT_EQ(.5f, ev0->force());
EXPECT_FLOAT_EQ(0.f, ev0->rotation_angle());
// Press
EXPECT_EQ(ui::ET_TOUCH_PRESSED, ev1->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev1->time_stamp());
EXPECT_EQ(101, ev1->x());
EXPECT_EQ(102, ev1->y());
EXPECT_EQ(1, ev1->touch_id());
EXPECT_FLOAT_EQ(.5f, ev1->force());
EXPECT_FLOAT_EQ(0.f, ev1->rotation_angle());
// Stationary 0, Moves 1.
struct input_event mock_kernel_queue_stationary0_move1[] = {
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 40}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
dev->ConfigureReadMock(mock_kernel_queue_stationary0_move1, 2, 0);
dev->ReadNow();
EXPECT_EQ(5u, dev->size());
ev1 = dev->event(4);
EXPECT_EQ(ui::ET_TOUCH_MOVED, ev1->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev1->time_stamp());
EXPECT_EQ(40, ev1->x());
EXPECT_EQ(102, ev1->y());
EXPECT_EQ(1, ev1->touch_id());
EXPECT_FLOAT_EQ(.5f, ev1->force());
EXPECT_FLOAT_EQ(0.f, ev1->rotation_angle());
// Move 0, stationary 1.
struct input_event mock_kernel_queue_move0_stationary1[] = {
{{0, 0}, EV_ABS, ABS_MT_SLOT, 0}, {{0, 0}, EV_ABS, ABS_MT_POSITION_X, 39},
{{0, 0}, EV_SYN, SYN_REPORT, 0}
};
dev->ConfigureReadMock(mock_kernel_queue_move0_stationary1, 3, 0);
dev->ReadNow();
EXPECT_EQ(6u, dev->size());
ev0 = dev->event(5);
EXPECT_EQ(ui::ET_TOUCH_MOVED, ev0->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev0->time_stamp());
EXPECT_EQ(39, ev0->x());
EXPECT_EQ(51, ev0->y());
EXPECT_EQ(0, ev0->touch_id());
EXPECT_FLOAT_EQ(.5f, ev0->force());
EXPECT_FLOAT_EQ(0.f, ev0->rotation_angle());
// Release 0, move 1.
struct input_event mock_kernel_queue_release0_move1[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, -1}, {{0, 0}, EV_ABS, ABS_MT_SLOT, 1},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 38}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
dev->ConfigureReadMock(mock_kernel_queue_release0_move1, 4, 0);
dev->ReadNow();
EXPECT_EQ(8u, dev->size());
ev0 = dev->event(6);
ev1 = dev->event(7);
EXPECT_EQ(ui::ET_TOUCH_RELEASED, ev0->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev0->time_stamp());
EXPECT_EQ(39, ev0->x());
EXPECT_EQ(51, ev0->y());
EXPECT_EQ(0, ev0->touch_id());
EXPECT_FLOAT_EQ(.5f, ev0->force());
EXPECT_FLOAT_EQ(0.f, ev0->rotation_angle());
EXPECT_EQ(ui::ET_TOUCH_MOVED, ev1->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev1->time_stamp());
EXPECT_EQ(38, ev1->x());
EXPECT_EQ(102, ev1->y());
EXPECT_EQ(1, ev1->touch_id());
EXPECT_FLOAT_EQ(.5f, ev1->force());
EXPECT_FLOAT_EQ(0.f, ev1->rotation_angle());
// Release 1.
struct input_event mock_kernel_queue_release1[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, -1}, {{0, 0}, EV_SYN, SYN_REPORT, 0},
};
dev->ConfigureReadMock(mock_kernel_queue_release1, 2, 0);
dev->ReadNow();
EXPECT_EQ(9u, dev->size());
ev1 = dev->event(8);
EXPECT_EQ(ui::ET_TOUCH_RELEASED, ev1->type());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(0), ev1->time_stamp());
EXPECT_EQ(38, ev1->x());
EXPECT_EQ(102, ev1->y());
EXPECT_EQ(1, ev1->touch_id());
EXPECT_FLOAT_EQ(.5f, ev1->force());
EXPECT_FLOAT_EQ(0.f, ev1->rotation_angle());
}
TEST_F(TouchEventConverterEvdevTest, TypeA) {
ui::MockTouchEventConverterEvdev* dev = device();
struct input_event mock_kernel_queue_press0[] = {
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51},
{{0, 0}, EV_SYN, SYN_MT_REPORT, 0},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 61},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 71},
{{0, 0}, EV_SYN, SYN_MT_REPORT, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Check that two events are generated.
dev->ConfigureReadMock(mock_kernel_queue_press0, 10, 0);
dev->ReadNow();
EXPECT_EQ(2u, dev->size());
}
TEST_F(TouchEventConverterEvdevTest, Unsync) {
ui::MockTouchEventConverterEvdev* dev = device();
struct input_event mock_kernel_queue_press0[] = {
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 684},
{{0, 0}, EV_ABS, ABS_MT_TOUCH_MAJOR, 3},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 45},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 42},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 51}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
dev->ConfigureReadMock(mock_kernel_queue_press0, 6, 0);
dev->ReadNow();
EXPECT_EQ(1u, dev->size());
// Prepare a move with a drop.
struct input_event mock_kernel_queue_move0[] = {
{{0, 0}, EV_SYN, SYN_DROPPED, 0},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 40}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Verify that we didn't receive it/
dev->ConfigureReadMock(mock_kernel_queue_move0, 3, 0);
dev->ReadNow();
EXPECT_EQ(1u, dev->size());
struct input_event mock_kernel_queue_move1[] = {
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 40}, {{0, 0}, EV_SYN, SYN_REPORT, 0}
};
// Verify that it re-syncs after a SYN_REPORT.
dev->ConfigureReadMock(mock_kernel_queue_move1, 2, 0);
dev->ReadNow();
EXPECT_EQ(2u, dev->size());
}
// crbug.com/407386
TEST_F(TouchEventConverterEvdevTest,
DontChangeMultitouchPositionFromLegacyAxes) {
ui::MockTouchEventConverterEvdev* dev = device();
struct input_event mock_kernel_queue[] = {
{{0, 0}, EV_ABS, ABS_MT_SLOT, 0},
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 100},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 999},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 888},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 55},
{{0, 0}, EV_ABS, ABS_MT_SLOT, 1},
{{0, 0}, EV_ABS, ABS_MT_TRACKING_ID, 200},
{{0, 0}, EV_ABS, ABS_MT_PRESSURE, 44},
{{0, 0}, EV_ABS, ABS_MT_POSITION_X, 777},
{{0, 0}, EV_ABS, ABS_MT_POSITION_Y, 666},
{{0, 0}, EV_ABS, ABS_X, 999},
{{0, 0}, EV_ABS, ABS_Y, 888},
{{0, 0}, EV_ABS, ABS_PRESSURE, 55},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
};
// Check that two events are generated.
dev->ConfigureReadMock(mock_kernel_queue, arraysize(mock_kernel_queue), 0);
dev->ReadNow();
const unsigned int kExpectedEventCount = 2;
EXPECT_EQ(kExpectedEventCount, dev->size());
if (kExpectedEventCount != dev->size())
return;
ui::TouchEvent* ev0 = dev->event(0);
ui::TouchEvent* ev1 = dev->event(1);
EXPECT_EQ(0, ev0->touch_id());
EXPECT_EQ(999, ev0->x());
EXPECT_EQ(888, ev0->y());
EXPECT_FLOAT_EQ(0.8333333f, ev0->force());
EXPECT_EQ(1, ev1->touch_id());
EXPECT_EQ(777, ev1->x());
EXPECT_EQ(666, ev1->y());
EXPECT_FLOAT_EQ(0.4666666f, ev1->force());
}