Commit 2e2f5a00 authored by Sean O'Brien's avatar Sean O'Brien Committed by Commit Bot

ozone: Add stylus button converter

Add an event converter to handle bluetooth stylus buttons.  Since we
only support one device so far, hard-code the key generated by
double-click.

Bug: b:139781900

Change-Id: I546845563b55f6001259cb198850e43a4e404156
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2079893
Commit-Queue: Sean O'Brien <seobrien@chromium.org>
Reviewed-by: default avatarMichael Spang <spang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747449}
parent 05a30b0c
...@@ -67,6 +67,8 @@ component("evdev") { ...@@ -67,6 +67,8 @@ component("evdev") {
"keyboard_util_evdev.h", "keyboard_util_evdev.h",
"mouse_button_map_evdev.cc", "mouse_button_map_evdev.cc",
"mouse_button_map_evdev.h", "mouse_button_map_evdev.h",
"stylus_button_event_converter_evdev.cc",
"stylus_button_event_converter_evdev.h",
"tablet_event_converter_evdev.cc", "tablet_event_converter_evdev.cc",
"tablet_event_converter_evdev.h", "tablet_event_converter_evdev.h",
"touch_evdev_debug_buffer.cc", "touch_evdev_debug_buffer.cc",
...@@ -167,6 +169,7 @@ source_set("unittests") { ...@@ -167,6 +169,7 @@ source_set("unittests") {
"event_device_test_util.h", "event_device_test_util.h",
"gamepad_event_converter_evdev_unittest.cc", "gamepad_event_converter_evdev_unittest.cc",
"input_injector_evdev_unittest.cc", "input_injector_evdev_unittest.cc",
"stylus_button_event_converter_evdev_unittest.cc",
"tablet_event_converter_evdev_unittest.cc", "tablet_event_converter_evdev_unittest.cc",
"testing/fake_cursor_delegate_evdev.h", "testing/fake_cursor_delegate_evdev.h",
"touch_event_converter_evdev_unittest.cc", "touch_event_converter_evdev_unittest.cc",
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ui/events/ozone/evdev/event_converter_evdev_impl.h" #include "ui/events/ozone/evdev/event_converter_evdev_impl.h"
#include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/evdev/event_device_info.h"
#include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h" #include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h"
#include "ui/events/ozone/evdev/stylus_button_event_converter_evdev.h"
#include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h"
#include "ui/events/ozone/evdev/touch_event_converter_evdev.h" #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h" #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
...@@ -124,6 +125,12 @@ std::unique_ptr<EventConverterEvdev> CreateConverter( ...@@ -124,6 +125,12 @@ std::unique_ptr<EventConverterEvdev> CreateConverter(
std::move(fd), params.path, params.id, devinfo, params.dispatcher)); std::move(fd), params.path, params.id, devinfo, params.dispatcher));
} }
if (devinfo.IsStylusButtonDevice()) {
return base::WrapUnique<EventConverterEvdev>(
new StylusButtonEventConverterEvdev(
std::move(fd), params.path, params.id, devinfo, params.dispatcher));
}
// Everything else: use EventConverterEvdevImpl. // Everything else: use EventConverterEvdevImpl.
return base::WrapUnique<EventConverterEvdevImpl>( return base::WrapUnique<EventConverterEvdevImpl>(
new EventConverterEvdevImpl(std::move(fd), params.path, params.id, new EventConverterEvdevImpl(std::move(fd), params.path, params.id,
......
// Copyright 2020 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/events/ozone/evdev/stylus_button_event_converter_evdev.h"
#include <errno.h>
#include <linux/input.h>
#include "base/trace_event/trace_event.h"
#include "ui/events/event_utils.h"
#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
namespace ui {
namespace {
const int kKeyReleaseValue = 0;
} // namespace
StylusButtonEventConverterEvdev::StylusButtonEventConverterEvdev(
base::ScopedFD fd,
base::FilePath path,
int id,
const EventDeviceInfo& devinfo,
DeviceEventDispatcherEvdev* dispatcher)
: EventConverterEvdev(fd.get(),
path,
id,
devinfo.device_type(),
devinfo.name(),
devinfo.phys(),
devinfo.vendor_id(),
devinfo.product_id(),
devinfo.version()),
input_device_fd_(std::move(fd)),
dispatcher_(dispatcher) {}
StylusButtonEventConverterEvdev::~StylusButtonEventConverterEvdev() {}
void StylusButtonEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
TRACE_EVENT1("evdev",
"StylusButtonEventConverterEvdev::OnFileCanReadWithoutBlocking",
"fd", fd);
while (true) {
input_event input;
ssize_t read_size = read(fd, &input, sizeof(input));
if (read_size != sizeof(input)) {
if (errno == EINTR || errno == EAGAIN)
return;
if (errno != ENODEV)
PLOG(ERROR) << "error reading device " << path_.value();
Stop();
return;
}
ProcessEvent(input);
}
}
void StylusButtonEventConverterEvdev::ProcessEvent(const input_event& input) {
if (input.type == EV_KEY && input.code == KEY_F19) {
bool down = input.value != kKeyReleaseValue;
dispatcher_->DispatchKeyEvent(KeyEventParams(
input_device_.id, ui::EF_IS_STYLUS_BUTTON, input.code, down,
true /* suppress_auto_repeat */, TimeTicksFromInputEvent(input)));
}
}
} // namespace ui
// Copyright 2020 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.
#ifndef UI_EVENTS_OZONE_EVDEV_STYLUS_BUTTON_EVENT_CONVERTER_EVDEV_H_
#define UI_EVENTS_OZONE_EVDEV_STYLUS_BUTTON_EVENT_CONVERTER_EVDEV_H_
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "ui/events/ozone/evdev/event_converter_evdev.h"
#include "ui/events/ozone/evdev/event_device_info.h"
namespace ui {
class DeviceEventDispatcherEvdev;
class COMPONENT_EXPORT(EVDEV) StylusButtonEventConverterEvdev
: public EventConverterEvdev {
public:
StylusButtonEventConverterEvdev(base::ScopedFD fd,
base::FilePath path,
int id,
const EventDeviceInfo& devinfo,
DeviceEventDispatcherEvdev* dispatcher);
~StylusButtonEventConverterEvdev() override;
// EventConverterEvdev
void OnFileCanReadWithoutBlocking(int fd) override;
void ProcessEvent(const struct input_event& input);
private:
friend class MockStylusButtonEventConverterEvdev;
// Input device file descriptor.
const base::ScopedFD input_device_fd_;
// Callbacks for dispatching events.
DeviceEventDispatcherEvdev* const dispatcher_;
DISALLOW_COPY_AND_ASSIGN(StylusButtonEventConverterEvdev);
};
} // namespace ui
#endif // UI_EVENTS_OZONE_EVDEV_STYLUS_BUTTON_EVENT_CONVERTER_EVDEV_H_
// Copyright 2020 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/events/ozone/evdev/stylus_button_event_converter_evdev.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/memory/ptr_util.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event.h"
#include "ui/events/ozone/device/device_manager.h"
#include "ui/events/ozone/evdev/event_converter_test_util.h"
#include "ui/events/ozone/evdev/event_device_test_util.h"
#include "ui/events/ozone/evdev/event_factory_evdev.h"
#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/test/scoped_event_test_tick_clock.h"
namespace {
const char kTestDevicePath[] = "/dev/input/test-device";
} // namespace
namespace ui {
class MockStylusButtonEventConverterEvdev
: public StylusButtonEventConverterEvdev {
public:
MockStylusButtonEventConverterEvdev(base::ScopedFD fd,
base::FilePath path,
const EventDeviceInfo& devinfo,
DeviceEventDispatcherEvdev* dispatcher);
~MockStylusButtonEventConverterEvdev() override {}
void ConfigureReadMock(struct input_event* queue,
long read_this_many,
long queue_index);
// Actually dispatch the event reader code.
void ReadNow() {
OnFileCanReadWithoutBlocking(read_pipe_);
base::RunLoop().RunUntilIdle();
}
private:
int read_pipe_;
int write_pipe_;
std::vector<std::unique_ptr<Event>> dispatched_events_;
DISALLOW_COPY_AND_ASSIGN(MockStylusButtonEventConverterEvdev);
};
MockStylusButtonEventConverterEvdev::MockStylusButtonEventConverterEvdev(
base::ScopedFD fd,
base::FilePath path,
const EventDeviceInfo& devinfo,
DeviceEventDispatcherEvdev* dispatcher)
: StylusButtonEventConverterEvdev(std::move(fd),
path,
1,
devinfo,
dispatcher) {
int fds[2];
if (pipe(fds))
PLOG(FATAL) << "failed pipe";
EXPECT_TRUE(base::SetNonBlocking(fds[0]) || base::SetNonBlocking(fds[1]))
<< "failed to set non-blocking: " << strerror(errno);
read_pipe_ = fds[0];
write_pipe_ = fds[1];
}
void MockStylusButtonEventConverterEvdev::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 StylusButtonEventConverterEvdevTest : public testing::Test {
public:
StylusButtonEventConverterEvdevTest() {}
// Overridden from testing::Test:
void SetUp() override {
device_manager_ = ui::CreateDeviceManagerForTest();
keyboard_layout_engine_ = std::make_unique<ui::StubKeyboardLayoutEngine>();
event_factory_ = ui::CreateEventFactoryEvdevForTest(
nullptr, device_manager_.get(), keyboard_layout_engine_.get(),
base::BindRepeating(
&StylusButtonEventConverterEvdevTest::DispatchEventForTest,
base::Unretained(this)));
dispatcher_ =
ui::CreateDeviceEventDispatcherEvdevForTest(event_factory_.get());
}
ui::MockStylusButtonEventConverterEvdev* CreateDevice(
const ui::DeviceCapabilities& caps) {
int evdev_io[2];
if (pipe(evdev_io))
PLOG(FATAL) << "failed pipe";
base::ScopedFD events_in(evdev_io[0]);
events_out_.reset(evdev_io[1]);
ui::EventDeviceInfo devinfo;
CapabilitiesToDeviceInfo(caps, &devinfo);
return new ui::MockStylusButtonEventConverterEvdev(
std::move(events_in), base::FilePath(kTestDevicePath), devinfo,
dispatcher_.get());
}
unsigned size() { return dispatched_events_.size(); }
ui::KeyEvent* dispatched_event(unsigned index) {
DCHECK_GT(dispatched_events_.size(), index);
ui::Event* ev = dispatched_events_[index].get();
DCHECK(ev->IsKeyEvent());
return ev->AsKeyEvent();
}
void DispatchEventForTest(ui::Event* event) {
std::unique_ptr<ui::Event> cloned_event = ui::Event::Clone(*event);
dispatched_events_.push_back(std::move(cloned_event));
}
private:
std::unique_ptr<ui::DeviceManager> device_manager_;
std::unique_ptr<ui::KeyboardLayoutEngine> keyboard_layout_engine_;
std::unique_ptr<ui::EventFactoryEvdev> event_factory_;
std::unique_ptr<ui::DeviceEventDispatcherEvdev> dispatcher_;
std::vector<std::unique_ptr<ui::Event>> dispatched_events_;
base::ScopedFD events_out_;
DISALLOW_COPY_AND_ASSIGN(StylusButtonEventConverterEvdevTest);
};
TEST_F(StylusButtonEventConverterEvdevTest, DellActivePenSingleClick) {
std::unique_ptr<ui::MockStylusButtonEventConverterEvdev> dev =
base::WrapUnique(CreateDevice(ui::kDellActivePenButton));
struct input_event mock_kernel_queue[] = {
{{0, 0}, EV_MSC, MSC_SCAN, 0x700e3},
{{0, 0}, EV_KEY, KEY_LEFTMETA, 1},
{{0, 0}, EV_MSC, MSC_SCAN, 0x7006f},
{{0, 0}, EV_KEY, KEY_F20, 1},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_MSC, MSC_SCAN, 0x7006f},
{{0, 0}, EV_KEY, KEY_F20, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_MSC, MSC_SCAN, 0x700e3},
{{0, 0}, EV_KEY, KEY_LEFTMETA, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
};
for (unsigned i = 0; i < base::size(mock_kernel_queue); ++i) {
dev->ProcessEvent(mock_kernel_queue[i]);
}
EXPECT_EQ(0u, size());
}
TEST_F(StylusButtonEventConverterEvdevTest, DellActivePenDoubleClick) {
std::unique_ptr<ui::MockStylusButtonEventConverterEvdev> dev =
base::WrapUnique(CreateDevice(ui::kDellActivePenButton));
struct input_event mock_kernel_queue[] = {
{{0, 0}, EV_MSC, MSC_SCAN, 0x700e3},
{{0, 0}, EV_KEY, KEY_LEFTMETA, 1},
{{0, 0}, EV_MSC, MSC_SCAN, 0x7006e},
{{0, 0}, EV_KEY, KEY_F19, 1},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_MSC, MSC_SCAN, 0x7006e},
{{0, 0}, EV_KEY, KEY_F19, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_MSC, MSC_SCAN, 0x700e3},
{{0, 0}, EV_KEY, KEY_LEFTMETA, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
};
for (unsigned i = 0; i < base::size(mock_kernel_queue); ++i) {
dev->ProcessEvent(mock_kernel_queue[i]);
}
EXPECT_EQ(2u, size());
ui::KeyEvent* event = dispatched_event(0);
EXPECT_EQ(ui::ET_KEY_PRESSED, event->type());
EXPECT_TRUE(event->flags() & ui::EF_IS_STYLUS_BUTTON);
event = dispatched_event(1);
EXPECT_EQ(ui::ET_KEY_RELEASED, event->type());
EXPECT_TRUE(event->flags() & ui::EF_IS_STYLUS_BUTTON);
}
TEST_F(StylusButtonEventConverterEvdevTest, DellActivePenLongPress) {
std::unique_ptr<ui::MockStylusButtonEventConverterEvdev> dev =
base::WrapUnique(CreateDevice(ui::kDellActivePenButton));
struct input_event mock_kernel_queue[] = {
{{0, 0}, EV_MSC, MSC_SCAN, 0x700e3},
{{0, 0}, EV_KEY, KEY_LEFTMETA, 1},
{{0, 0}, EV_MSC, MSC_SCAN, 0x7006d},
{{0, 0}, EV_KEY, KEY_F18, 1},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_MSC, MSC_SCAN, 0x7006d},
{{0, 0}, EV_KEY, KEY_F18, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
{{0, 0}, EV_MSC, MSC_SCAN, 0x700e3},
{{0, 0}, EV_KEY, KEY_LEFTMETA, 0},
{{0, 0}, EV_SYN, SYN_REPORT, 0},
};
for (unsigned i = 0; i < base::size(mock_kernel_queue); ++i) {
dev->ProcessEvent(mock_kernel_queue[i]);
}
EXPECT_EQ(0u, size());
}
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