Commit 27a13dea authored by Matt Reynolds's avatar Matt Reynolds Committed by Commit Bot

[gamepad] Refactor HID output report platform logic

Previously, HID gamepad haptics duplicated the OS-specific
logic for writing HID output reports. This CL factors out the
duplicated logic.

BUG=984155

Change-Id: I31001415f840a169a261ee9c1e84030e0e6ee17e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1754666
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Reviewed-by: default avatarOvidio de Jesús Ruiz-Henríquez <odejesush@chromium.org>
Cr-Commit-Position: refs/heads/master@{#688716}
parent d3045ebd
...@@ -108,7 +108,7 @@ test("device_unittests") { ...@@ -108,7 +108,7 @@ test("device_unittests") {
"gamepad/gamepad_id_list_unittest.cc", "gamepad/gamepad_id_list_unittest.cc",
"gamepad/gamepad_provider_unittest.cc", "gamepad/gamepad_provider_unittest.cc",
"gamepad/gamepad_service_unittest.cc", "gamepad/gamepad_service_unittest.cc",
"gamepad/hid_haptic_gamepad_base_unittest.cc", "gamepad/hid_haptic_gamepad_unittest.cc",
"gamepad/public/cpp/gamepad_mojom_traits_unittest.cc", "gamepad/public/cpp/gamepad_mojom_traits_unittest.cc",
"test/run_all_unittests.cc", "test/run_all_unittests.cc",
] ]
......
...@@ -15,14 +15,8 @@ component("gamepad") { ...@@ -15,14 +15,8 @@ component("gamepad") {
sources = [ sources = [
"abstract_haptic_gamepad.cc", "abstract_haptic_gamepad.cc",
"abstract_haptic_gamepad.h", "abstract_haptic_gamepad.h",
"dualshock4_controller_base.cc", "dualshock4_controller.cc",
"dualshock4_controller_base.h", "dualshock4_controller.h",
"dualshock4_controller_linux.cc",
"dualshock4_controller_linux.h",
"dualshock4_controller_mac.cc",
"dualshock4_controller_mac.h",
"dualshock4_controller_win.cc",
"dualshock4_controller_win.h",
"game_controller_data_fetcher_mac.h", "game_controller_data_fetcher_mac.h",
"game_controller_data_fetcher_mac.mm", "game_controller_data_fetcher_mac.mm",
"gamepad_blocklist.cc", "gamepad_blocklist.cc",
...@@ -72,14 +66,15 @@ component("gamepad") { ...@@ -72,14 +66,15 @@ component("gamepad") {
"gamepad_user_gesture.h", "gamepad_user_gesture.h",
"hid_dll_functions_win.cc", "hid_dll_functions_win.cc",
"hid_dll_functions_win.h", "hid_dll_functions_win.h",
"hid_haptic_gamepad_base.cc", "hid_haptic_gamepad.cc",
"hid_haptic_gamepad_base.h", "hid_haptic_gamepad.h",
"hid_haptic_gamepad_linux.cc", "hid_writer.h",
"hid_haptic_gamepad_linux.h", "hid_writer_linux.cc",
"hid_haptic_gamepad_mac.cc", "hid_writer_linux.h",
"hid_haptic_gamepad_mac.h", "hid_writer_mac.cc",
"hid_haptic_gamepad_win.cc", "hid_writer_mac.h",
"hid_haptic_gamepad_win.h", "hid_writer_win.cc",
"hid_writer_win.h",
"raw_input_data_fetcher_win.cc", "raw_input_data_fetcher_win.cc",
"raw_input_data_fetcher_win.h", "raw_input_data_fetcher_win.h",
"raw_input_gamepad_device_win.cc", "raw_input_gamepad_device_win.cc",
......
...@@ -2,52 +2,39 @@ ...@@ -2,52 +2,39 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "device/gamepad/dualshock4_controller_base.h" #include "device/gamepad/dualshock4_controller.h"
#include <array> #include <array>
#include "device/gamepad/gamepad_id_list.h"
#include "device/gamepad/hid_writer.h"
namespace { namespace {
const uint16_t kVendorSony = 0x054c;
const uint16_t kProductDualshock4 = 0x05c4;
const uint16_t kProductDualshock4Slim = 0x9cc;
const uint8_t kRumbleMagnitudeMax = 0xff; const uint8_t kRumbleMagnitudeMax = 0xff;
enum ControllerType {
UNKNOWN_CONTROLLER,
DUALSHOCK4_CONTROLLER,
DUALSHOCK4_SLIM_CONTROLLER
};
ControllerType ControllerTypeFromDeviceIds(uint16_t vendor_id,
uint16_t product_id) {
if (vendor_id == kVendorSony) {
switch (product_id) {
case kProductDualshock4:
return DUALSHOCK4_CONTROLLER;
case kProductDualshock4Slim:
return DUALSHOCK4_SLIM_CONTROLLER;
default:
break;
}
}
return UNKNOWN_CONTROLLER;
}
} // namespace } // namespace
namespace device { namespace device {
Dualshock4ControllerBase::~Dualshock4ControllerBase() = default; Dualshock4Controller::Dualshock4Controller(std::unique_ptr<HidWriter> writer)
: writer_(std::move(writer)) {}
Dualshock4Controller::~Dualshock4Controller() = default;
// static // static
bool Dualshock4ControllerBase::IsDualshock4(uint16_t vendor_id, bool Dualshock4Controller::IsDualshock4(uint16_t vendor_id,
uint16_t product_id) { uint16_t product_id) {
return ControllerTypeFromDeviceIds(vendor_id, product_id) != auto gamepad_id = GamepadIdList::Get().GetGamepadId(vendor_id, product_id);
UNKNOWN_CONTROLLER; return gamepad_id == GamepadId::kSonyProduct05c4 ||
gamepad_id == GamepadId::kSonyProduct09cc;
} }
void Dualshock4ControllerBase::SetVibration(double strong_magnitude, void Dualshock4Controller::DoShutdown() {
double weak_magnitude) { writer_.reset();
}
void Dualshock4Controller::SetVibration(double strong_magnitude,
double weak_magnitude) {
DCHECK(writer_);
std::array<uint8_t, 32> control_report; std::array<uint8_t, 32> control_report;
control_report.fill(0); control_report.fill(0);
control_report[0] = 0x05; // report ID control_report[0] = 0x05; // report ID
...@@ -57,7 +44,11 @@ void Dualshock4ControllerBase::SetVibration(double strong_magnitude, ...@@ -57,7 +44,11 @@ void Dualshock4ControllerBase::SetVibration(double strong_magnitude,
control_report[5] = control_report[5] =
static_cast<uint8_t>(strong_magnitude * kRumbleMagnitudeMax); static_cast<uint8_t>(strong_magnitude * kRumbleMagnitudeMax);
WriteOutputReport(control_report); writer_->WriteOutputReport(control_report);
}
base::WeakPtr<AbstractHapticGamepad> Dualshock4Controller::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
} }
} // namespace device } // namespace device
...@@ -2,28 +2,38 @@ ...@@ -2,28 +2,38 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_BASE_ #ifndef DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_H_
#define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_BASE_ #define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_H_
#include <stdint.h>
#include <memory>
#include "base/memory/weak_ptr.h"
#include "device/gamepad/abstract_haptic_gamepad.h" #include "device/gamepad/abstract_haptic_gamepad.h"
namespace device { namespace device {
class Dualshock4ControllerBase : public AbstractHapticGamepad { class HidWriter;
class Dualshock4Controller final : public AbstractHapticGamepad {
public: public:
Dualshock4ControllerBase() = default; Dualshock4Controller(std::unique_ptr<HidWriter> hid_writer);
~Dualshock4ControllerBase() override; ~Dualshock4Controller() override;
static bool IsDualshock4(uint16_t vendor_id, uint16_t product_id); static bool IsDualshock4(uint16_t vendor_id, uint16_t product_id);
// AbstractHapticGamepad implementation. // AbstractHapticGamepad public implementation.
void SetVibration(double strong_magnitude, double weak_magnitude) override; void SetVibration(double strong_magnitude, double weak_magnitude) override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
private:
// AbstractHapticGamepad private implementation.
void DoShutdown() override;
// Sends an output report to the gamepad. Derived classes should override this std::unique_ptr<HidWriter> writer_;
// method with a platform-specific implementation. base::WeakPtrFactory<Dualshock4Controller> weak_factory_{this};
virtual size_t WriteOutputReport(base::span<const uint8_t> report) = 0;
}; };
} // namespace device } // namespace device
#endif // DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_BASE_ #endif // DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_H_
// Copyright 2018 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 DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_LINUX_
#define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_LINUX_
#include "base/files/scoped_file.h"
#include "base/memory/weak_ptr.h"
#include "device/gamepad/dualshock4_controller_base.h"
namespace device {
class Dualshock4ControllerLinux final : public Dualshock4ControllerBase {
public:
Dualshock4ControllerLinux(const base::ScopedFD& fd);
~Dualshock4ControllerLinux() override;
// AbstractHapticGamepad implementation.
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// Dualshock4ControllerBase implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override;
private:
int fd_; // Not owned.
base::WeakPtrFactory<Dualshock4ControllerLinux> weak_factory_{this};
};
} // namespace device
#endif // DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_LINUX_
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "device/gamepad/dualshock4_controller.h"
#include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/hid_haptic_gamepad.h"
#include "device/gamepad/hid_writer_linux.h"
#include "device/udev_linux/udev.h" #include "device/udev_linux/udev.h"
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
...@@ -542,18 +545,19 @@ void GamepadDeviceLinux::InitializeHidraw(base::ScopedFD fd) { ...@@ -542,18 +545,19 @@ void GamepadDeviceLinux::InitializeHidraw(base::ScopedFD fd) {
bool is_dualshock4 = false; bool is_dualshock4 = false;
bool is_hid_haptic = false; bool is_hid_haptic = false;
if (GetHidrawDevinfo(hidraw_fd_, &bus_type_, &vendor_id, &product_id)) { if (GetHidrawDevinfo(hidraw_fd_, &bus_type_, &vendor_id, &product_id)) {
is_dualshock4 = is_dualshock4 = Dualshock4Controller::IsDualshock4(vendor_id, product_id);
Dualshock4ControllerLinux::IsDualshock4(vendor_id, product_id); is_hid_haptic = HidHapticGamepad::IsHidHaptic(vendor_id, product_id);
is_hid_haptic = HidHapticGamepadLinux::IsHidHaptic(vendor_id, product_id);
DCHECK_LE(is_dualshock4 + is_hid_haptic, 1); DCHECK_LE(is_dualshock4 + is_hid_haptic, 1);
} }
if (is_dualshock4 && !dualshock4_) if (is_dualshock4 && !dualshock4_) {
dualshock4_ = std::make_unique<Dualshock4ControllerLinux>(hidraw_fd_); dualshock4_ = std::make_unique<Dualshock4Controller>(
std::make_unique<HidWriterLinux>(hidraw_fd_));
}
if (is_hid_haptic && !hid_haptics_) { if (is_hid_haptic && !hid_haptics_) {
hid_haptics_ = hid_haptics_ = HidHapticGamepad::Create(
HidHapticGamepadLinux::Create(vendor_id, product_id, hidraw_fd_); vendor_id, product_id, std::make_unique<HidWriterLinux>(hidraw_fd_));
} }
} }
......
...@@ -12,10 +12,8 @@ ...@@ -12,10 +12,8 @@
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "device/gamepad/abstract_haptic_gamepad.h" #include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/dualshock4_controller_linux.h"
#include "device/gamepad/gamepad_id_list.h" #include "device/gamepad/gamepad_id_list.h"
#include "device/gamepad/gamepad_standard_mappings.h" #include "device/gamepad/gamepad_standard_mappings.h"
#include "device/gamepad/hid_haptic_gamepad_linux.h"
#include "device/gamepad/udev_gamepad_linux.h" #include "device/gamepad/udev_gamepad_linux.h"
extern "C" { extern "C" {
...@@ -24,6 +22,9 @@ struct udev_device; ...@@ -24,6 +22,9 @@ struct udev_device;
namespace device { namespace device {
class Dualshock4Controller;
class HidHapticGamepad;
// GamepadDeviceLinux represents a single gamepad device which may be accessed // GamepadDeviceLinux represents a single gamepad device which may be accessed
// through multiple host interfaces. Gamepad button and axis state are queried // through multiple host interfaces. Gamepad button and axis state are queried
// through the joydev interface, while haptics commands are routed through the // through the joydev interface, while haptics commands are routed through the
...@@ -91,8 +92,7 @@ class GamepadDeviceLinux final : public AbstractHapticGamepad { ...@@ -91,8 +92,7 @@ class GamepadDeviceLinux final : public AbstractHapticGamepad {
// Closes the hidraw device node and shuts down haptics. // Closes the hidraw device node and shuts down haptics.
void CloseHidrawNode(); void CloseHidrawNode();
// AbstractHapticGamepad implementation. // AbstractHapticGamepad public implementation.
void DoShutdown() override;
void SetVibration(double strong_magnitude, double weak_magnitude) override; void SetVibration(double strong_magnitude, double weak_magnitude) override;
void SetZeroVibration() override; void SetZeroVibration() override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override; base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
...@@ -100,6 +100,9 @@ class GamepadDeviceLinux final : public AbstractHapticGamepad { ...@@ -100,6 +100,9 @@ class GamepadDeviceLinux final : public AbstractHapticGamepad {
private: private:
using OpenPathCallback = base::OnceCallback<void(base::ScopedFD)>; using OpenPathCallback = base::OnceCallback<void(base::ScopedFD)>;
// AbstractHapticGamepad private implementation.
void DoShutdown() override;
void OnOpenHidrawNodeComplete(OpenDeviceNodeCallback callback, void OnOpenHidrawNodeComplete(OpenDeviceNodeCallback callback,
base::ScopedFD fd); base::ScopedFD fd);
void InitializeHidraw(base::ScopedFD fd); void InitializeHidraw(base::ScopedFD fd);
...@@ -183,10 +186,10 @@ class GamepadDeviceLinux final : public AbstractHapticGamepad { ...@@ -183,10 +186,10 @@ class GamepadDeviceLinux final : public AbstractHapticGamepad {
GamepadBusType bus_type_ = GAMEPAD_BUS_UNKNOWN; GamepadBusType bus_type_ = GAMEPAD_BUS_UNKNOWN;
// Dualshock4 functionality, if available. // Dualshock4 functionality, if available.
std::unique_ptr<Dualshock4ControllerLinux> dualshock4_; std::unique_ptr<Dualshock4Controller> dualshock4_;
// A controller that uses a HID output report for vibration effects. // A controller that uses a HID output report for vibration effects.
std::unique_ptr<HidHapticGamepadLinux> hid_haptics_; std::unique_ptr<HidHapticGamepad> hid_haptics_;
// Task runner to use for D-Bus tasks. D-Bus client classes (including // Task runner to use for D-Bus tasks. D-Bus client classes (including
// PermissionBrokerClient) are not thread-safe and should be used only on the // PermissionBrokerClient) are not thread-safe and should be used only on the
......
...@@ -13,12 +13,13 @@ ...@@ -13,12 +13,13 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "device/gamepad/abstract_haptic_gamepad.h" #include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/dualshock4_controller_mac.h"
#include "device/gamepad/hid_haptic_gamepad_mac.h"
#include "device/gamepad/public/cpp/gamepad.h" #include "device/gamepad/public/cpp/gamepad.h"
namespace device { namespace device {
class Dualshock4Controller;
class HidHapticGamepad;
// GamepadDeviceMac represents a single gamepad device. Gamepad enumeration // GamepadDeviceMac represents a single gamepad device. Gamepad enumeration
// and state polling is handled through the raw HID interface, while haptics // and state polling is handled through the raw HID interface, while haptics
// commands are issued through the ForceFeedback framework. // commands are issued through the ForceFeedback framework.
...@@ -51,13 +52,13 @@ class GamepadDeviceMac final : public AbstractHapticGamepad { ...@@ -51,13 +52,13 @@ class GamepadDeviceMac final : public AbstractHapticGamepad {
// ForceFeedback framework. // ForceFeedback framework.
bool SupportsVibration(); bool SupportsVibration();
// AbstractHapticGamepad implementation. // AbstractHapticGamepad public implementation.
void SetVibration(double strong_magnitude, double weak_magnitude) override; void SetVibration(double strong_magnitude, double weak_magnitude) override;
void SetZeroVibration() override; void SetZeroVibration() override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override; base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
private: private:
// AbstractHapticGamepad implementation. // AbstractHapticGamepad private implementation.
void DoShutdown() override; void DoShutdown() override;
// Initialize button capabilities for |gamepad|. // Initialize button capabilities for |gamepad|.
...@@ -106,10 +107,10 @@ class GamepadDeviceMac final : public AbstractHapticGamepad { ...@@ -106,10 +107,10 @@ class GamepadDeviceMac final : public AbstractHapticGamepad {
LONG direction_data_[2]; LONG direction_data_[2];
// Dualshock4 functionality, if available. // Dualshock4 functionality, if available.
std::unique_ptr<Dualshock4ControllerMac> dualshock4_; std::unique_ptr<Dualshock4Controller> dualshock4_;
// A controller that uses a HID output report for vibration effects. // A controller that uses a HID output report for vibration effects.
std::unique_ptr<HidHapticGamepadMac> hid_haptics_; std::unique_ptr<HidHapticGamepad> hid_haptics_;
base::WeakPtrFactory<GamepadDeviceMac> weak_factory_{this}; base::WeakPtrFactory<GamepadDeviceMac> weak_factory_{this};
}; };
......
...@@ -7,7 +7,10 @@ ...@@ -7,7 +7,10 @@
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "device/gamepad/dualshock4_controller.h"
#include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/hid_haptic_gamepad.h"
#include "device/gamepad/hid_writer_mac.h"
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
...@@ -76,11 +79,12 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, ...@@ -76,11 +79,12 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id,
device_ref_(device_ref), device_ref_(device_ref),
ff_device_ref_(nullptr), ff_device_ref_(nullptr),
ff_effect_ref_(nullptr) { ff_effect_ref_(nullptr) {
if (Dualshock4ControllerMac::IsDualshock4(vendor_id, product_id)) { if (Dualshock4Controller::IsDualshock4(vendor_id, product_id)) {
dualshock4_ = std::make_unique<Dualshock4ControllerMac>(device_ref); dualshock4_ = std::make_unique<Dualshock4Controller>(
} else if (HidHapticGamepadMac::IsHidHaptic(vendor_id, product_id)) { std::make_unique<HidWriterMac>(device_ref));
hid_haptics_ = } else if (HidHapticGamepad::IsHidHaptic(vendor_id, product_id)) {
HidHapticGamepadMac::Create(vendor_id, product_id, device_ref); hid_haptics_ = HidHapticGamepad::Create(
vendor_id, product_id, std::make_unique<HidWriterMac>(device_ref));
} else if (device_ref) { } else if (device_ref) {
ff_device_ref_ = CreateForceFeedbackDevice(device_ref); ff_device_ref_ = CreateForceFeedbackDevice(device_ref);
if (ff_device_ref_) { if (ff_device_ref_) {
......
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "device/gamepad/hid_haptic_gamepad_base.h" #include "device/gamepad/hid_haptic_gamepad.h"
#include <algorithm>
#include <vector>
#include "device/gamepad/hid_writer.h"
namespace device { namespace device {
...@@ -37,7 +42,7 @@ void MagnitudeToBytes(double magnitude, ...@@ -37,7 +42,7 @@ void MagnitudeToBytes(double magnitude,
} // namespace } // namespace
// Supported HID gamepads. // Supported HID gamepads.
HidHapticGamepadBase::HapticReportData kHapticReportData[] = { HidHapticGamepad::HapticReportData kHapticReportData[] = {
// XSkills Gamecube USB adapter // XSkills Gamecube USB adapter
{0x0b43, 0x0005, 0x00, 4, 3, 3, 1 * kBitsPerByte, 0, 1}, {0x0b43, 0x0005, 0x00, 4, 3, 3, 1 * kBitsPerByte, 0, 1},
// Stadia controller prototype // Stadia controller prototype
...@@ -47,20 +52,33 @@ HidHapticGamepadBase::HapticReportData kHapticReportData[] = { ...@@ -47,20 +52,33 @@ HidHapticGamepadBase::HapticReportData kHapticReportData[] = {
}; };
size_t kHapticReportDataLength = base::size(kHapticReportData); size_t kHapticReportDataLength = base::size(kHapticReportData);
HidHapticGamepadBase::HidHapticGamepadBase(const HapticReportData& data) HidHapticGamepad::HidHapticGamepad(const HapticReportData& data,
std::unique_ptr<HidWriter> writer)
: report_id_(data.report_id), : report_id_(data.report_id),
report_length_bytes_(data.report_length_bytes), report_length_bytes_(data.report_length_bytes),
strong_offset_bytes_(data.strong_offset_bytes), strong_offset_bytes_(data.strong_offset_bytes),
weak_offset_bytes_(data.weak_offset_bytes), weak_offset_bytes_(data.weak_offset_bytes),
report_size_bits_(data.report_size_bits), report_size_bits_(data.report_size_bits),
logical_min_(data.logical_min), logical_min_(data.logical_min),
logical_max_(data.logical_max) {} logical_max_(data.logical_max),
writer_(std::move(writer)) {}
HidHapticGamepad::~HidHapticGamepad() = default;
HidHapticGamepadBase::~HidHapticGamepadBase() = default; // static
std::unique_ptr<HidHapticGamepad> HidHapticGamepad::Create(
uint16_t vendor_id,
uint16_t product_id,
std::unique_ptr<HidWriter> writer) {
DCHECK(writer);
const auto* haptic_data = GetHapticReportData(vendor_id, product_id);
if (!haptic_data)
return nullptr;
return std::make_unique<HidHapticGamepad>(*haptic_data, std::move(writer));
}
// static // static
bool HidHapticGamepadBase::IsHidHaptic(uint16_t vendor_id, bool HidHapticGamepad::IsHidHaptic(uint16_t vendor_id, uint16_t product_id) {
uint16_t product_id) {
const auto* begin = kHapticReportData; const auto* begin = kHapticReportData;
const auto* end = kHapticReportData + kHapticReportDataLength; const auto* end = kHapticReportData + kHapticReportDataLength;
const auto* find_it = const auto* find_it =
...@@ -71,9 +89,9 @@ bool HidHapticGamepadBase::IsHidHaptic(uint16_t vendor_id, ...@@ -71,9 +89,9 @@ bool HidHapticGamepadBase::IsHidHaptic(uint16_t vendor_id,
} }
// static // static
const HidHapticGamepadBase::HapticReportData* const HidHapticGamepad::HapticReportData* HidHapticGamepad::GetHapticReportData(
HidHapticGamepadBase::GetHapticReportData(uint16_t vendor_id, uint16_t vendor_id,
uint16_t product_id) { uint16_t product_id) {
const auto* begin = kHapticReportData; const auto* begin = kHapticReportData;
const auto* end = kHapticReportData + kHapticReportDataLength; const auto* end = kHapticReportData + kHapticReportDataLength;
const auto* find_it = const auto* find_it =
...@@ -83,8 +101,13 @@ HidHapticGamepadBase::GetHapticReportData(uint16_t vendor_id, ...@@ -83,8 +101,13 @@ HidHapticGamepadBase::GetHapticReportData(uint16_t vendor_id,
return find_it == end ? nullptr : &*find_it; return find_it == end ? nullptr : &*find_it;
} }
void HidHapticGamepadBase::SetVibration(double strong_magnitude, void HidHapticGamepad::DoShutdown() {
double weak_magnitude) { writer_.reset();
}
void HidHapticGamepad::SetVibration(double strong_magnitude,
double weak_magnitude) {
DCHECK(writer_);
std::vector<uint8_t> control_report(report_length_bytes_); std::vector<uint8_t> control_report(report_length_bytes_);
control_report[0] = report_id_; control_report[0] = report_id_;
if (strong_offset_bytes_ == weak_offset_bytes_) { if (strong_offset_bytes_ == weak_offset_bytes_) {
...@@ -123,7 +146,11 @@ void HidHapticGamepadBase::SetVibration(double strong_magnitude, ...@@ -123,7 +146,11 @@ void HidHapticGamepadBase::SetVibration(double strong_magnitude,
std::copy(right_bytes.begin(), right_bytes.end(), std::copy(right_bytes.begin(), right_bytes.end(),
control_report.begin() + weak_offset_bytes_); control_report.begin() + weak_offset_bytes_);
} }
WriteOutputReport(control_report); writer_->WriteOutputReport(control_report);
}
base::WeakPtr<AbstractHapticGamepad> HidHapticGamepad::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
} }
} // namespace device } // namespace device
...@@ -2,19 +2,26 @@ ...@@ -2,19 +2,26 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_BASE_H_ #ifndef DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_H_
#define DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_BASE_H_ #define DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/memory/weak_ptr.h"
#include "device/gamepad/abstract_haptic_gamepad.h" #include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/gamepad_export.h" #include "device/gamepad/gamepad_export.h"
namespace device { namespace device {
class DEVICE_GAMEPAD_EXPORT HidHapticGamepadBase class HidWriter;
class DEVICE_GAMEPAD_EXPORT HidHapticGamepad final
: public AbstractHapticGamepad { : public AbstractHapticGamepad {
public: public:
// Devices that support HID haptic effects with a simple output report can // Devices that support HID haptic effects with a simple output report can
// be supported through HidHapticGamepadBase by adding a new HapticReportData // be supported through HidHapticGamepad by adding a new HapticReportData
// item to kHapticReportData. // item to kHapticReportData.
// //
// Example: // Example:
...@@ -61,26 +68,32 @@ class DEVICE_GAMEPAD_EXPORT HidHapticGamepadBase ...@@ -61,26 +68,32 @@ class DEVICE_GAMEPAD_EXPORT HidHapticGamepadBase
const uint32_t logical_max; const uint32_t logical_max;
}; };
~HidHapticGamepadBase() override; HidHapticGamepad(const HapticReportData& data,
std::unique_ptr<HidWriter> writer);
~HidHapticGamepad() override;
static std::unique_ptr<HidHapticGamepad> Create(
uint16_t vendor_id,
uint16_t product_id,
std::unique_ptr<HidWriter> writer);
// Return true if the device IDs match an item in kHapticReportData. // Return true if the device IDs match an item in kHapticReportData.
static bool IsHidHaptic(uint16_t vendor_id, uint16_t product_id); static bool IsHidHaptic(uint16_t vendor_id, uint16_t product_id);
// Return the HapticReportData for the device with matching device IDs. // Return the HapticReportData for the device with matching device IDs.
static const HidHapticGamepadBase::HapticReportData* GetHapticReportData( static const HidHapticGamepad::HapticReportData* GetHapticReportData(
uint16_t vendor_id, uint16_t vendor_id,
uint16_t product_id); uint16_t product_id);
// Set the current vibration magnitudes. // AbstractHapticGamepad public implementation.
void SetVibration(double strong_magnitude, double weak_magnitude) override; void SetVibration(double strong_magnitude, double weak_magnitude) override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// Write the vibration output report to the device.
virtual size_t WriteOutputReport(base::span<const uint8_t> report) = 0;
protected:
HidHapticGamepadBase(const HapticReportData& data);
private: private:
// AbstractHapticGamepad private implementation.
void DoShutdown() override;
// Report ID of the report to use for vibration commands, or zero if report // Report ID of the report to use for vibration commands, or zero if report
// IDs are not used. // IDs are not used.
uint8_t report_id_; uint8_t report_id_;
...@@ -98,11 +111,15 @@ class DEVICE_GAMEPAD_EXPORT HidHapticGamepadBase ...@@ -98,11 +111,15 @@ class DEVICE_GAMEPAD_EXPORT HidHapticGamepadBase
// Logical bounds of the vibration magnitude. Assumed to be positive. // Logical bounds of the vibration magnitude. Assumed to be positive.
uint32_t logical_min_; uint32_t logical_min_;
uint32_t logical_max_; uint32_t logical_max_;
std::unique_ptr<HidWriter> writer_;
base::WeakPtrFactory<HidHapticGamepad> weak_factory_{this};
}; };
extern HidHapticGamepadBase::HapticReportData kHapticReportData[]; extern HidHapticGamepad::HapticReportData kHapticReportData[];
extern size_t kHapticReportDataLength; extern size_t kHapticReportDataLength;
} // namespace device } // namespace device
#endif // DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_BASE_H_ #endif // DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_H_
// Copyright 2018 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 "device/gamepad/hid_haptic_gamepad_linux.h"
#include "base/posix/eintr_wrapper.h"
namespace device {
HidHapticGamepadLinux::HidHapticGamepadLinux(const base::ScopedFD& fd,
const HapticReportData& data)
: HidHapticGamepadBase(data), fd_(fd.get()) {}
HidHapticGamepadLinux::~HidHapticGamepadLinux() = default;
// static
std::unique_ptr<HidHapticGamepadLinux> HidHapticGamepadLinux::Create(
uint16_t vendor_id,
uint16_t product_id,
const base::ScopedFD& fd) {
const auto* haptic_data = GetHapticReportData(vendor_id, product_id);
if (!haptic_data)
return nullptr;
return std::make_unique<HidHapticGamepadLinux>(fd, *haptic_data);
}
size_t HidHapticGamepadLinux::WriteOutputReport(
base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U);
ssize_t bytes_written =
HANDLE_EINTR(write(fd_, report.data(), report.size_bytes()));
return bytes_written < 0 ? 0 : static_cast<size_t>(bytes_written);
}
base::WeakPtr<AbstractHapticGamepad> HidHapticGamepadLinux::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device
// Copyright 2018 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 DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_LINUX_H_
#define DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_LINUX_H_
#include "device/gamepad/hid_haptic_gamepad_base.h"
#include <memory>
#include "base/files/scoped_file.h"
#include "base/memory/weak_ptr.h"
namespace device {
class HidHapticGamepadLinux final : public HidHapticGamepadBase {
public:
HidHapticGamepadLinux(const base::ScopedFD& fd, const HapticReportData& data);
~HidHapticGamepadLinux() override;
static std::unique_ptr<HidHapticGamepadLinux>
Create(uint16_t vendor_id, uint16_t product_id, const base::ScopedFD& fd);
// AbstractHapticGamepad implementation.
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// HidHapticGamepadBase implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override;
private:
// Not owned.
int fd_;
base::WeakPtrFactory<HidHapticGamepadLinux> weak_factory_{this};
};
} // namespace device
#endif // DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_LINUX_H_
// Copyright 2018 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 "device/gamepad/hid_haptic_gamepad_mac.h"
#include <CoreFoundation/CoreFoundation.h>
namespace device {
HidHapticGamepadMac::HidHapticGamepadMac(IOHIDDeviceRef device_ref,
const HapticReportData& data)
: HidHapticGamepadBase(data), device_ref_(device_ref) {}
HidHapticGamepadMac::~HidHapticGamepadMac() = default;
void HidHapticGamepadMac::DoShutdown() {
device_ref_ = nullptr;
}
// static
std::unique_ptr<HidHapticGamepadMac> HidHapticGamepadMac::Create(
uint16_t vendor_id,
uint16_t product_id,
IOHIDDeviceRef device_ref) {
const auto* haptic_data = GetHapticReportData(vendor_id, product_id);
if (!haptic_data)
return nullptr;
return std::make_unique<HidHapticGamepadMac>(device_ref, *haptic_data);
}
size_t HidHapticGamepadMac::WriteOutputReport(
base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U);
if (!device_ref_)
return 0;
IOReturn success =
IOHIDDeviceSetReport(device_ref_, kIOHIDReportTypeOutput, report[0],
report.data(), report.size_bytes());
return (success == kIOReturnSuccess) ? report.size_bytes() : 0;
}
base::WeakPtr<AbstractHapticGamepad> HidHapticGamepadMac::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device
// Copyright 2018 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 DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_MAC_H_
#define DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_MAC_H_
#include <memory>
#include <IOKit/hid/IOHIDManager.h>
#include "base/memory/weak_ptr.h"
#include "device/gamepad/hid_haptic_gamepad_base.h"
namespace device {
class HidHapticGamepadMac final : public HidHapticGamepadBase {
public:
HidHapticGamepadMac(IOHIDDeviceRef device_ref, const HapticReportData& data);
~HidHapticGamepadMac() override;
static std::unique_ptr<HidHapticGamepadMac> Create(uint16_t vendor_id,
uint16_t product_id,
IOHIDDeviceRef device_ref);
// AbstractHapticGamepad implementation.
void DoShutdown() override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// HidHapticGamepadBase implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override;
private:
IOHIDDeviceRef device_ref_;
base::WeakPtrFactory<HidHapticGamepadMac> weak_factory_{this};
};
} // namespace device
#endif // DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_MAC_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "device/gamepad/hid_haptic_gamepad_base.h" #include "device/gamepad/hid_haptic_gamepad.h"
#include <memory> #include <memory>
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "device/gamepad/hid_writer.h"
#include "device/gamepad/public/mojom/gamepad.mojom.h" #include "device/gamepad/public/mojom/gamepad.mojom.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -23,7 +24,7 @@ namespace { ...@@ -23,7 +24,7 @@ namespace {
// magnitude fields. // magnitude fields.
constexpr uint8_t kReportId = 0x42; constexpr uint8_t kReportId = 0x42;
constexpr size_t kReportLength = 5; constexpr size_t kReportLength = 5;
constexpr HidHapticGamepadBase::HapticReportData kHapticReportData = { constexpr HidHapticGamepad::HapticReportData kHapticReportData = {
0x1234, 0xabcd, kReportId, kReportLength, 1, 3, 16, 0, 0xffff}; 0x1234, 0xabcd, kReportId, kReportLength, 1, 3, 16, 0, 0xffff};
// The expected "stop vibration" report bytes (zero vibration). // The expected "stop vibration" report bytes (zero vibration).
...@@ -51,31 +52,24 @@ constexpr double kZeroMagnitude = 0.0; ...@@ -51,31 +52,24 @@ constexpr double kZeroMagnitude = 0.0;
constexpr base::TimeDelta kPendingTaskDuration = constexpr base::TimeDelta kPendingTaskDuration =
base::TimeDelta::FromMillisecondsD(kDurationMillis); base::TimeDelta::FromMillisecondsD(kDurationMillis);
// An implementation of HidHapticGamepadBase that records its output reports. class FakeHidWriter : public HidWriter {
class FakeHidHapticGamepad final : public HidHapticGamepadBase {
public: public:
FakeHidHapticGamepad(const HidHapticGamepadBase::HapticReportData& data) FakeHidWriter() = default;
: HidHapticGamepadBase(data) {} ~FakeHidWriter() override = default;
~FakeHidHapticGamepad() override = default;
// HidWriter implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override { size_t WriteOutputReport(base::span<const uint8_t> report) override {
output_reports_.push_back( output_reports.emplace_back(report.begin(), report.end());
std::vector<uint8_t>(report.begin(), report.end()));
return report.size_bytes(); return report.size_bytes();
} }
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override { std::vector<std::vector<uint8_t>> output_reports;
return weak_factory_.GetWeakPtr();
}
std::vector<std::vector<uint8_t>> output_reports_;
base::WeakPtrFactory<FakeHidHapticGamepad> weak_factory_{this};
}; };
// Main test fixture // Main test fixture
class HidHapticGamepadBaseTest : public testing::Test { class HidHapticGamepadTest : public testing::Test {
public: public:
HidHapticGamepadBaseTest() HidHapticGamepadTest()
: start_vibration_output_report_(kStartVibrationData, : start_vibration_output_report_(kStartVibrationData,
kStartVibrationData + kReportLength), kStartVibrationData + kReportLength),
stop_vibration_output_report_(kStopVibrationData, stop_vibration_output_report_(kStopVibrationData,
...@@ -85,8 +79,12 @@ class HidHapticGamepadBaseTest : public testing::Test { ...@@ -85,8 +79,12 @@ class HidHapticGamepadBaseTest : public testing::Test {
first_callback_result_( first_callback_result_(
mojom::GamepadHapticsResult::GamepadHapticsResultError), mojom::GamepadHapticsResult::GamepadHapticsResultError),
second_callback_result_( second_callback_result_(
mojom::GamepadHapticsResult::GamepadHapticsResultError), mojom::GamepadHapticsResult::GamepadHapticsResultError) {
gamepad_(std::make_unique<FakeHidHapticGamepad>(kHapticReportData)) {} auto fake_hid_writer = std::make_unique<FakeHidWriter>();
fake_hid_writer_ = fake_hid_writer.get();
gamepad_ = std::make_unique<HidHapticGamepad>(kHapticReportData,
std::move(fake_hid_writer));
}
void TearDown() override { gamepad_->Shutdown(); } void TearDown() override { gamepad_->Shutdown(); }
...@@ -128,25 +126,26 @@ class HidHapticGamepadBaseTest : public testing::Test { ...@@ -128,25 +126,26 @@ class HidHapticGamepadBaseTest : public testing::Test {
int second_callback_count_; int second_callback_count_;
mojom::GamepadHapticsResult first_callback_result_; mojom::GamepadHapticsResult first_callback_result_;
mojom::GamepadHapticsResult second_callback_result_; mojom::GamepadHapticsResult second_callback_result_;
std::unique_ptr<FakeHidHapticGamepad> gamepad_; FakeHidWriter* fake_hid_writer_;
std::unique_ptr<HidHapticGamepad> gamepad_;
base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME}; base::test::TaskEnvironment::TimeSource::MOCK_TIME};
DISALLOW_COPY_AND_ASSIGN(HidHapticGamepadBaseTest); DISALLOW_COPY_AND_ASSIGN(HidHapticGamepadTest);
}; };
TEST_F(HidHapticGamepadBaseTest, PlayEffectTest) { TEST_F(HidHapticGamepadTest, PlayEffectTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Run the queued task and start vibration. // Run the queued task and start vibration.
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(start_vibration_output_report_)); testing::ElementsAre(start_vibration_output_report_));
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u);
...@@ -154,7 +153,7 @@ TEST_F(HidHapticGamepadBaseTest, PlayEffectTest) { ...@@ -154,7 +153,7 @@ TEST_F(HidHapticGamepadBaseTest, PlayEffectTest) {
// Finish the effect. // Finish the effect.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(start_vibration_output_report_)); testing::ElementsAre(start_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(mojom::GamepadHapticsResult::GamepadHapticsResultComplete, EXPECT_EQ(mojom::GamepadHapticsResult::GamepadHapticsResultComplete,
...@@ -162,17 +161,17 @@ TEST_F(HidHapticGamepadBaseTest, PlayEffectTest) { ...@@ -162,17 +161,17 @@ TEST_F(HidHapticGamepadBaseTest, PlayEffectTest) {
EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
} }
TEST_F(HidHapticGamepadBaseTest, ResetVibrationTest) { TEST_F(HidHapticGamepadTest, ResetVibrationTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
PostResetVibration(base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, PostResetVibration(base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Run the queued task and reset vibration. // Run the queued task and reset vibration.
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_)); testing::ElementsAre(stop_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(mojom::GamepadHapticsResult::GamepadHapticsResultComplete, EXPECT_EQ(mojom::GamepadHapticsResult::GamepadHapticsResultComplete,
...@@ -180,18 +179,18 @@ TEST_F(HidHapticGamepadBaseTest, ResetVibrationTest) { ...@@ -180,18 +179,18 @@ TEST_F(HidHapticGamepadBaseTest, ResetVibrationTest) {
EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
} }
TEST_F(HidHapticGamepadBaseTest, ZeroVibrationTest) { TEST_F(HidHapticGamepadTest, ZeroVibrationTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
PostPlayEffect(kZeroStartDelayMillis, kZeroMagnitude, kZeroMagnitude, PostPlayEffect(kZeroStartDelayMillis, kZeroMagnitude, kZeroMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Run the queued task. // Run the queued task.
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_)); testing::ElementsAre(stop_vibration_output_report_));
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u);
...@@ -199,7 +198,7 @@ TEST_F(HidHapticGamepadBaseTest, ZeroVibrationTest) { ...@@ -199,7 +198,7 @@ TEST_F(HidHapticGamepadBaseTest, ZeroVibrationTest) {
// Finish the effect. // Finish the effect.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_)); testing::ElementsAre(stop_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(mojom::GamepadHapticsResult::GamepadHapticsResultComplete, EXPECT_EQ(mojom::GamepadHapticsResult::GamepadHapticsResultComplete,
...@@ -207,19 +206,19 @@ TEST_F(HidHapticGamepadBaseTest, ZeroVibrationTest) { ...@@ -207,19 +206,19 @@ TEST_F(HidHapticGamepadBaseTest, ZeroVibrationTest) {
EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
} }
TEST_F(HidHapticGamepadBaseTest, StartDelayTest) { TEST_F(HidHapticGamepadTest, StartDelayTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
// Issue PlayEffect with non-zero |start_delay|. // Issue PlayEffect with non-zero |start_delay|.
PostPlayEffect(kNonZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kNonZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Stop vibration for the delay period. // Stop vibration for the delay period.
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_)); testing::ElementsAre(stop_vibration_output_report_));
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u);
...@@ -227,7 +226,7 @@ TEST_F(HidHapticGamepadBaseTest, StartDelayTest) { ...@@ -227,7 +226,7 @@ TEST_F(HidHapticGamepadBaseTest, StartDelayTest) {
// Start vibration. // Start vibration.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_, testing::ElementsAre(stop_vibration_output_report_,
start_vibration_output_report_)); start_vibration_output_report_));
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
...@@ -236,7 +235,7 @@ TEST_F(HidHapticGamepadBaseTest, StartDelayTest) { ...@@ -236,7 +235,7 @@ TEST_F(HidHapticGamepadBaseTest, StartDelayTest) {
// Finish the effect. // Finish the effect.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_, testing::ElementsAre(stop_vibration_output_report_,
start_vibration_output_report_)); start_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
...@@ -245,20 +244,20 @@ TEST_F(HidHapticGamepadBaseTest, StartDelayTest) { ...@@ -245,20 +244,20 @@ TEST_F(HidHapticGamepadBaseTest, StartDelayTest) {
EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
} }
TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) { TEST_F(HidHapticGamepadTest, ZeroStartDelayPreemptionTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
EXPECT_EQ(0, second_callback_count_); EXPECT_EQ(0, second_callback_count_);
// Start an ongoing effect. We'll preempt this one with another effect. // Start an ongoing effect. We'll preempt this one with another effect.
PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Start a second effect with zero |start_delay|. This should cause the first // Start a second effect with zero |start_delay|. This should cause the first
// effect to be preempted before it calls SetVibration. // effect to be preempted before it calls SetVibration.
PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::SecondCallback, base::BindOnce(&HidHapticGamepadTest::SecondCallback,
base::Unretained(this))); base::Unretained(this)));
// Execute the pending tasks. // Execute the pending tasks.
...@@ -266,7 +265,7 @@ TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) { ...@@ -266,7 +265,7 @@ TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) {
// The first effect should have already returned with a "preempted" result // The first effect should have already returned with a "preempted" result
// without issuing a report. The second effect has started vibration. // without issuing a report. The second effect has started vibration.
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(start_vibration_output_report_)); testing::ElementsAre(start_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(0, second_callback_count_); EXPECT_EQ(0, second_callback_count_);
...@@ -277,7 +276,7 @@ TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) { ...@@ -277,7 +276,7 @@ TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) {
// Finish the effect. // Finish the effect.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(start_vibration_output_report_)); testing::ElementsAre(start_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(1, second_callback_count_); EXPECT_EQ(1, second_callback_count_);
...@@ -286,20 +285,20 @@ TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) { ...@@ -286,20 +285,20 @@ TEST_F(HidHapticGamepadBaseTest, ZeroStartDelayPreemptionTest) {
EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
} }
TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) { TEST_F(HidHapticGamepadTest, NonZeroStartDelayPreemptionTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
EXPECT_EQ(0, second_callback_count_); EXPECT_EQ(0, second_callback_count_);
// Start an ongoing effect. We'll preempt this one with another effect. // Start an ongoing effect. We'll preempt this one with another effect.
PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Start a second effect with non-zero |start_delay|. This should cause the // Start a second effect with non-zero |start_delay|. This should cause the
// first effect to be preempted before it calls SetVibration. // first effect to be preempted before it calls SetVibration.
PostPlayEffect(kNonZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kNonZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::SecondCallback, base::BindOnce(&HidHapticGamepadTest::SecondCallback,
base::Unretained(this))); base::Unretained(this)));
// Execute the pending tasks. // Execute the pending tasks.
...@@ -309,7 +308,7 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) { ...@@ -309,7 +308,7 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) {
// Because the second effect has a non-zero |start_delay| and is preempting // Because the second effect has a non-zero |start_delay| and is preempting
// another effect, it will call SetZeroVibration to ensure no vibration // another effect, it will call SetZeroVibration to ensure no vibration
// occurs during its |start_delay| period. // occurs during its |start_delay| period.
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_)); testing::ElementsAre(stop_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(0, second_callback_count_); EXPECT_EQ(0, second_callback_count_);
...@@ -320,7 +319,7 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) { ...@@ -320,7 +319,7 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) {
// Start vibration. // Start vibration.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_, testing::ElementsAre(stop_vibration_output_report_,
start_vibration_output_report_)); start_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
...@@ -330,7 +329,7 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) { ...@@ -330,7 +329,7 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) {
// Finish the effect. // Finish the effect.
task_environment_.FastForwardBy(kPendingTaskDuration); task_environment_.FastForwardBy(kPendingTaskDuration);
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_, testing::ElementsAre(stop_vibration_output_report_,
start_vibration_output_report_)); start_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
...@@ -340,25 +339,25 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) { ...@@ -340,25 +339,25 @@ TEST_F(HidHapticGamepadBaseTest, NonZeroStartDelayPreemptionTest) {
EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u); EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
} }
TEST_F(HidHapticGamepadBaseTest, ResetVibrationPreemptionTest) { TEST_F(HidHapticGamepadTest, ResetVibrationPreemptionTest) {
EXPECT_TRUE(gamepad_->output_reports_.empty()); EXPECT_TRUE(fake_hid_writer_->output_reports.empty());
EXPECT_EQ(0, first_callback_count_); EXPECT_EQ(0, first_callback_count_);
EXPECT_EQ(0, second_callback_count_); EXPECT_EQ(0, second_callback_count_);
// Start an ongoing effect. We'll preempt it with a reset. // Start an ongoing effect. We'll preempt it with a reset.
PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude, PostPlayEffect(kZeroStartDelayMillis, kStrongMagnitude, kWeakMagnitude,
base::BindOnce(&HidHapticGamepadBaseTest::FirstCallback, base::BindOnce(&HidHapticGamepadTest::FirstCallback,
base::Unretained(this))); base::Unretained(this)));
// Reset vibration. This should cause the effect to be preempted before it // Reset vibration. This should cause the effect to be preempted before it
// calls SetVibration. // calls SetVibration.
PostResetVibration(base::BindOnce(&HidHapticGamepadBaseTest::SecondCallback, PostResetVibration(base::BindOnce(&HidHapticGamepadTest::SecondCallback,
base::Unretained(this))); base::Unretained(this)));
// Execute the pending tasks. // Execute the pending tasks.
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
EXPECT_THAT(gamepad_->output_reports_, EXPECT_THAT(fake_hid_writer_->output_reports,
testing::ElementsAre(stop_vibration_output_report_)); testing::ElementsAre(stop_vibration_output_report_));
EXPECT_EQ(1, first_callback_count_); EXPECT_EQ(1, first_callback_count_);
EXPECT_EQ(1, second_callback_count_); EXPECT_EQ(1, second_callback_count_);
......
// Copyright 2018 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 "device/gamepad/hid_haptic_gamepad_win.h"
#include <Unknwn.h>
#include <WinDef.h>
#include <stdint.h>
#include <windows.h>
namespace device {
HidHapticGamepadWin::HidHapticGamepadWin(HANDLE device_handle,
const HapticReportData& data)
: HidHapticGamepadBase(data) {
// Fetch the size of the RIDI_DEVICENAME string.
UINT size;
UINT result = ::GetRawInputDeviceInfo(device_handle, RIDI_DEVICENAME,
/*pData=*/nullptr, &size);
if (result == 0U) {
// Read RIDI_DEVICENAME into a buffer.
std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
result = ::GetRawInputDeviceInfo(device_handle, RIDI_DEVICENAME,
name_buffer.get(), &size);
if (result == size) {
// Open the device handle for asynchronous I/O.
hid_handle_.Set(::CreateFile(
name_buffer.get(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
/*hTemplateFile=*/nullptr));
}
}
}
HidHapticGamepadWin::~HidHapticGamepadWin() = default;
// static
std::unique_ptr<HidHapticGamepadWin> HidHapticGamepadWin::Create(
uint16_t vendor_id,
uint16_t product_id,
HANDLE device_handle) {
const auto* haptic_data = GetHapticReportData(vendor_id, product_id);
if (!haptic_data)
return nullptr;
return std::make_unique<HidHapticGamepadWin>(device_handle, *haptic_data);
}
void HidHapticGamepadWin::DoShutdown() {
hid_handle_.Close();
}
size_t HidHapticGamepadWin::WriteOutputReport(
base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U);
if (!hid_handle_.IsValid())
return 0;
base::win::ScopedHandle event_handle(
::CreateEvent(/*lpEventAttributes=*/nullptr,
/*bManualReset=*/false,
/*bInitialState=*/false,
/*lpName=*/L""));
OVERLAPPED overlapped = {0};
overlapped.hEvent = event_handle.Get();
// Set up an asynchronous write.
DWORD bytes_written = 0;
BOOL write_success =
::WriteFile(hid_handle_.Get(), report.data(), report.size_bytes(),
&bytes_written, &overlapped);
if (!write_success) {
DWORD error = ::GetLastError();
if (error == ERROR_IO_PENDING) {
// Wait for the write to complete. This causes WriteOutputReport to behave
// synchronously.
DWORD wait_object = ::WaitForSingleObject(overlapped.hEvent,
/*dwMilliseconds=*/100);
if (wait_object == WAIT_OBJECT_0) {
::GetOverlappedResult(hid_handle_.Get(), &overlapped, &bytes_written,
/*bWait=*/true);
} else {
// Wait failed, or the timeout was exceeded before the write completed.
// Cancel the write request.
if (::CancelIo(hid_handle_.Get())) {
HANDLE handles[2];
handles[0] = hid_handle_.Get();
handles[1] = overlapped.hEvent;
::WaitForMultipleObjects(/*nCount=*/2, handles,
/*bWaitAll=*/false, INFINITE);
}
}
}
}
return write_success ? bytes_written : 0;
}
base::WeakPtr<AbstractHapticGamepad> HidHapticGamepadWin::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device
// Copyright 2018 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 DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_WIN_H_
#define DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_WIN_H_
#include "base/memory/weak_ptr.h"
#include "base/win/scoped_handle.h"
#include "device/gamepad/hid_haptic_gamepad_base.h"
#include <memory>
namespace device {
class HidHapticGamepadWin final : public HidHapticGamepadBase {
public:
HidHapticGamepadWin(HANDLE device_handle, const HapticReportData& data);
~HidHapticGamepadWin() override;
static std::unique_ptr<HidHapticGamepadWin> Create(uint16_t vendor_id,
uint16_t product_id,
HANDLE device_handle);
// AbstractHapticGamepad implementation.
void DoShutdown() override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// HidHapticGamepadBase implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override;
private:
base::win::ScopedHandle hid_handle_;
base::WeakPtrFactory<HidHapticGamepadWin> weak_factory_{this};
};
} // namespace device
#endif // DEVICE_GAMEPAD_HID_HAPTIC_GAMEPAD_WIN_H_
// Copyright 2019 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 DEVICE_GAMEPAD_HID_WRITER_H_
#define DEVICE_GAMEPAD_HID_WRITER_H_
#include <stddef.h>
#include <stdint.h>
#include "base/containers/span.h"
namespace device {
// HidWriter defines an interface for writing output reports to a single HID
// device.
class HidWriter {
public:
HidWriter() = default;
virtual ~HidWriter() = default;
// Platform implementation for writing an output report. |report| contains
// the data to be written, with the report ID (if present) as the first byte.
// Returns the number of bytes written, or zero on failure.
virtual size_t WriteOutputReport(base::span<const uint8_t> report) = 0;
};
} // namespace device
#endif // DEVICE_GAMEPAD_HID_WRITER_H_
// Copyright 2018 The Chromium Authors. All rights reserved. // Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "device/gamepad/dualshock4_controller_linux.h" #include "device/gamepad/hid_writer_linux.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
namespace device { namespace device {
Dualshock4ControllerLinux::Dualshock4ControllerLinux(const base::ScopedFD& fd) HidWriterLinux::HidWriterLinux(const base::ScopedFD& fd) : fd_(fd.get()) {}
: fd_(fd.get()) {}
Dualshock4ControllerLinux::~Dualshock4ControllerLinux() = default; HidWriterLinux::~HidWriterLinux() = default;
size_t Dualshock4ControllerLinux::WriteOutputReport( size_t HidWriterLinux::WriteOutputReport(base::span<const uint8_t> report) {
base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U);
ssize_t bytes_written = ssize_t bytes_written =
HANDLE_EINTR(write(fd_, report.data(), report.size_bytes())); HANDLE_EINTR(write(fd_, report.data(), report.size_bytes()));
return bytes_written < 0 ? 0 : static_cast<size_t>(bytes_written); return bytes_written < 0 ? 0 : static_cast<size_t>(bytes_written);
} }
base::WeakPtr<AbstractHapticGamepad> Dualshock4ControllerLinux::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device } // namespace device
// Copyright 2019 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 DEVICE_GAMEPAD_HID_WRITER_LINUX_H_
#define DEVICE_GAMEPAD_HID_WRITER_LINUX_H_
#include <stddef.h>
#include <stdint.h>
#include "base/containers/span.h"
#include "base/files/scoped_file.h"
#include "device/gamepad/hid_writer.h"
namespace device {
class HidWriterLinux final : public HidWriter {
public:
explicit HidWriterLinux(const base::ScopedFD& fd);
~HidWriterLinux() override;
// HidWriter implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override;
private:
// Not owned.
int fd_;
};
} // namespace device
#endif // DEVICE_GAMEPAD_HID_WRITER_LINUX_H_
// Copyright 2018 The Chromium Authors. All rights reserved. // Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "device/gamepad/dualshock4_controller_mac.h" #include "device/gamepad/hid_writer_mac.h"
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
namespace device { namespace device {
Dualshock4ControllerMac::Dualshock4ControllerMac(IOHIDDeviceRef device_ref) HidWriterMac::HidWriterMac(IOHIDDeviceRef device_ref)
: device_ref_(device_ref) {} : device_ref_(device_ref) {}
Dualshock4ControllerMac::~Dualshock4ControllerMac() = default; HidWriterMac::~HidWriterMac() = default;
void Dualshock4ControllerMac::DoShutdown() {
device_ref_ = nullptr;
}
size_t Dualshock4ControllerMac::WriteOutputReport(
base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U);
if (!device_ref_)
return 0;
size_t HidWriterMac::WriteOutputReport(base::span<const uint8_t> report) {
IOReturn success = IOReturn success =
IOHIDDeviceSetReport(device_ref_, kIOHIDReportTypeOutput, report[0], IOHIDDeviceSetReport(device_ref_, kIOHIDReportTypeOutput, report[0],
report.data(), report.size_bytes()); report.data(), report.size_bytes());
return (success == kIOReturnSuccess) ? report.size_bytes() : 0; return (success == kIOReturnSuccess) ? report.size_bytes() : 0;
} }
base::WeakPtr<AbstractHapticGamepad> Dualshock4ControllerMac::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device } // namespace device
// Copyright 2018 The Chromium Authors. All rights reserved. // Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_MAC_H_ #ifndef DEVICE_GAMEPAD_HID_WRITER_MAC_H_
#define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_MAC_H_ #define DEVICE_GAMEPAD_HID_WRITER_MAC_H_
#include <stddef.h>
#include <stdint.h>
#include <IOKit/hid/IOHIDManager.h> #include <IOKit/hid/IOHIDManager.h>
#include "base/memory/weak_ptr.h" #include "base/containers/span.h"
#include "device/gamepad/dualshock4_controller_base.h" #include "device/gamepad/hid_writer.h"
namespace device { namespace device {
class Dualshock4ControllerMac final : public Dualshock4ControllerBase { class HidWriterMac final : public HidWriter {
public: public:
Dualshock4ControllerMac(IOHIDDeviceRef device_ref); explicit HidWriterMac(IOHIDDeviceRef device_ref);
~Dualshock4ControllerMac() override; ~HidWriterMac() override;
// AbstractHapticGamepad implementation.
void DoShutdown() override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// Dualshock4ControllerBase implementation. // HidWriter implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override; size_t WriteOutputReport(base::span<const uint8_t> report) override;
private: private:
IOHIDDeviceRef device_ref_; IOHIDDeviceRef device_ref_;
base::WeakPtrFactory<Dualshock4ControllerMac> weak_factory_{this};
}; };
} // namespace device } // namespace device
#endif // DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_MAC_H_ #endif // DEVICE_GAMEPAD_HID_WRITER_MAC_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "device/gamepad/dualshock4_controller_win.h" #include "device/gamepad/hid_writer_win.h"
#include <Unknwn.h> #include <Unknwn.h>
#include <WinDef.h> #include <WinDef.h>
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
namespace device { namespace device {
Dualshock4ControllerWin::Dualshock4ControllerWin(HANDLE device_handle) { HidWriterWin::HidWriterWin(HANDLE device) {
UINT size; UINT size;
UINT result = UINT result =
::GetRawInputDeviceInfo(device_handle, RIDI_DEVICENAME, nullptr, &size); ::GetRawInputDeviceInfo(device, RIDI_DEVICENAME, nullptr, &size);
if (result == 0U) { if (result == 0U) {
std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]); std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
result = ::GetRawInputDeviceInfo(device_handle, RIDI_DEVICENAME, result = ::GetRawInputDeviceInfo(device, RIDI_DEVICENAME, name_buffer.get(),
name_buffer.get(), &size); &size);
if (result == size) { if (result == size) {
// Open the device handle for asynchronous I/O. // Open the device handle for asynchronous I/O.
hid_handle_.Set( hid_handle_.Set(
...@@ -29,14 +29,9 @@ Dualshock4ControllerWin::Dualshock4ControllerWin(HANDLE device_handle) { ...@@ -29,14 +29,9 @@ Dualshock4ControllerWin::Dualshock4ControllerWin(HANDLE device_handle) {
} }
} }
Dualshock4ControllerWin::~Dualshock4ControllerWin() = default; HidWriterWin::~HidWriterWin() = default;
void Dualshock4ControllerWin::DoShutdown() { size_t HidWriterWin::WriteOutputReport(base::span<const uint8_t> report) {
hid_handle_.Close();
}
size_t Dualshock4ControllerWin::WriteOutputReport(
base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U); DCHECK_GE(report.size_bytes(), 1U);
if (!hid_handle_.IsValid()) if (!hid_handle_.IsValid())
return 0; return 0;
...@@ -75,8 +70,4 @@ size_t Dualshock4ControllerWin::WriteOutputReport( ...@@ -75,8 +70,4 @@ size_t Dualshock4ControllerWin::WriteOutputReport(
return write_success ? bytes_written : 0; return write_success ? bytes_written : 0;
} }
base::WeakPtr<AbstractHapticGamepad> Dualshock4ControllerWin::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace device } // namespace device
// Copyright 2018 The Chromium Authors. All rights reserved. // Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_WIN_ #ifndef DEVICE_GAMEPAD_HID_WRITER_WIN_H_
#define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_WIN_ #define DEVICE_GAMEPAD_HID_WRITER_WIN_H_
#include "base/memory/weak_ptr.h" #include <stddef.h>
#include <stdint.h>
#include "base/containers/span.h"
#include "base/win/scoped_handle.h" #include "base/win/scoped_handle.h"
#include "device/gamepad/dualshock4_controller_base.h" #include "base/win/windows_types.h"
#include "device/gamepad/hid_writer.h"
namespace device { namespace device {
class Dualshock4ControllerWin final : public Dualshock4ControllerBase { class HidWriterWin final : public HidWriter {
public: public:
explicit Dualshock4ControllerWin(HANDLE device_handle); explicit HidWriterWin(HANDLE device);
~Dualshock4ControllerWin() override; ~HidWriterWin() override;
// AbstractHapticGamepad implementation.
void DoShutdown() override;
base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
// Dualshock4ControllerBase implementation. // HidWriter implementation.
size_t WriteOutputReport(base::span<const uint8_t> report) override; size_t WriteOutputReport(base::span<const uint8_t> report) override;
private: private:
base::win::ScopedHandle hid_handle_; base::win::ScopedHandle hid_handle_;
base::WeakPtrFactory<Dualshock4ControllerWin> weak_factory_{this};
}; };
} // namespace device } // namespace device
#endif // DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_WIN_ #endif // DEVICE_GAMEPAD_HID_WRITER_WIN_H_
...@@ -5,8 +5,11 @@ ...@@ -5,8 +5,11 @@
#include "raw_input_gamepad_device_win.h" #include "raw_input_gamepad_device_win.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "device/gamepad/dualshock4_controller.h"
#include "device/gamepad/gamepad_blocklist.h" #include "device/gamepad/gamepad_blocklist.h"
#include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/hid_haptic_gamepad.h"
#include "device/gamepad/hid_writer_win.h"
namespace device { namespace device {
...@@ -68,11 +71,12 @@ RawInputGamepadDeviceWin::RawInputGamepadDeviceWin( ...@@ -68,11 +71,12 @@ RawInputGamepadDeviceWin::RawInputGamepadDeviceWin(
is_valid_ = QueryDeviceInfo(); is_valid_ = QueryDeviceInfo();
if (is_valid_) { if (is_valid_) {
if (Dualshock4ControllerWin::IsDualshock4(vendor_id_, product_id_)) { if (Dualshock4Controller::IsDualshock4(vendor_id_, product_id_)) {
dualshock4_ = std::make_unique<Dualshock4ControllerWin>(handle_); dualshock4_ = std::make_unique<Dualshock4Controller>(
} else if (HidHapticGamepadWin::IsHidHaptic(vendor_id_, product_id_)) { std::make_unique<HidWriterWin>(handle_));
hid_haptics_ = } else if (HidHapticGamepad::IsHidHaptic(vendor_id_, product_id_)) {
HidHapticGamepadWin::Create(vendor_id_, product_id_, handle_); hid_haptics_ = HidHapticGamepad::Create(
vendor_id_, product_id_, std::make_unique<HidWriterWin>(handle_));
} }
} }
} }
......
...@@ -17,13 +17,14 @@ ...@@ -17,13 +17,14 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "device/gamepad/abstract_haptic_gamepad.h" #include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/dualshock4_controller_win.h"
#include "device/gamepad/hid_dll_functions_win.h" #include "device/gamepad/hid_dll_functions_win.h"
#include "device/gamepad/hid_haptic_gamepad_win.h"
#include "device/gamepad/public/cpp/gamepad.h" #include "device/gamepad/public/cpp/gamepad.h"
namespace device { namespace device {
class HidHapticGamepad;
class Dualshock4Controller;
class RawInputGamepadDeviceWin final : public AbstractHapticGamepad { class RawInputGamepadDeviceWin final : public AbstractHapticGamepad {
public: public:
// Relevant usage IDs within the Generic Desktop usage page. RawInput gamepads // Relevant usage IDs within the Generic Desktop usage page. RawInput gamepads
...@@ -138,10 +139,10 @@ class RawInputGamepadDeviceWin final : public AbstractHapticGamepad { ...@@ -138,10 +139,10 @@ class RawInputGamepadDeviceWin final : public AbstractHapticGamepad {
PHIDP_PREPARSED_DATA preparsed_data_ = nullptr; PHIDP_PREPARSED_DATA preparsed_data_ = nullptr;
// Dualshock4-specific functionality (e.g., haptics), if available. // Dualshock4-specific functionality (e.g., haptics), if available.
std::unique_ptr<Dualshock4ControllerWin> dualshock4_; std::unique_ptr<Dualshock4Controller> dualshock4_;
// A controller that uses a HID output report for vibration effects. // A controller that uses a HID output report for vibration effects.
std::unique_ptr<HidHapticGamepadWin> hid_haptics_; std::unique_ptr<HidHapticGamepad> hid_haptics_;
base::WeakPtrFactory<RawInputGamepadDeviceWin> weak_factory_{this}; base::WeakPtrFactory<RawInputGamepadDeviceWin> weak_factory_{this};
}; };
......
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