Commit d5524bc1 authored by Jun Mukai's avatar Jun Mukai Committed by Commit Bot

Revert "Add gamepad rumble support in Exo and Wayland."

This reverts commit 047950bb.

Reason for revert: failing arc.Gamepad

Original change's description:
> Add gamepad rumble support in Exo and Wayland.
> 
> - Add exo::Gamepad interface.
> - Add exo::GamepadObserver interface.
> - Add WaylandGamepadVibratorImpl to handle vibration and cancel
> vibration requests from the client.
> - Refactor WaylandGamingSeatDelegate so that WaylandGamepadDelegate
> handles all gamepad-specific events.
> - Add Wayland protocol to support gamepad rumble. The zcr_gamepad_vibrator_v2 interface is created when the server calls
> the vibrator_added event. The client can then trigger/disable vibration
> events on a gamepad vibrator through the interface.
> 
> BUG=b:158686809
> TEST=Add unit tests in gamepad_unittest. All tests pass in
> exo_unittests.
> 
> Change-Id: Ie4b1b3b5f9748f31846c54c97cb4f7c315d09a05
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2313976
> Commit-Queue: Merissa Tan <liyingtan@google.com>
> Auto-Submit: Merissa Tan <liyingtan@google.com>
> Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#798253}

TBR=oshima@chromium.org,xutan@chromium.org,tetsui@chromium.org,prabirmsp@chromium.org,liyingtan@google.com

Change-Id: I39d80057e7bcfda6bbfab5d2ea99d0d94a743aaf
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: b:158686809
Bug: 1116682
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2358690Reviewed-by: default avatarJun Mukai <mukai@chromium.org>
Commit-Queue: Jun Mukai <mukai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798400}
parent 5d8dcbd9
......@@ -222,10 +222,6 @@ const base::Feature kFilesZipPack{"FilesZipPack",
const base::Feature kFilesZipUnpack{"FilesZipUnpack",
base::FEATURE_DISABLED_BY_DEFAULT};
// Controls gamepad vibration in Exo.
const base::Feature kGamepadVibration{"ExoGamepadVibration",
base::FEATURE_DISABLED_BY_DEFAULT};
// Enables the use of Mojo by Chrome-process code to communicate with Power
// Manager. In order to use mojo, this feature must be turned on and a callsite
// must use PowerManagerMojoClient::Get().
......
......@@ -109,8 +109,6 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kFsNosymfollow;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kGaiaActionButtons;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kGamepadVibration;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kGesturePropertiesDBusService;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kHelpAppFirstRun;
......
......@@ -121,9 +121,6 @@ static_library("exo") {
"client_controlled_accelerators.h",
"client_controlled_shell_surface.cc",
"client_controlled_shell_surface.h",
"gamepad.cc",
"gamepad.h",
"gamepad_observer.h",
"gaming_seat.cc",
"gaming_seat.h",
"input_method_surface.cc",
......@@ -253,7 +250,6 @@ source_set("unit_tests") {
"data_source_unittest.cc",
"display_unittest.cc",
"drag_drop_operation_unittest.cc",
"gamepad_unittest.cc",
"gaming_seat_unittest.cc",
"input_method_surface_unittest.cc",
"keyboard_unittest.cc",
......
// 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 "components/exo/gamepad.h"
#include "base/bind.h"
#include "base/logging.h"
#include "chromeos/constants/chromeos_features.h"
namespace exo {
Gamepad::Gamepad(const ui::GamepadDevice& gamepad_device)
: device(gamepad_device),
input_controller_(
ui::OzonePlatform::GetInstance()->GetInputController()) {}
Gamepad::~Gamepad() {
for (GamepadObserver& observer : observer_list_)
observer.OnGamepadDestroying(this);
if (delegate_)
delegate_->OnRemoved();
}
void Gamepad::Vibrate(const std::vector<int64_t>& duration_millis,
const std::vector<uint8_t>& amplitudes,
int32_t repeat) {
if (!device.supports_vibration_rumble) {
VLOG(2) << "Vibrate failed because gamepad does not support vibration.";
return;
}
if (duration_millis.size() != amplitudes.size()) {
VLOG(2) << "Vibrate failed because the amplitudes vector and "
"duration_millis vector are not the same size.";
return;
}
vibration_timer_.Stop();
vibration_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(0),
base::BindOnce(&Gamepad::HandleVibrate, base::Unretained(this),
duration_millis, amplitudes, repeat, /*index=*/0,
/*duration_already_vibrated=*/0));
}
void Gamepad::HandleVibrate(const std::vector<int64_t>& duration_millis,
const std::vector<uint8_t>& amplitudes,
int32_t repeat,
size_t index,
int64_t duration_already_vibrated) {
size_t vector_size = duration_millis.size();
if (index >= vector_size)
return;
if (!can_vibrate_) {
VLOG(2) << "Gamepad is not allowed to vibrate because it is not in focus.";
return;
}
int64_t duration_left_to_vibrate =
duration_millis[index] - duration_already_vibrated;
if (duration_left_to_vibrate > kMaxDurationMillis) {
// The device does not support effects this long. Issue periodic vibration
// commands until the effect is complete.
SendVibrate(amplitudes[index], kMaxDurationMillis);
vibration_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(kMaxDurationMillis),
base::BindOnce(&Gamepad::HandleVibrate, base::Unretained(this),
duration_millis, amplitudes, repeat, index,
/*duration_already_vibrated=*/duration_already_vibrated +
kMaxDurationMillis));
} else {
SendVibrate(amplitudes[index], duration_left_to_vibrate);
index++;
bool needs_to_repeat = index >= vector_size && repeat >= 0 &&
repeat < static_cast<int32_t>(vector_size);
if (needs_to_repeat)
index = repeat;
vibration_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(duration_left_to_vibrate),
base::BindOnce(&Gamepad::HandleVibrate, base::Unretained(this),
duration_millis, amplitudes, repeat, index,
/*duration_already_vibrated=*/0));
}
}
void Gamepad::SendVibrate(uint8_t amplitude, int64_t duration_millis) {
// |duration_millis| is always <= |kMaxDurationMillis|, which is the max value
// for uint16_t, so it is safe to cast it to uint16_t here.
input_controller_->PlayVibrationEffect(
device.id, amplitude, static_cast<uint16_t>(duration_millis));
}
void Gamepad::CancelVibration() {
if (!device.supports_vibration_rumble) {
VLOG(2)
<< "CancelVibration failed because gamepad does not support vibration.";
return;
}
if (!vibration_timer_.IsRunning())
return;
vibration_timer_.Stop();
SendCancelVibration();
}
void Gamepad::SendCancelVibration() {
input_controller_->StopVibration(device.id);
}
void Gamepad::SetDelegate(std::unique_ptr<GamepadDelegate> delegate) {
DCHECK(!delegate_);
delegate_ = std::move(delegate);
}
void Gamepad::AddObserver(GamepadObserver* observer) {
observer_list_.AddObserver(observer);
}
bool Gamepad::HasObserver(GamepadObserver* observer) const {
return observer_list_.HasObserver(observer);
}
void Gamepad::RemoveObserver(GamepadObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void Gamepad::OnGamepadFocused() {
can_vibrate_ =
base::FeatureList::IsEnabled(chromeos::features::kGamepadVibration);
}
void Gamepad::OnGamepadFocusLost() {
can_vibrate_ = false;
CancelVibration();
}
void Gamepad::OnGamepadEvent(const ui::GamepadEvent& event) {
DCHECK(delegate_);
switch (event.type()) {
case ui::GamepadEventType::BUTTON:
delegate_->OnButton(event.code(), event.value(), event.timestamp());
break;
case ui::GamepadEventType::AXIS:
delegate_->OnAxis(event.code(), event.value(), event.timestamp());
break;
case ui::GamepadEventType::FRAME:
delegate_->OnFrame(event.timestamp());
break;
}
}
} // namespace exo
// 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 COMPONENTS_EXO_GAMEPAD_H_
#define COMPONENTS_EXO_GAMEPAD_H_
#include <vector>
#include "base/observer_list.h"
#include "base/timer/timer.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gamepad_observer.h"
#include "ui/events/devices/gamepad_device.h"
#include "ui/events/ozone/gamepad/gamepad_event.h"
#include "ui/ozone/public/input_controller.h"
#include "ui/ozone/public/ozone_platform.h"
namespace exo {
// Maximum force feedback duration supported by Linux.
constexpr int64_t kMaxDurationMillis = 0xFFFF;
// This class represents one gamepad. It allows control over the gamepad's
// vibration and provides focus tracking for the gamepad.
class Gamepad {
public:
explicit Gamepad(const ui::GamepadDevice& gamepad_device);
Gamepad(const Gamepad& other) = delete;
Gamepad& operator=(const Gamepad& other) = delete;
// The destructor also informs GamepadObservers and GamepadDelegate when a
// gamepad has been disconnected.
virtual ~Gamepad();
// Controls vibration effects on the gamepad.
// The duration_millis/amplitude pairs determine the duration and strength of
// the vibration. Note that the two vectors have to be the same size.
// The repeat value determines the index of the duration_millis (or
// amplitudes) vector at which the pattern to repeat begins. If repeat is
// enabled, the vibration pattern will repeat indefinitely until the vibration
// event is canceled. A repeat value of -1 disables repeat.
// The user does not have to explicitly call CancelVibration() at the end of
// every vibration call. However, if Vibrate() is called when there is an
// ongoing vibration, the existing vibration is automatically interrupted and
// canceled. The gamepad has to be focused in order for the gamepad to
// vibrate. If focus is lost when there is an ongoing vibration, the vibration
// is canceled automatically.
void Vibrate(const std::vector<int64_t>& duration_millis,
const std::vector<uint8_t>& amplitudes,
int32_t repeat);
void CancelVibration();
// The GamepadDelegate is not owned by Gamepad. The delegate must stay alive
// until OnRemoved is called.
void SetDelegate(std::unique_ptr<GamepadDelegate> delegate);
// Manages the GamepadObserver list. GamepadObservers are notified when the
// gamepad is being destroyed.
void AddObserver(GamepadObserver* observer);
bool HasObserver(GamepadObserver* observer) const;
void RemoveObserver(GamepadObserver* observer);
// Informs the gamepad when window focus changes; focus changes determine
// whether a gamepad is allowed to vibrate at any given time.
void OnGamepadFocused();
void OnGamepadFocusLost();
// Forwards gamepad events to the corresponding GamepadDelegate calls.
void OnGamepadEvent(const ui::GamepadEvent& event);
const ui::GamepadDevice device;
private:
// Private method for handling vibration patterns. Handles repeat and
// breaking down of vibration events by iterating through duration/amplitude
// vectors. Also provides handling for a vibration event that exceeds the
// maximum force feedback duration supported by Linux.
void HandleVibrate(const std::vector<int64_t>& duration_millis,
const std::vector<uint8_t>& amplitudes,
int32_t repeat,
size_t start_index,
int64_t duration_already_vibrated);
// These methods forward vibration calls to |input_controller_|.
// They are virtual for testing purposes.
virtual void SendVibrate(uint8_t amplitude, int64_t duration_millis);
virtual void SendCancelVibration();
// Keeps track of whether the gamepad is allowed to vibrate at any given
// time.
bool can_vibrate_ = false;
std::unique_ptr<GamepadDelegate> delegate_;
base::ObserverList<GamepadObserver>::Unchecked observer_list_;
// Methods to control gamepad vibration are routed through InputController.
ui::InputController* input_controller_;
// A timer to keep track of vibration requests.
base::OneShotTimer vibration_timer_;
};
} // namespace exo
#endif // COMPONENTS_EXO_GAMEPAD_H_
......@@ -12,7 +12,6 @@ namespace exo {
// Handles events for a specific gamepad.
class GamepadDelegate {
public:
virtual ~GamepadDelegate() {}
// Called when the gamepad has been removed.
virtual void OnRemoved() = 0;
......@@ -27,6 +26,9 @@ class GamepadDelegate {
// Called after all gamepad information of this frame has been set and the
// client should evaluate the updated state.
virtual void OnFrame(base::TimeTicks timestamp) = 0;
protected:
virtual ~GamepadDelegate() {}
};
} // namespace exo
......
// 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 COMPONENTS_EXO_GAMEPAD_OBSERVER_H_
#define COMPONENTS_EXO_GAMEPAD_OBSERVER_H_
namespace exo {
class Gamepad;
// Observers to the gamepad are notified when the gamepad destructs.
class GamepadObserver {
public:
virtual ~GamepadObserver() = default;
// Called at the top of the gamepad's destructor, to give observers a change
// to remove themselves.
virtual void OnGamepadDestroying(Gamepad* gamepad) = 0;
};
} // namespace exo
#endif // COMPONENTS_EXO_GAMEPAD_OBSERVER_H_
This diff is collapsed.
......@@ -6,7 +6,6 @@
#include <vector>
#include "components/exo/gamepad.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/shell_surface_util.h"
......@@ -28,6 +27,9 @@ GamingSeat::~GamingSeat() {
if (focused_)
ui::GamepadProviderOzone::GetInstance()->RemoveGamepadObserver(this);
delegate_->OnGamingSeatDestroying(this);
// Disconnect all the gamepads.
for (auto& entry : gamepads_)
entry.second->OnRemoved();
WMHelper::GetInstance()->RemoveFocusObserver(this);
}
......@@ -53,12 +55,8 @@ void GamingSeat::OnWindowFocused(aura::Window* gained_focus,
if (focused) {
ui::GamepadProviderOzone::GetInstance()->AddGamepadObserver(this);
OnGamepadDevicesUpdated();
for (auto& entry : gamepads_)
entry.second->OnGamepadFocused();
} else {
ui::GamepadProviderOzone::GetInstance()->RemoveGamepadObserver(this);
for (auto& entry : gamepads_)
entry.second->OnGamepadFocusLost();
}
}
}
......@@ -70,26 +68,25 @@ void GamingSeat::OnGamepadDevicesUpdated() {
std::vector<ui::GamepadDevice> gamepad_devices =
ui::GamepadProviderOzone::GetInstance()->GetGamepadDevices();
base::flat_map<int, std::unique_ptr<Gamepad>> new_gamepads;
base::flat_map<int, GamepadDelegate*> new_gamepads;
// Copy the "still connected gamepads".
for (auto& device : gamepad_devices) {
auto it = gamepads_.find(device.id);
if (it != gamepads_.end()) {
new_gamepads[device.id] = std::move(it->second);
new_gamepads[device.id] = it->second;
gamepads_.erase(it);
}
}
// Remove each disconected gamepad.
for (auto& entry : gamepads_)
entry.second->OnRemoved();
// Add each new connected gamepad.
for (auto& device : gamepad_devices) {
if (new_gamepads.find(device.id) == new_gamepads.end()) {
std::unique_ptr<Gamepad> gamepad = std::make_unique<Gamepad>(device);
if (focused_)
gamepad->OnGamepadFocused();
delegate_->GamepadAdded(*gamepad);
new_gamepads[device.id] = std::move(gamepad);
}
if (new_gamepads.find(device.id) == new_gamepads.end())
new_gamepads[device.id] = delegate_->GamepadAdded(device);
}
new_gamepads.swap(gamepads_);
......@@ -100,7 +97,17 @@ void GamingSeat::OnGamepadEvent(const ui::GamepadEvent& event) {
if (it == gamepads_.end())
return;
it->second->OnGamepadEvent(event);
switch (event.type()) {
case ui::GamepadEventType::BUTTON:
it->second->OnButton(event.code(), event.value(), event.timestamp());
break;
case ui::GamepadEventType::AXIS:
it->second->OnAxis(event.code(), event.value(), event.timestamp());
break;
case ui::GamepadEventType::FRAME:
it->second->OnFrame(event.timestamp());
break;
}
}
} // namespace exo
......@@ -12,7 +12,6 @@
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h"
#include "components/exo/gamepad.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/client/focus_change_observer.h"
#include "ui/events/ozone/gamepad/gamepad_observer.h"
......@@ -27,7 +26,8 @@ class GamepadDelegate;
class GamingSeat : public aura::client::FocusChangeObserver,
public ui::GamepadObserver {
public:
// This class will monitor gamepad connection changes and manage gamepads.
// This class will monitor gamepad connection changes and manage gamepad
// returned by gaming_seat_delegate.
GamingSeat(GamingSeatDelegate* gaming_seat_delegate);
~GamingSeat() override;
......@@ -45,7 +45,7 @@ class GamingSeat : public aura::client::FocusChangeObserver,
GamingSeatDelegate* const delegate_;
// Contains the delegate for each gamepad device.
base::flat_map<int, std::unique_ptr<Gamepad>> gamepads_;
base::flat_map<int, GamepadDelegate*> gamepads_;
// The flag if a valid target for gaming seat is focused. In other words, if
// it's true, this class is observing gamepad events.
......
......@@ -5,11 +5,14 @@
#ifndef COMPONENTS_EXO_GAMING_SEAT_DELEGATE_H_
#define COMPONENTS_EXO_GAMING_SEAT_DELEGATE_H_
namespace ui {
struct GamepadDevice;
} // namespace ui
namespace exo {
class Surface;
class GamepadDelegate;
class GamingSeat;
class Gamepad;
// It send gamepad_added event and generate the GamepadDelegate.
class GamingSeatDelegate {
......@@ -22,9 +25,9 @@ class GamingSeatDelegate {
// seat. E.g. the surface is owned by the same client as the gaming seat.
virtual bool CanAcceptGamepadEventsForSurface(Surface* surface) const = 0;
// When a new gamepad is connected, gaming seat call this to assign a
// gamepad delegate to the gamepad.
virtual void GamepadAdded(Gamepad& gamepad) = 0;
// When a new gamepad is connected, gaming seat call this to get the
// gamepad delegate.
virtual GamepadDelegate* GamepadAdded(const ui::GamepadDevice& device) = 0;
protected:
virtual ~GamingSeatDelegate() {}
......
......@@ -11,7 +11,6 @@
#include "base/run_loop.h"
#include "base/time/time.h"
#include "components/exo/buffer.h"
#include "components/exo/gamepad.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/shell_surface.h"
......@@ -27,33 +26,24 @@
namespace exo {
namespace {
class MockGamingSeatDelegate : public GamingSeatDelegate {
public:
MOCK_CONST_METHOD1(CanAcceptGamepadEventsForSurface, bool(Surface*));
MOCK_METHOD1(GamepadAdded, GamepadDelegate*(const ui::GamepadDevice&));
MOCK_METHOD0(Die, void());
void OnGamingSeatDestroying(GamingSeat*) override { delete this; }
~MockGamingSeatDelegate() { Die(); }
};
class MockGamepadDelegate : public GamepadDelegate {
public:
MockGamepadDelegate() {}
// Overridden from GamepadDelegate:
MOCK_METHOD(void, OnRemoved, (), (override));
MOCK_METHOD(void,
OnAxis,
(int axis, double value, base::TimeTicks timestamp),
(override));
MOCK_METHOD(void,
OnButton,
(int button, bool pressed, base::TimeTicks timestamp),
(override));
MOCK_METHOD(void, OnFrame, (base::TimeTicks timestamp), (override));
};
class MockGamingSeatDelegate : public GamingSeatDelegate {
public:
MOCK_METHOD(bool,
CanAcceptGamepadEventsForSurface,
(Surface * surface),
(const, override));
MOCK_METHOD(void, GamepadAdded, (Gamepad & gamepad), (override));
MOCK_METHOD(void, Die, (), ());
void OnGamingSeatDestroying(GamingSeat*) override { delete this; }
~MockGamingSeatDelegate() { Die(); }
MOCK_METHOD0(OnRemoved, void());
MOCK_METHOD3(OnAxis, void(int, double, base::TimeTicks));
MOCK_METHOD3(OnButton, void(int, bool, base::TimeTicks));
MOCK_METHOD1(OnFrame, void(base::TimeTicks));
};
class GamingSeatTest : public test::ExoTestBase {
......@@ -99,6 +89,7 @@ class GamingSeatTest : public test::ExoTestBase {
protected:
std::unique_ptr<GamingSeat> gaming_seat_;
DISALLOW_COPY_AND_ASSIGN(GamingSeatTest);
};
......@@ -118,63 +109,44 @@ TEST_F(GamingSeatTest, ConnectionChange) {
.WillOnce(testing::Return(true));
InitializeGamingSeat(gaming_seat_delegate);
std::unique_ptr<MockGamepadDelegate> gamepad_delegates[6];
for (auto& delegate : gamepad_delegates)
delegate = std::make_unique<testing::StrictMock<MockGamepadDelegate>>();
testing::StrictMock<MockGamepadDelegate> gamepad_delegate[6];
{ // Test sequence
testing::InSequence s;
// Connect 2 gamepads.
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegates[0]));
}));
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegates[1]));
}));
.WillOnce(testing::Return(&gamepad_delegate[0]))
.WillOnce(testing::Return(&gamepad_delegate[1]));
// Send frame to connected gamepad.
EXPECT_CALL(*gamepad_delegates[0], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[1], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[0], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[1], OnFrame(testing::_)).Times(1);
// Connect 3 more.
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegates[2]));
}));
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegates[3]));
}));
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegates[4]));
}));
.WillOnce(testing::Return(&gamepad_delegate[2]))
.WillOnce(testing::Return(&gamepad_delegate[3]))
.WillOnce(testing::Return(&gamepad_delegate[4]));
// Send frame to all gamepads.
EXPECT_CALL(*gamepad_delegates[0], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[1], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[2], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[3], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[4], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[0], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[1], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[2], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[3], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[4], OnFrame(testing::_)).Times(1);
// Disconnect gamepad 0 and gamepad 2 and connect a new gamepad.
EXPECT_CALL(gamepad_delegate[0], OnRemoved()).Times(1);
EXPECT_CALL(gamepad_delegate[2], OnRemoved()).Times(1);
EXPECT_CALL(gamepad_delegate[4], OnRemoved()).Times(1);
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegates](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegates[5]));
}));
.WillOnce(testing::Return(&gamepad_delegate[5]));
// Send frame to all gamepads.
EXPECT_CALL(*gamepad_delegates[1], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[3], OnFrame(testing::_)).Times(1);
EXPECT_CALL(*gamepad_delegates[5], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[1], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[3], OnFrame(testing::_)).Times(1);
EXPECT_CALL(gamepad_delegate[5], OnFrame(testing::_)).Times(1);
// disconnect other gamepads
EXPECT_CALL(gamepad_delegate[1], OnRemoved()).Times(1);
EXPECT_CALL(gamepad_delegate[3], OnRemoved()).Times(1);
EXPECT_CALL(gamepad_delegate[5], OnRemoved()).Times(1);
}
EXPECT_CALL(*gamepad_delegates[0], OnRemoved()).Times(1);
EXPECT_CALL(*gamepad_delegates[2], OnRemoved()).Times(1);
EXPECT_CALL(*gamepad_delegates[4], OnRemoved()).Times(1);
// The rest of gamepads should be disconnected after GamingSeat is
// destroyed.
EXPECT_CALL(*gamepad_delegates[1], OnRemoved()).Times(1);
EXPECT_CALL(*gamepad_delegates[3], OnRemoved()).Times(1);
EXPECT_CALL(*gamepad_delegates[5], OnRemoved()).Times(1);
// Gamepad connected.
UpdateGamepadDevice({0, 1});
SendFrameToGamepads({0, 1});
......@@ -182,8 +154,8 @@ TEST_F(GamingSeatTest, ConnectionChange) {
SendFrameToGamepads({0, 1, 2, 3, 4});
UpdateGamepadDevice({1, 3, 5});
SendFrameToGamepads({1, 2, 3, 4, 5});
DestroyGamingSeat(gaming_seat_delegate);
UpdateGamepadDevice({});
DestroyGamingSeat(gaming_seat_delegate);
}
TEST_F(GamingSeatTest, Timestamp) {
......@@ -202,8 +174,8 @@ TEST_F(GamingSeatTest, Timestamp) {
.WillOnce(testing::Return(true));
InitializeGamingSeat(gaming_seat_delegate);
auto gamepad_delegate =
std::make_unique<testing::StrictMock<MockGamepadDelegate>>();
testing::StrictMock<MockGamepadDelegate> gamepad_delegate;
base::TimeTicks expected_time = base::TimeTicks::Now();
{ // Test sequence
......@@ -211,18 +183,14 @@ TEST_F(GamingSeatTest, Timestamp) {
// Connect gamepad.
EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_))
.WillOnce(testing::Invoke([&gamepad_delegate](auto& gamepad) {
gamepad.SetDelegate(std::move(gamepad_delegate));
}));
.WillOnce(testing::Return(&gamepad_delegate));
// Send button to connected gamepad. Expect correct timestamp.
EXPECT_CALL(*gamepad_delegate,
EXPECT_CALL(gamepad_delegate,
OnButton(testing::_, testing::_, testing::Eq(expected_time)))
.Times(1);
// Disconnect gamepad.
EXPECT_CALL(gamepad_delegate, OnRemoved()).Times(1);
}
// Disconnect gamepad.
EXPECT_CALL(*gamepad_delegate, OnRemoved()).Times(1);
// Gamepad connected.
UpdateGamepadDevice({1});
SendButtonToGamepads({1}, expected_time);
......
......@@ -181,7 +181,7 @@ Server::Server(Display* display)
bind_shell);
wl_global_create(wl_display_.get(), &zcr_cursor_shapes_v1_interface, 1,
display_, bind_cursor_shapes);
wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, 2,
wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, 1,
display_, bind_gaming_input);
wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface,
zcr_keyboard_configuration_v1_interface.version, display_,
......
......@@ -12,9 +12,7 @@
#include "base/feature_list.h"
#include "base/macros.h"
#include "components/exo/gamepad.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gamepad_observer.h"
#include "components/exo/gaming_seat.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/wayland/server_util.h"
......@@ -38,89 +36,12 @@ unsigned int GetGamepadBusType(ui::InputDeviceType type) {
////////////////////////////////////////////////////////////////////////////////
// gaming_input_interface:
// Handles the vibration requests sent by the client for a gamepad.
class WaylandGamepadVibratorImpl : public GamepadObserver {
public:
explicit WaylandGamepadVibratorImpl(Gamepad* gamepad) : gamepad_(gamepad) {
gamepad_->AddObserver(this);
}
WaylandGamepadVibratorImpl(const WaylandGamepadVibratorImpl& other) = delete;
WaylandGamepadVibratorImpl& operator=(
const WaylandGamepadVibratorImpl& other) = delete;
~WaylandGamepadVibratorImpl() override {
if (gamepad_)
gamepad_->RemoveObserver(this);
}
void OnVibrate(wl_array* duration_millis,
wl_array* amplitudes,
int32_t repeat) {
std::vector<int64_t> extracted_durations;
int64_t* p;
const uint8_t* duration_millis_end =
static_cast<uint8_t*>(duration_millis->data) + duration_millis->size;
for (p = static_cast<int64_t*>(duration_millis->data);
(const uint8_t*)p < duration_millis_end; p++) {
extracted_durations.emplace_back(*p);
}
const uint8_t* amplitudes_start = static_cast<uint8_t*>(amplitudes->data);
size_t amplitude_size = amplitudes->size / sizeof(uint8_t);
const uint8_t* amplitudes_end = amplitudes_start + amplitude_size;
std::vector<uint8_t> extracted_amplitudes(amplitudes_start, amplitudes_end);
if (gamepad_)
gamepad_->Vibrate(extracted_durations, extracted_amplitudes, repeat);
}
void OnCancelVibration() {
if (gamepad_)
gamepad_->CancelVibration();
}
// Overridden from GamepadObserver
void OnGamepadDestroying(Gamepad* gamepad) override {
DCHECK_EQ(gamepad_, gamepad);
gamepad_ = nullptr;
}
private:
Gamepad* gamepad_;
};
void gamepad_vibrator_vibrate(wl_client* client,
wl_resource* resource,
wl_array* duration_millis,
wl_array* amplitudes,
int32_t repeat) {
GetUserDataAs<WaylandGamepadVibratorImpl>(resource)->OnVibrate(
duration_millis, amplitudes, repeat);
}
void gamepad_vibrator_cancel_vibration(wl_client* client,
wl_resource* resource) {
GetUserDataAs<WaylandGamepadVibratorImpl>(resource)->OnCancelVibration();
}
void gamepad_vibrator_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
const struct zcr_gamepad_vibrator_v2_interface gamepad_vibrator_implementation =
{gamepad_vibrator_vibrate, gamepad_vibrator_cancel_vibration,
gamepad_vibrator_destroy};
// Gamepad delegate class that forwards gamepad events to the client resource.
class WaylandGamepadDelegate : public GamepadDelegate {
public:
explicit WaylandGamepadDelegate(wl_resource* gamepad_resource)
: gamepad_resource_(gamepad_resource) {}
// The object should be deleted by OnRemoved().
~WaylandGamepadDelegate() override = default;
// If gamepad_resource_ is destroyed first, ResetGamepadResource will
// be called to remove the resource from delegate, and delegate won't
// do anything after that. If delegate is destructed first, it will
......@@ -172,33 +93,10 @@ class WaylandGamepadDelegate : public GamepadDelegate {
wl_client_flush(client());
}
void ConfigureDevice(Gamepad* gamepad) {
for (const auto& axis : gamepad->device.axes) {
zcr_gamepad_v2_send_axis_added(gamepad_resource_, axis.code,
axis.min_value, axis.max_value, axis.flat,
axis.fuzz, axis.resolution);
}
if (gamepad->device.supports_vibration_rumble &&
wl_resource_get_version(gamepad_resource_) >=
ZCR_GAMEPAD_V2_VIBRATOR_ADDED_SINCE_VERSION) {
wl_resource* gamepad_vibrator_resource =
wl_resource_create(wl_resource_get_client(gamepad_resource_),
&zcr_gamepad_vibrator_v2_interface,
wl_resource_get_version(gamepad_resource_), 0);
SetImplementation(gamepad_vibrator_resource,
&gamepad_vibrator_implementation,
std::make_unique<WaylandGamepadVibratorImpl>(gamepad));
zcr_gamepad_v2_send_vibrator_added(gamepad_resource_,
gamepad_vibrator_resource);
}
zcr_gamepad_v2_send_activated(gamepad_resource_);
}
private:
// The object should be deleted by OnRemoved().
~WaylandGamepadDelegate() override {}
// The client who own this gamepad instance.
wl_client* client() const {
return wl_resource_get_client(gamepad_resource_);
......@@ -231,28 +129,33 @@ class WaylandGamingSeatDelegate : public GamingSeatDelegate {
wl_resource_get_client(surface_resource) ==
wl_resource_get_client(gaming_seat_resource_);
}
void GamepadAdded(Gamepad& gamepad) override {
GamepadDelegate* GamepadAdded(const ui::GamepadDevice& device) override {
wl_resource* gamepad_resource =
wl_resource_create(wl_resource_get_client(gaming_seat_resource_),
&zcr_gamepad_v2_interface,
wl_resource_get_version(gaming_seat_resource_), 0);
zcr_gaming_seat_v2_send_gamepad_added_with_device_info(
gaming_seat_resource_, gamepad_resource, gamepad.device.name.c_str(),
GetGamepadBusType(gamepad.device.type), gamepad.device.vendor_id,
gamepad.device.product_id, gamepad.device.version);
std::unique_ptr<WaylandGamepadDelegate> gamepad_delegate =
std::make_unique<WaylandGamepadDelegate>(gamepad_resource);
GamepadDelegate* gamepad_delegate =
new WaylandGamepadDelegate(gamepad_resource);
wl_resource_set_implementation(
gamepad_resource, &gamepad_implementation, gamepad_delegate.get(),
gamepad_resource, &gamepad_implementation, gamepad_delegate,
&WaylandGamepadDelegate::ResetGamepadResource);
gamepad_delegate->ConfigureDevice(&gamepad);
gamepad.SetDelegate(std::move(gamepad_delegate));
zcr_gaming_seat_v2_send_gamepad_added_with_device_info(
gaming_seat_resource_, gamepad_resource, device.name.c_str(),
GetGamepadBusType(device.type), device.vendor_id, device.product_id,
device.version);
for (const auto& axis : device.axes) {
zcr_gamepad_v2_send_axis_added(gamepad_resource, axis.code,
axis.min_value, axis.max_value, axis.flat,
axis.fuzz, axis.resolution);
}
zcr_gamepad_v2_send_activated(gamepad_resource);
wl_client_flush(wl_resource_get_client(gaming_seat_resource_));
return gamepad_delegate;
}
private:
......
......@@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="zcr_gaming_input_v2" version="2">
<interface name="zcr_gaming_input_v2" version="1">
<description summary="extends wl_seat with gaming input devices">
A global interface to provide gaming input devices for a given seat.
......@@ -98,7 +98,7 @@
</event>
</interface>
<interface name="zcr_gamepad_v2" version="2">
<interface name="zcr_gamepad_v2" version="1">
<description summary="gamepad input device">
The zcr_gamepad_v2 interface represents one or more gamepad input devices,
which are reported as a normalized 'Standard Gamepad' as it is specified
......@@ -106,10 +106,7 @@
</description>
<request name="destroy" type="destructor">
<description summary="destroy gamepad">
Destroy gamepad. Instances created from this gamepad are unaffected
and should be destroyed separately.
</description>
<description summary="destroy gamepad object"/>
</request>
<event name="removed">
......@@ -194,51 +191,5 @@
gamepad_added_with_device_info.
</description>
</event>
<!-- added since v2 -->
<event name="vibrator_added" since="2">
<description summary="a vibrator is added">
Adds a vibrator to the gamepad. Only called if server has verified
that gamepad has a vibrator. The vibrator(s) for a gamepad are expected
to be added before the "activated" event is called.
</description>
<arg name="vibrator" type="new_id" interface="zcr_gamepad_vibrator_v2" summary="the gamepad vibrator"/>
</event>
</interface>
<interface name="zcr_gamepad_vibrator_v2" version="2">
<description summary="vibrator interface for a gamepad">
An interface that provides access to the vibrator of a gamepad. Requests can be
sent to make the gamepad vibrate and to stop an ongoing vibration.
</description>
<request name="vibrate" since="2">
<description summary="triggers the vibration event">
Triggers the vibration event on the gamepad vibrator. The gamepad is only allowed to
vibrate while the window is in focus. The values in the timings array are 64-bit integers
and the values in the amplitudes array are unsigned 8-bit integers.
The timings array and the amplitudes array are of the same length.
For each timing/amplitude pair, the amplitude determines the strength of
the vibration and the timing determines the length of the vibration in milliseconds.
Amplitude values must be between 0 and 255. An amplitude of 0 implies no vibration
and any timing/amplitude pair with a timing value of 0 is ignored.
The repeat argument determines the index at which the vibration pattern to repeat begins.
A repeat value of -1 disables repetition. If repetition is enabled, the vibration
pattern will repeat indefinitely until stopped, or when focus is lost.
</description>
<arg name="timings" type="array" summary="array of timing values" />
<arg name="amplitudes" type="array" summary="array of amplitude values" />
<arg name="repeat" type="int" summary="index into the timings array at which to repeat" />
</request>
<request name="cancel_vibration" since="2">
<description summary="cancels the existing vibration event">
Cancels the currently ongoing vibration event on the gamepad vibrator.
</description>
</request>
<request name="destroy" type="destructor" since="2">
<description summary="destroy gamepad vibrator"/>
</request>
</interface>
</protocol>
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