Commit 15026124 authored by Merissa Tan's avatar Merissa Tan Committed by Commit Bot

Add gamepad rumble support in Ozone.

- Add method to query device rumble capabilities in EventDeviceInfo.
- Add methods to support vibration (PlayVibrationEffect, StopVibration,
StoreRumbleEffect, StartOrStopEffect, etc.) in GamepadEventConverterEvdev.
- Route methods to control gamepad vibration through InputController to
GamepadEventConverterEvdev.

BUG=b:158685462
TEST=Add tests to GamepadEventConverterEvdevTest; all tests pass.

Change-Id: Ic1a2d1d1965ddd0a9321a93224a59bc9d1b681fd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2294560
Commit-Queue: Merissa Tan <liyingtan@google.com>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarMitsuru Oshima (OOO) <oshima@chromium.org>
Reviewed-by: default avatarMichael Spang <spang@chromium.org>
Auto-Submit: Merissa Tan <liyingtan@google.com>
Cr-Commit-Position: refs/heads/master@{#797041}
parent 78aedc57
......@@ -66,6 +66,10 @@ class TestInputController : public ui::InputController {
GetTouchEventLogReply reply) override {
std::move(reply).Run(std::vector<base::FilePath>());
}
void PlayVibrationEffect(int id,
uint8_t amplitude,
uint16_t duration_millis) override {}
void StopVibration(int id) override {}
void SetInternalTouchpadEnabled(bool enabled) override {}
bool IsInternalTouchpadEnabled() const override { return false; }
void SetTouchscreensEnabled(bool enabled) override {}
......
......@@ -63,7 +63,8 @@ class GamingSeatTest : public test::ExoTestBase {
for (auto& id : gamepad_device_ids) {
gamepad_devices.emplace_back(
ui::InputDevice(id, ui::InputDeviceType::INPUT_DEVICE_USB, "gamepad"),
std::vector<ui::GamepadDevice::Axis>());
std::vector<ui::GamepadDevice::Axis>(),
/*supports_vibration_rumble=*/false);
}
ui::GamepadProviderOzone::GetInstance()->DispatchGamepadDevicesUpdated(
gamepad_devices);
......
......@@ -7,8 +7,11 @@
namespace ui {
GamepadDevice::GamepadDevice(const InputDevice& input_device,
std::vector<GamepadDevice::Axis>&& axes)
: InputDevice(input_device), axes(std::move(axes)) {}
std::vector<GamepadDevice::Axis>&& axes,
bool supports_rumble)
: InputDevice(input_device),
axes(std::move(axes)),
supports_vibration_rumble(supports_rumble) {}
GamepadDevice::GamepadDevice(const GamepadDevice& other) = default;
......
......@@ -27,12 +27,17 @@ struct EVENTS_DEVICES_EXPORT GamepadDevice : public InputDevice {
int32_t resolution = 0;
};
GamepadDevice(const InputDevice& input_device, std::vector<Axis>&& axes);
GamepadDevice(const InputDevice& input_device,
std::vector<Axis>&& axes,
bool supports_rumble);
GamepadDevice(const GamepadDevice& other);
~GamepadDevice() override;
// Axes the gamepad has e.g. analog thumb sticks.
std::vector<Axis> axes;
// Whether the gamepad device supports rumble type force feedback.
bool supports_vibration_rumble = false;
};
} // namespace ui
......
......@@ -135,6 +135,20 @@ std::vector<ui::GamepadDevice::Axis> EventConverterEvdev::GetGamepadAxes()
return std::vector<ui::GamepadDevice::Axis>();
}
bool EventConverterEvdev::GetGamepadRumbleCapability() const {
NOTREACHED();
return false;
}
void EventConverterEvdev::PlayVibrationEffect(uint8_t amplitude,
uint16_t duration_millis) {
NOTREACHED();
}
void EventConverterEvdev::StopVibration() {
NOTREACHED();
}
int EventConverterEvdev::GetTouchPoints() const {
NOTREACHED();
return 0;
......
......@@ -102,7 +102,10 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev
// Returns information for all axes if the converter is used for a gamepad
// device.
virtual std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const;
virtual std::vector<GamepadDevice::Axis> GetGamepadAxes() const;
// Returns whether the gamepad device supports rumble type force feedback.
virtual bool GetGamepadRumbleCapability() const;
// Sets which keyboard keys should be processed. If |enable_filter| is
// false, all keys are allowed and |allowed_keys| is ignored.
......@@ -122,6 +125,10 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev
// Helper to generate a base::TimeTicks from an input_event's time
static base::TimeTicks TimeTicksFromInputEvent(const input_event& event);
// Handle gamepad force feedback effects.
virtual void PlayVibrationEffect(uint8_t amplitude, uint16_t duration_millis);
virtual void StopVibration();
protected:
// base::MessagePumpForUI::FdWatcher:
void OnFileCanWriteWithoutBlocking(int fd) override;
......
......@@ -177,6 +177,7 @@ EventDeviceInfo::EventDeviceInfo() {
memset(msc_bits_, 0, sizeof(msc_bits_));
memset(sw_bits_, 0, sizeof(sw_bits_));
memset(led_bits_, 0, sizeof(led_bits_));
memset(ff_bits_, 0, sizeof(ff_bits_));
memset(prop_bits_, 0, sizeof(prop_bits_));
memset(abs_info_, 0, sizeof(abs_info_));
}
......@@ -205,6 +206,9 @@ bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) {
if (!GetEventBits(fd, path, EV_LED, led_bits_, sizeof(led_bits_)))
return false;
if (!GetEventBits(fd, path, EV_FF, ff_bits_, sizeof(ff_bits_)))
return false;
if (!GetPropBits(fd, path, prop_bits_, sizeof(prop_bits_)))
return false;
......@@ -274,6 +278,10 @@ void EventDeviceInfo::SetLedEvents(const unsigned long* led_bits, size_t len) {
AssignBitset(led_bits, len, led_bits_, base::size(led_bits_));
}
void EventDeviceInfo::SetFfEvents(const unsigned long* ff_bits, size_t len) {
AssignBitset(ff_bits, len, ff_bits_, base::size(ff_bits_));
}
void EventDeviceInfo::SetProps(const unsigned long* prop_bits, size_t len) {
AssignBitset(prop_bits, len, prop_bits_, base::size(prop_bits_));
}
......@@ -357,6 +365,12 @@ bool EventDeviceInfo::HasLedEvent(unsigned int code) const {
return EvdevBitIsSet(led_bits_, code);
}
bool EventDeviceInfo::HasFfEvent(unsigned int code) const {
if (code > FF_MAX)
return false;
return EvdevBitIsSet(ff_bits_, code);
}
bool EventDeviceInfo::HasProp(unsigned int code) const {
if (code > INPUT_PROP_MAX)
return false;
......@@ -536,6 +550,10 @@ bool EventDeviceInfo::HasGamepad() const {
return support_gamepad_btn && !HasTablet() && !HasKeyboard();
}
bool EventDeviceInfo::SupportsRumble() const {
return HasEventType(EV_FF) && HasFfEvent(FF_RUMBLE);
}
// static
ui::InputDeviceType EventDeviceInfo::GetInputDeviceTypeFromId(input_id id) {
static constexpr struct {
......
......@@ -64,6 +64,7 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
void SetMscEvents(const unsigned long* msc_bits, size_t len);
void SetSwEvents(const unsigned long* sw_bits, size_t len);
void SetLedEvents(const unsigned long* led_bits, size_t len);
void SetFfEvents(const unsigned long* ff_bits, size_t len);
void SetProps(const unsigned long* prop_bits, size_t len);
void SetAbsInfo(unsigned int code, const input_absinfo& absinfo);
void SetAbsMtSlots(unsigned int code, const std::vector<int32_t>& values);
......@@ -80,6 +81,7 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
bool HasMscEvent(unsigned int code) const;
bool HasSwEvent(unsigned int code) const;
bool HasLedEvent(unsigned int code) const;
bool HasFfEvent(unsigned int code) const;
// Properties of absolute axes.
int32_t GetAbsMinimum(unsigned int code) const;
......@@ -148,6 +150,9 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
// Determine whether there's a gamepad on this device.
bool HasGamepad() const;
// Determine whether the device supports rumble.
bool SupportsRumble() const;
// Determine if this is a dedicated device for a stylus button.
bool IsStylusButtonDevice() const;
......@@ -177,6 +182,7 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
unsigned long sw_bits_[EVDEV_BITS_TO_LONGS(SW_CNT)];
unsigned long led_bits_[EVDEV_BITS_TO_LONGS(LED_CNT)];
unsigned long prop_bits_[EVDEV_BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long ff_bits_[EVDEV_BITS_TO_LONGS(FF_CNT)];
struct input_absinfo abs_info_[ABS_CNT];
......
......@@ -1090,6 +1090,11 @@ bool CapabilitiesToDeviceInfo(const DeviceCapabilities& capabilities,
return false;
devinfo->SetLedEvents(&led_bits[0], led_bits.size());
std::vector<unsigned long> ff_bits;
if (!ParseBitfield(capabilities.ff, FF_CNT, &ff_bits))
return false;
devinfo->SetFfEvents(&ff_bits[0], ff_bits.size());
std::vector<unsigned long> prop_bits;
if (!ParseBitfield(capabilities.prop, INPUT_PROP_CNT, &prop_bits))
return false;
......
......@@ -7,14 +7,21 @@
#include <errno.h>
#include <linux/input.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/trace_event/trace_event.h"
#include "ui/events/event_utils.h"
#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
#include "ui/events/ozone/gamepad/gamepad_event.h"
#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
namespace {
constexpr int kInvalidEffectId = -1;
}
namespace ui {
GamepadEventConverterEvdev::GamepadEventConverterEvdev(
......@@ -34,7 +41,8 @@ GamepadEventConverterEvdev::GamepadEventConverterEvdev(
devinfo.version()),
will_send_frame_(false),
input_device_fd_(std::move(fd)),
dispatcher_(dispatcher) {
dispatcher_(dispatcher),
effect_id_(-1) {
input_absinfo abs_info;
for (int code = 0; code < ABS_CNT; ++code) {
abs_info = devinfo.GetAbsInfoByCode(code);
......@@ -49,6 +57,7 @@ GamepadEventConverterEvdev::GamepadEventConverterEvdev(
axes_.push_back(axis);
}
}
supports_rumble_ = devinfo.SupportsRumble();
}
GamepadEventConverterEvdev::~GamepadEventConverterEvdev() {
......@@ -59,6 +68,102 @@ bool GamepadEventConverterEvdev::HasGamepad() const {
return true;
}
void GamepadEventConverterEvdev::PlayVibrationEffect(uint8_t amplitude,
uint16_t duration_millis) {
constexpr uint16_t kRumbleMagnitudeMax = 0xFFFF;
constexpr uint8_t kAmplitudeMax = 0xFF;
// only rumble type force feedback is supported at the moment
if (!supports_rumble_) {
LOG(ERROR) << "Device doesn't support rumble, but SetVibration is called.";
return;
}
float amplitude_ratio = static_cast<float>(amplitude) / kAmplitudeMax;
uint16_t magnitude_scaled =
static_cast<uint16_t>(amplitude_ratio * kRumbleMagnitudeMax);
effect_id_ = StoreRumbleEffect(input_device_fd_, effect_id_, duration_millis,
0, magnitude_scaled, magnitude_scaled);
if (effect_id_ == kInvalidEffectId) {
LOG(ERROR) << "SetVibration is called with an invalid effect ID.";
return;
}
StartOrStopEffect(input_device_fd_, effect_id_, true);
}
void GamepadEventConverterEvdev::StopVibration() {
if (!supports_rumble_) {
LOG(ERROR)
<< "Device doesn't support rumble, but SetZeroVibration is called.";
return;
}
if (effect_id_ == kInvalidEffectId) {
LOG(ERROR) << "SetZeroVibration is called with an invalid effect ID.";
return;
}
StartOrStopEffect(input_device_fd_, effect_id_, false);
}
int GamepadEventConverterEvdev::StoreRumbleEffect(const base::ScopedFD& fd,
int effect_id,
uint16_t duration,
uint16_t start_delay,
uint16_t strong_magnitude,
uint16_t weak_magnitude) {
struct ff_effect effect;
memset(&effect, 0, sizeof(effect));
effect.type = FF_RUMBLE;
effect.id = effect_id;
effect.replay.length = duration;
effect.replay.delay = start_delay;
effect.u.rumble.strong_magnitude = strong_magnitude;
effect.u.rumble.weak_magnitude = weak_magnitude;
return UploadFfEffect(fd, &effect);
}
void GamepadEventConverterEvdev::StartOrStopEffect(const base::ScopedFD& fd,
int effect_id,
bool do_start) {
struct input_event start_stop;
memset(&start_stop, 0, sizeof(start_stop));
start_stop.type = EV_FF;
start_stop.code = effect_id;
start_stop.value = do_start ? 1 : 0;
ssize_t nbytes = WriteEvent(fd, start_stop);
if (nbytes != sizeof(start_stop)) {
PLOG(ERROR) << "Error writing the event in StartOrStopEffect";
}
}
void GamepadEventConverterEvdev::DestroyEffect(const base::ScopedFD& fd,
int effect_id) {
if (HANDLE_EINTR(ioctl(fd.get(), EVIOCRMFF, effect_id)) < 0) {
PLOG(ERROR) << "Error destroying rumble effect.";
}
effect_id_ = kInvalidEffectId;
}
ssize_t GamepadEventConverterEvdev::WriteEvent(
const base::ScopedFD& fd,
const struct input_event& event) {
return HANDLE_EINTR(
write(fd.get(), static_cast<const void*>(&event), sizeof(event)));
}
int GamepadEventConverterEvdev::UploadFfEffect(const base::ScopedFD& fd,
struct ff_effect* effect) {
if (HANDLE_EINTR(ioctl(fd.get(), EVIOCSFF, effect)) < 0) {
PLOG(ERROR) << "Error storing rumble effect.";
return kInvalidEffectId;
}
return effect->id;
}
void GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
TRACE_EVENT1("evdev",
"GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
......@@ -90,6 +195,10 @@ GamepadEventConverterEvdev::GetGamepadAxes() const {
return axes_;
}
bool GamepadEventConverterEvdev::GetGamepadRumbleCapability() const {
return supports_rumble_;
}
void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) {
base::TimeTicks timestamp = TimeTicksFromInputEvent(evdev_ev);
// We may have missed Gamepad releases. Reset everything.
......@@ -134,6 +243,9 @@ void GamepadEventConverterEvdev::ProcessEvdevAbs(
void GamepadEventConverterEvdev::ResetGamepad() {
base::TimeTicks timestamp = ui::EventTimeForNow();
if (effect_id_ != kInvalidEffectId) {
DestroyEffect(input_device_fd_, effect_id_);
}
// Reset all the buttons.
for (unsigned int code : pressed_buttons_)
ProcessEvdevKey(code, 0, timestamp);
......
......@@ -39,10 +39,18 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev
bool HasGamepad() const override;
void OnDisabled() override;
std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const override;
bool GetGamepadRumbleCapability() const override;
// This function processes one input_event from evdev.
void ProcessEvent(const struct input_event& input);
// This function sends a vibration effect to the gamepaddevice.
void PlayVibrationEffect(uint8_t amplitude,
uint16_t duration_millis) override;
// This function stops the gamepad device's vibration effect.
void StopVibration() override;
private:
// This function processes EV_KEY event from gamepad device.
void ProcessEvdevKey(uint16_t code,
......@@ -62,11 +70,43 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev
void OnSync(const base::TimeTicks& timestamp);
// This function uploads the rumble force feedback effect to the gamepad
// device and returns the new effect id. If we already created an effect on
// this device, then the existing id is reused and returned.
int StoreRumbleEffect(const base::ScopedFD& fd,
int effect_id,
uint16_t duration,
uint16_t start_delay,
uint16_t strong_magnitude,
uint16_t weak_magnitude);
// This function controls the playback of the effect on the gamepad device.
void StartOrStopEffect(const base::ScopedFD& fd,
int effect_id,
bool do_start);
// This function removes the effect from the gamepad device.
void DestroyEffect(const base::ScopedFD& fd, int effect_id);
// This function writes the input_event into the kernel and returns the result
// of the write.
virtual ssize_t WriteEvent(const base::ScopedFD& fd,
const struct input_event& input);
// This function uploads the ff_effect to the gamepad device and returns the
// unique id assigned by the driver.
virtual int UploadFfEffect(const base::ScopedFD& fd,
struct ff_effect* effect);
// Sometimes, we want to drop abs values, when we do so, we no longer want to
// send gamepad frame event when we see next sync. This flag is set to false
// when each frame is sent. It is set to true when Btn or Abs event is sent.
bool will_send_frame_;
// This flag is set to true if the gamepad supports force feedback of type
// FF_RUMBLE.
bool supports_rumble_;
std::vector<ui::GamepadDevice::Axis> axes_;
// Evdev scancodes of pressed buttons.
......@@ -78,6 +118,10 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev
// Callbacks for dispatching events.
DeviceEventDispatcherEvdev* const dispatcher_;
// The effect id is needed to keep track of effects that are uploaded and
// stored in the gamepad device.
int effect_id_;
DISALLOW_COPY_AND_ASSIGN(GamepadEventConverterEvdev);
};
......
......@@ -60,6 +60,33 @@ class TestGamepadObserver : public ui::GamepadObserver {
} // namespace
namespace ui {
class TestGamepadEventConverterEvdev : public ui::GamepadEventConverterEvdev {
public:
TestGamepadEventConverterEvdev(base::ScopedFD fd,
base::FilePath path,
int id,
const EventDeviceInfo& info,
DeviceEventDispatcherEvdev* dispatcher)
: GamepadEventConverterEvdev(std::move(fd), path, id, info, dispatcher) {}
int UploadFfEffect(const base::ScopedFD& fd,
struct ff_effect* effect) override {
uploaded_ff_effects_.push_back(*effect);
return kEffectId;
}
ssize_t WriteEvent(const base::ScopedFD& fd,
const struct input_event& event) override {
written_input_events_.push_back(event);
return 0;
}
int kEffectId = 0;
std::vector<ff_effect> uploaded_ff_effects_;
std::vector<input_event> written_input_events_;
};
class GamepadEventConverterEvdevTest : public testing::Test {
public:
GamepadEventConverterEvdevTest() {}
......@@ -77,7 +104,7 @@ class GamepadEventConverterEvdevTest : public testing::Test {
ui::CreateDeviceEventDispatcherEvdevForTest(event_factory_.get());
}
std::unique_ptr<ui::GamepadEventConverterEvdev> CreateDevice(
std::unique_ptr<ui::TestGamepadEventConverterEvdev> CreateDevice(
const ui::DeviceCapabilities& caps) {
int evdev_io[2];
if (pipe(evdev_io))
......@@ -87,7 +114,7 @@ class GamepadEventConverterEvdevTest : public testing::Test {
ui::EventDeviceInfo devinfo;
CapabilitiesToDeviceInfo(caps, &devinfo);
return std::make_unique<ui::GamepadEventConverterEvdev>(
return std::make_unique<ui::TestGamepadEventConverterEvdev>(
std::move(events_in), base::FilePath(kTestDevicePath), 1, devinfo,
dispatcher_.get());
}
......@@ -95,7 +122,7 @@ class GamepadEventConverterEvdevTest : public testing::Test {
private:
void DispatchEventForTest(ui::Event* event) {}
std::unique_ptr<ui::GamepadEventConverterEvdev> gamepad_evdev_;
std::unique_ptr<ui::TestGamepadEventConverterEvdev> gamepad_evdev_;
std::unique_ptr<ui::DeviceManager> device_manager_;
std::unique_ptr<ui::KeyboardLayoutEngine> keyboard_layout_engine_;
std::unique_ptr<ui::EventFactoryEvdev> event_factory_;
......@@ -110,9 +137,20 @@ struct ExpectedEvent {
double value;
};
struct ExpectedVibrationEvent {
uint16_t code;
double value;
};
struct ExpectedVibrationEffect {
uint16_t duration;
double strong_magnitude;
double weak_magnitude;
};
TEST_F(GamepadEventConverterEvdevTest, XboxGamepadEvents) {
TestGamepadObserver observer;
std::unique_ptr<ui::GamepadEventConverterEvdev> dev =
std::unique_ptr<ui::TestGamepadEventConverterEvdev> dev =
CreateDevice(kXboxGamepad);
struct input_event mock_kernel_queue[] = {
......@@ -180,4 +218,34 @@ TEST_F(GamepadEventConverterEvdevTest, XboxGamepadEvents) {
}
}
TEST_F(GamepadEventConverterEvdevTest, XboxGamepadVibrationEvents) {
TestGamepadObserver observer;
std::unique_ptr<ui::TestGamepadEventConverterEvdev> dev =
CreateDevice(kXboxGamepad);
struct ExpectedVibrationEvent expected_events[] = {{dev->kEffectId, 1},
{dev->kEffectId, 0}};
struct ExpectedVibrationEffect expected_effect = {10000, 0x8080, 0x8080};
dev->PlayVibrationEffect(0x80, 10000);
EXPECT_EQ(1UL, dev->written_input_events_.size());
input_event received_vibration_event = dev->written_input_events_[0];
EXPECT_EQ(expected_events[0].code, received_vibration_event.code);
EXPECT_EQ(expected_events[0].value, received_vibration_event.value);
ff_effect received_effect = dev->uploaded_ff_effects_[0];
EXPECT_EQ(expected_effect.duration, received_effect.replay.length);
EXPECT_EQ(expected_effect.strong_magnitude,
received_effect.u.rumble.strong_magnitude);
EXPECT_EQ(expected_effect.weak_magnitude,
received_effect.u.rumble.weak_magnitude);
dev->StopVibration();
EXPECT_EQ(2UL, dev->written_input_events_.size());
input_event received_cancel_event = dev->written_input_events_[1];
EXPECT_EQ(expected_events[1].code, received_cancel_event.code);
EXPECT_EQ(expected_events[1].value, received_cancel_event.value);
}
} // namespace ui
......@@ -257,4 +257,18 @@ void InputControllerEvdev::UpdateCapsLockLed() {
caps_lock_led_state_ = caps_lock_state;
}
void InputControllerEvdev::PlayVibrationEffect(int id,
uint8_t amplitude,
uint16_t duration_millis) {
if (!input_device_factory_)
return;
input_device_factory_->PlayVibrationEffect(id, amplitude, duration_millis);
}
void InputControllerEvdev::StopVibration(int id) {
if (!input_device_factory_)
return;
input_device_factory_->StopVibration(id);
}
} // namespace ui
......@@ -79,6 +79,10 @@ class COMPONENT_EXPORT(EVDEV) InputControllerEvdev : public InputController {
void GetGesturePropertiesService(
mojo::PendingReceiver<ozone::mojom::GesturePropertiesService> receiver)
override;
void PlayVibrationEffect(int id,
uint8_t amplitude,
uint16_t duration_millis) override;
void StopVibration(int id) override;
private:
// Post task to update settings.
......
......@@ -397,6 +397,26 @@ void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
}
}
void InputDeviceFactoryEvdev::PlayVibrationEffect(int id,
uint8_t amplitude,
uint16_t duration_millis) {
for (const auto& it : converters_) {
if (it.second->id() == id) {
it.second->PlayVibrationEffect(amplitude, duration_millis);
return;
}
}
}
void InputDeviceFactoryEvdev::StopVibration(int id) {
for (const auto& it : converters_) {
if (it.second->id() == id) {
it.second->StopVibration();
return;
}
}
}
bool InputDeviceFactoryEvdev::IsDeviceEnabled(
const EventConverterEvdev* converter) {
if (!input_device_settings_.enable_internal_touchpad &&
......@@ -515,7 +535,8 @@ void InputDeviceFactoryEvdev::NotifyGamepadDevicesUpdated() {
for (auto it = converters_.begin(); it != converters_.end(); ++it) {
if (it->second->HasGamepad()) {
gamepads.emplace_back(it->second->input_device(),
it->second->GetGamepadAxes());
it->second->GetGamepadAxes(),
it->second->GetGamepadRumbleCapability());
}
}
......
......@@ -62,6 +62,10 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdev {
// LED state.
void SetCapsLockLed(bool enabled);
// Handle gamepad force feedback effects.
void PlayVibrationEffect(int id, uint8_t amplitude, uint16_t duration_millis);
void StopVibration(int id);
// Bits from InputController that have to be answered on IO.
void UpdateInputDeviceSettings(const InputDeviceSettingsEvdev& settings);
void GetTouchDeviceStatus(InputController::GetTouchDeviceStatusReply reply);
......
......@@ -108,4 +108,20 @@ void InputDeviceFactoryEvdevProxy::GetGesturePropertiesService(
input_device_factory_, std::move(receiver)));
}
void InputDeviceFactoryEvdevProxy::PlayVibrationEffect(
int id,
uint8_t amplitude,
uint16_t duration_millis) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&InputDeviceFactoryEvdev::PlayVibrationEffect,
input_device_factory_, id, amplitude, duration_millis));
}
void InputDeviceFactoryEvdevProxy::StopVibration(int id) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&InputDeviceFactoryEvdev::StopVibration,
input_device_factory_, id));
}
} // namespace ui
......@@ -49,6 +49,8 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdevProxy {
InputController::GetTouchEventLogReply reply);
void GetGesturePropertiesService(
mojo::PendingReceiver<ozone::mojom::GesturePropertiesService> receiver);
void PlayVibrationEffect(int id, uint8_t amplitude, uint16_t duration_millis);
void StopVibration(int id);
private:
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
......
......@@ -67,6 +67,10 @@ class StubInputController : public InputController {
void GetGesturePropertiesService(
mojo::PendingReceiver<ui::ozone::mojom::GesturePropertiesService>
receiver) override {}
void PlayVibrationEffect(int id,
uint8_t amplitude,
uint16_t duration_millis) override {}
void StopVibration(int id) override {}
private:
DISALLOW_COPY_AND_ASSIGN(StubInputController);
......
......@@ -93,6 +93,15 @@ class COMPONENT_EXPORT(OZONE_BASE) InputController {
virtual void SetTouchscreensEnabled(bool enabled) = 0;
// Controls vibration for the gamepad device with the corresponding |id|.
// |amplitude| determines the strength of the vibration, where 0 is no
// vibration and 255 is maximum vibration, and |duration_millis|
// determines the duration of the vibration in milliseconds.
virtual void PlayVibrationEffect(int id,
uint8_t amplitude,
uint16_t duration_millis) = 0;
virtual void StopVibration(int id) = 0;
// If |enable_filter| is true, all keys on the internal keyboard except
// |allowed_keys| are disabled.
virtual void SetInternalKeyboardFilter(bool enable_filter,
......
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