Commit 0cf5d675 authored by denniskempin's avatar denniskempin Committed by Commit bot

Add palm suppression feature to EventConverterEvdev

The palm suppression is enabled by an internal stylus enabled
TouchEventConverterEvdev via a callback to InputDeviceFactoryEvdev.
When in palm suppression mode, all internal touchscreen devices will
be disabled.
Before a touchscreen would lift all touches when disabled, and
re-initialize when re-enabled. To prevent the unnecessary ioctls the
code has been updated to force cancel all touches while disabled, but
continue to read and sync the internal touch state. This will also
make sure that touches that went down while disabled, will not
suddenly show up after enabling and continue to stay cancelled until
lifted.

BUG=636458

Review-Url: https://codereview.chromium.org/2263693003
Cr-Commit-Position: refs/heads/master@{#414860}
parent 6afaa104
......@@ -96,6 +96,10 @@ bool EventConverterEvdev::HasTouchscreen() const {
return false;
}
bool EventConverterEvdev::HasPen() const {
return false;
}
bool EventConverterEvdev::HasCapsLockLed() const {
return false;
}
......@@ -145,6 +149,9 @@ void EventConverterEvdev::SetCapsLockLed(bool enabled) {
void EventConverterEvdev::SetTouchEventLoggingEnabled(bool enabled) {
}
void EventConverterEvdev::SetPalmSuppressionCallback(
const base::Callback<void(bool)>& callback) {}
base::TimeTicks EventConverterEvdev::TimeTicksFromInputEvent(
const input_event& event) {
return ui::EventTimeStampFromSeconds(event.time.tv_sec) +
......
......@@ -77,6 +77,9 @@ class EVENTS_OZONE_EVDEV_EXPORT EventConverterEvdev
// Returns true if the converter is used for a touchscreen device.
virtual bool HasTouchscreen() const;
// Returns true if the converter is used for a pen device.
virtual bool HasPen() const;
// Returns true if the converter is used for a device with a caps lock LED.
virtual bool HasCapsLockLed() const;
......@@ -99,6 +102,10 @@ class EVENTS_OZONE_EVDEV_EXPORT EventConverterEvdev
// Update touch event logging state.
virtual void SetTouchEventLoggingEnabled(bool enabled);
// Sets callback to enable/disable palm suppression.
virtual void SetPalmSuppressionCallback(
const base::Callback<void(bool)>& callback);
// Helper to generate a base::TimeTicks from an input_event's time
static base::TimeTicks TimeTicksFromInputEvent(const input_event& event);
......
......@@ -239,6 +239,13 @@ void InputDeviceFactoryEvdev::AttachInputDevice(
if (converters_[path])
DetachInputDevice(path);
if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
converter->HasPen()) {
converter->SetPalmSuppressionCallback(
base::Bind(&InputDeviceFactoryEvdev::EnablePalmSuppression,
base::Unretained(this)));
}
// Add initialized device to map.
converters_[path] = converter.release();
converters_[path]->Start();
......@@ -376,6 +383,11 @@ bool InputDeviceFactoryEvdev::IsDeviceEnabled(
converter->HasTouchscreen())
return false;
if (palm_suppression_enabled_ &&
converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL &&
converter->HasTouchscreen() && !converter->HasPen())
return false;
return input_device_settings_.enable_devices;
}
......@@ -491,4 +503,14 @@ void InputDeviceFactoryEvdev::SetBoolPropertyForOneType(
#endif
}
void InputDeviceFactoryEvdev::EnablePalmSuppression(bool enabled) {
if (enabled == palm_suppression_enabled_)
return;
palm_suppression_enabled_ = enabled;
for (const auto& it : converters_) {
it.second->SetEnabled(IsDeviceEnabled(it.second));
}
}
} // namespace ui
......@@ -68,6 +68,8 @@ class EVENTS_OZONE_EVDEV_EXPORT InputDeviceFactoryEvdev {
base::WeakPtr<InputDeviceFactoryEvdev> GetWeakPtr();
void EnablePalmSuppression(bool enabled);
private:
// Open device at path & starting processing events (on UI thread).
void AttachInputDevice(std::unique_ptr<EventConverterEvdev> converter);
......@@ -130,6 +132,9 @@ class EVENTS_OZONE_EVDEV_EXPORT InputDeviceFactoryEvdev {
// LEDs.
bool caps_lock_led_enabled_ = false;
// Whether touch palm suppression is enabled.
bool palm_suppression_enabled_ = false;
// Device settings. These primarily affect libgestures behavior.
InputDeviceSettingsEvdev input_device_settings_;
......
......@@ -27,6 +27,9 @@ struct EVENTS_OZONE_EVDEV_EXPORT InProgressTouchEvdev {
// Whether the touch was cancelled. Touch events should be ignored till a
// new touch is initiated.
bool was_cancelled = false;
// Whether the touch is going to be canceled.
bool cancelled = false;
bool was_touching = false;
......
......@@ -129,6 +129,7 @@ TouchEventConverterEvdev::~TouchEventConverterEvdev() {
void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
has_mt_ = info.HasMultitouch();
has_pen_ = info.HasKeyEvent(BTN_TOOL_PEN);
if (has_mt_) {
pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE);
......@@ -139,6 +140,7 @@ void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1;
touch_points_ =
std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, kNumTouchEvdevSlots);
major_max_ = info.GetAbsMaximum(ABS_MT_TOUCH_MAJOR);
current_slot_ = info.GetAbsValue(ABS_MT_SLOT);
} else {
pressure_min_ = info.GetAbsMinimum(ABS_PRESSURE);
......@@ -148,6 +150,7 @@ void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
y_min_tuxels_ = info.GetAbsMinimum(ABS_Y);
y_num_tuxels_ = info.GetAbsMaximum(ABS_Y) - y_min_tuxels_ + 1;
touch_points_ = 1;
major_max_ = 0;
current_slot_ = 0;
}
......@@ -188,12 +191,14 @@ void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
events_[i].altered = true;
// Optional bits.
events_[i].radius_x =
info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR, i, 0) / 2.0f;
int touch_major =
info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR, i, 0);
events_[i].radius_x = touch_major / 2.0f;
events_[i].radius_y =
info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MINOR, i, 0) / 2.0f;
events_[i].pressure = ScalePressure(
info.GetAbsMtSlotValueWithDefault(ABS_MT_PRESSURE, i, 0));
events_[i].cancelled = (major_max_ > 0 && touch_major == major_max_);
}
} else {
// TODO(spang): Add key state to EventDeviceInfo to allow initial contact.
......@@ -207,6 +212,7 @@ void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
events_[0].radius_y = 0;
events_[0].pressure = 0;
events_[0].tool_code = 0;
events_[0].cancelled = false;
}
}
......@@ -227,6 +233,10 @@ bool TouchEventConverterEvdev::HasTouchscreen() const {
return true;
}
bool TouchEventConverterEvdev::HasPen() const {
return has_pen_;
}
gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
return gfx::Size(x_num_tuxels_, y_num_tuxels_);
}
......@@ -236,12 +246,14 @@ int TouchEventConverterEvdev::GetTouchPoints() const {
}
void TouchEventConverterEvdev::OnEnabled() {
ReportEvents(EventTimeForNow());
}
void TouchEventConverterEvdev::OnDisabled() {
ReleaseTouches();
ReleaseButtons();
if (enable_palm_suppression_callback_) {
enable_palm_suppression_callback_.Run(false);
}
}
void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
......@@ -260,11 +272,6 @@ void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
return;
}
if (!enabled_) {
dropped_events_ = true;
return;
}
for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
if (!has_mt_) {
// Emulate the device as an MT device with only 1 slot by inserting extra
......@@ -284,6 +291,11 @@ void TouchEventConverterEvdev::SetTouchEventLoggingEnabled(bool enabled) {
touch_logging_enabled_ = enabled;
}
void TouchEventConverterEvdev::SetPalmSuppressionCallback(
const base::Callback<void(bool)>& callback) {
enable_palm_suppression_callback_ = callback;
}
void TouchEventConverterEvdev::ProcessMultitouchEvent(
const input_event& input) {
if (touch_logging_enabled_)
......@@ -357,6 +369,7 @@ void TouchEventConverterEvdev::ProcessKey(const input_event& input) {
} else {
events_[current_slot_].tool_code = 0;
}
events_[current_slot_].altered = true;
break;
default:
NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
......@@ -370,6 +383,11 @@ void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
// we can scale the ellipse correctly. However on the Pixel we get
// neither minor nor orientation, so this is all we can do.
events_[current_slot_].radius_x = input.value / 2.0f;
// The MT protocol cannot communicate cancelled touches, so some kernel
// drivers will identify palms by setting touch major to max.
if (major_max_ > 0 && input.value == major_max_)
events_[current_slot_].cancelled = true;
break;
case ABS_MT_TOUCH_MINOR:
events_[current_slot_].radius_y = input.value / 2.0f;
......@@ -419,14 +437,11 @@ void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
EventType TouchEventConverterEvdev::GetEventTypeForTouch(
const InProgressTouchEvdev& touch) {
if (touch.cancelled)
if (touch.was_cancelled)
return ET_UNKNOWN;
if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(touch.slot)) {
if (touch.touching && !touch.was_touching)
return ET_UNKNOWN;
return ET_TOUCH_CANCELLED;
}
if (touch.cancelled)
return touch.was_touching ? ET_TOUCH_CANCELLED : ET_UNKNOWN;
if (touch.touching)
return touch.was_touching ? ET_TOUCH_MOVED : ET_TOUCH_PRESSED;
......@@ -480,17 +495,21 @@ void TouchEventConverterEvdev::ReportEvents(base::TimeTicks timestamp) {
if (!event->altered)
continue;
if (enable_palm_suppression_callback_)
enable_palm_suppression_callback_.Run(event->tool_code > 0);
if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(event->slot))
event->cancelled = true;
if (event->tool_code > 0) {
ReportStylusEvent(*event, timestamp);
} else {
EventType event_type = GetEventTypeForTouch(*event);
if (event_type == ET_UNKNOWN || event_type == ET_TOUCH_CANCELLED)
event->cancelled = true;
if (event_type != ET_UNKNOWN)
ReportTouchEvent(*event, event_type, timestamp);
}
event->was_cancelled = event->cancelled;
event->was_touching = event->touching;
event->altered = false;
event->btn_left.changed = false;
......@@ -509,13 +528,17 @@ void TouchEventConverterEvdev::UpdateTrackingId(int slot, int tracking_id) {
event->touching = (tracking_id >= 0);
event->altered = true;
if (tracking_id >= 0)
event->cancelled = false;
if (tracking_id >= 0) {
event->was_cancelled = false;
event->cancelled = !enabled_;
}
}
void TouchEventConverterEvdev::ReleaseTouches() {
for (size_t slot = 0; slot < events_.size(); slot++)
UpdateTrackingId(slot, kTrackingIdForUnusedSlot);
for (size_t slot = 0; slot < events_.size(); slot++) {
events_[slot].cancelled = true;
events_[slot].altered = true;
}
ReportEvents(EventTimeForNow());
}
......
......@@ -45,6 +45,7 @@ class EVENTS_OZONE_EVDEV_EXPORT TouchEventConverterEvdev
// EventConverterEvdev:
bool HasTouchscreen() const override;
bool HasPen() const override;
gfx::Size GetTouchscreenSize() const override;
int GetTouchPoints() const override;
void OnEnabled() override;
......@@ -55,6 +56,10 @@ class EVENTS_OZONE_EVDEV_EXPORT TouchEventConverterEvdev
// Update touch event logging state
void SetTouchEventLoggingEnabled(bool enabled) override;
// Sets callback to enable/disable palm suppression.
void SetPalmSuppressionCallback(
const base::Callback<void(bool)>& callback) override;
// Unsafe part of initialization.
virtual void Initialize(const EventDeviceInfo& info);
......@@ -104,6 +109,9 @@ class EVENTS_OZONE_EVDEV_EXPORT TouchEventConverterEvdev
// Device has multitouch capability.
bool has_mt_ = false;
// Device supports pen input.
bool has_pen_ = false;
// Use BTN_LEFT instead of BT_TOUCH.
bool quirk_left_mouse_button_ = false;
......@@ -122,6 +130,9 @@ class EVENTS_OZONE_EVDEV_EXPORT TouchEventConverterEvdev
// Number of touch points reported by driver
int touch_points_ = 0;
// Maximum value of touch major axis
int major_max_ = 0;
// Tracking id counter.
int next_tracking_id_ = 0;
......@@ -140,6 +151,9 @@ class EVENTS_OZONE_EVDEV_EXPORT TouchEventConverterEvdev
// Records the recent touch events. It is used to fill the feedback reports
TouchEventLogEvdev touch_evdev_debug_buffer_;
// Callback to enable/disable palm suppression.
base::Callback<void(bool)> enable_palm_suppression_callback_;
DISALLOW_COPY_AND_ASSIGN(TouchEventConverterEvdev);
};
......
......@@ -609,7 +609,7 @@ TEST_F(TouchEventConverterEvdevTest, ShouldReleaseContactsOnStop) {
EXPECT_EQ(2u, size());
ui::TouchEventParams ev2 = dispatched_touch_event(1);
EXPECT_EQ(ET_TOUCH_RELEASED, ev2.type);
EXPECT_EQ(ET_TOUCH_CANCELLED, ev2.type);
EXPECT_EQ(0, ev2.slot);
}
......@@ -654,7 +654,7 @@ TEST_F(TouchEventConverterEvdevTest, ShouldRemoveContactsWhenDisabled) {
EXPECT_EQ(2u, size());
ui::TouchEventParams ev2 = dispatched_touch_event(1);
EXPECT_EQ(ET_TOUCH_RELEASED, ev2.type);
EXPECT_EQ(ET_TOUCH_CANCELLED, ev2.type);
EXPECT_EQ(0, ev2.slot);
// Set up the previous contact in slot 0.
......@@ -664,16 +664,16 @@ TEST_F(TouchEventConverterEvdevTest, ShouldRemoveContactsWhenDisabled) {
devinfo.SetAbsMtSlot(ABS_MT_POSITION_Y, 0, 749);
devinfo.SetAbsMtSlot(ABS_MT_PRESSURE, 0, 50);
// Re-enable the device (should re-apply the contact).
// Re-enable the device (touch is cancelled, should not come back)
dev->SimulateReinitialize(devinfo);
dev->SetEnabled(true);
EXPECT_EQ(3u, size());
EXPECT_EQ(2u, size());
ui::TouchEventParams ev3 = dispatched_touch_event(2);
EXPECT_EQ(ET_TOUCH_PRESSED, ev3.type);
EXPECT_EQ(0, ev3.slot);
EXPECT_EQ(1003, ev3.location.x());
EXPECT_EQ(749, ev3.location.y());
// Send updates to touch (touch is cancelled, should not come back)
dev->ConfigureReadMock(mock_kernel_queue_press,
arraysize(mock_kernel_queue_press), 0);
dev->ReadNow();
EXPECT_EQ(2u, size());
}
// crbug.com/477695
......
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