Commit 6455fb01 authored by Matt Reynolds's avatar Matt Reynolds Committed by Commit Bot

Add haptics for Dualshock4 gamepads on Mac

BUG=749295

Change-Id: I916483dfc21b2523c99f8507917cf05ae42ca00a
Reviewed-on: https://chromium-review.googlesource.com/961546Reviewed-by: default avatarBrandon Jones <bajones@chromium.org>
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543221}
parent ade8cb76
...@@ -19,6 +19,8 @@ component("gamepad") { ...@@ -19,6 +19,8 @@ component("gamepad") {
"dualshock4_controller_base.h", "dualshock4_controller_base.h",
"dualshock4_controller_linux.cc", "dualshock4_controller_linux.cc",
"dualshock4_controller_linux.h", "dualshock4_controller_linux.h",
"dualshock4_controller_mac.cc",
"dualshock4_controller_mac.h",
"dualshock4_controller_win.cc", "dualshock4_controller_win.cc",
"dualshock4_controller_win.h", "dualshock4_controller_win.h",
"game_controller_data_fetcher_mac.h", "game_controller_data_fetcher_mac.h",
......
...@@ -17,6 +17,7 @@ AbstractHapticGamepad::~AbstractHapticGamepad() { ...@@ -17,6 +17,7 @@ AbstractHapticGamepad::~AbstractHapticGamepad() {
void AbstractHapticGamepad::Shutdown() { void AbstractHapticGamepad::Shutdown() {
if (playing_effect_callback_) { if (playing_effect_callback_) {
sequence_id_++;
SetZeroVibration(); SetZeroVibration();
RunCallbackOnMojoThread( RunCallbackOnMojoThread(
mojom::GamepadHapticsResult::GamepadHapticsResultPreempted); mojom::GamepadHapticsResult::GamepadHapticsResultPreempted);
...@@ -95,7 +96,7 @@ void AbstractHapticGamepad::StartVibration(int sequence_id, ...@@ -95,7 +96,7 @@ void AbstractHapticGamepad::StartVibration(int sequence_id,
double duration, double duration,
double strong_magnitude, double strong_magnitude,
double weak_magnitude) { double weak_magnitude) {
if (sequence_id != sequence_id_) if (is_shut_down_ || sequence_id != sequence_id_)
return; return;
SetVibration(strong_magnitude, weak_magnitude); SetVibration(strong_magnitude, weak_magnitude);
...@@ -107,7 +108,7 @@ void AbstractHapticGamepad::StartVibration(int sequence_id, ...@@ -107,7 +108,7 @@ void AbstractHapticGamepad::StartVibration(int sequence_id,
} }
void AbstractHapticGamepad::StopVibration(int sequence_id) { void AbstractHapticGamepad::StopVibration(int sequence_id) {
if (sequence_id != sequence_id_) if (is_shut_down_ || sequence_id != sequence_id_)
return; return;
SetZeroVibration(); SetZeroVibration();
......
// 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/dualshock4_controller_mac.h"
#include <CoreFoundation/CoreFoundation.h>
namespace device {
Dualshock4ControllerMac::Dualshock4ControllerMac(IOHIDDeviceRef device_ref)
: device_ref_(device_ref) {}
Dualshock4ControllerMac::~Dualshock4ControllerMac() = default;
void Dualshock4ControllerMac::DoShutdown() {
device_ref_ = nullptr;
}
size_t Dualshock4ControllerMac::WriteOutputReport(void* report,
size_t report_length) {
if (!device_ref_)
return 0;
const unsigned char* report_data = static_cast<unsigned char*>(report);
IOReturn success =
IOHIDDeviceSetReport(device_ref_, kIOHIDReportTypeOutput, report_data[0],
report_data, report_length);
return (success == kIOReturnSuccess) ? report_length : 0;
}
} // 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_DUALSHOCK4_CONTROLLER_MAC_H_
#define DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_MAC_H_
#include "device/gamepad/dualshock4_controller_base.h"
#include <IOKit/hid/IOHIDManager.h>
namespace device {
class Dualshock4ControllerMac : public Dualshock4ControllerBase {
public:
Dualshock4ControllerMac(IOHIDDeviceRef device_ref);
~Dualshock4ControllerMac() override;
void DoShutdown() override;
size_t WriteOutputReport(void* report, size_t report_length) override;
private:
IOHIDDeviceRef device_ref_;
};
} // namespace device
#endif // DEVICE_GAMEPAD_DUALSHOCK4_CONTROLLER_MAC_H_
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <stddef.h> #include <stddef.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/public/cpp/gamepad.h" #include "device/gamepad/public/cpp/gamepad.h"
namespace device { namespace device {
...@@ -18,9 +19,15 @@ namespace device { ...@@ -18,9 +19,15 @@ namespace device {
// 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.
//
// Dualshock4 haptics are not supported through ForceFeedback and are instead
// sent through the raw HID interface.
class GamepadDeviceMac : public AbstractHapticGamepad { class GamepadDeviceMac : public AbstractHapticGamepad {
public: public:
GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref); GamepadDeviceMac(int location_id,
IOHIDDeviceRef device_ref,
int vendor_id,
int product_id);
~GamepadDeviceMac() override; ~GamepadDeviceMac() override;
// Initialize |gamepad| with the number of buttons and axes described in the // Initialize |gamepad| with the number of buttons and axes described in the
...@@ -39,7 +46,7 @@ class GamepadDeviceMac : public AbstractHapticGamepad { ...@@ -39,7 +46,7 @@ class GamepadDeviceMac : public AbstractHapticGamepad {
// Return true if this device supports force feedback through the // Return true if this device supports force feedback through the
// ForceFeedback framework. // ForceFeedback framework.
bool SupportsForceFeedback() { return ff_device_ref_ != nullptr; } bool SupportsVibration();
// Starts vibrating the device with the specified magnitudes. // Starts vibrating the device with the specified magnitudes.
void SetVibration(double strong_magnitude, double weak_magnitude) override; void SetVibration(double strong_magnitude, double weak_magnitude) override;
...@@ -89,6 +96,9 @@ class GamepadDeviceMac : public AbstractHapticGamepad { ...@@ -89,6 +96,9 @@ class GamepadDeviceMac : public AbstractHapticGamepad {
LONG force_data_[2]; LONG force_data_[2];
DWORD axes_data_[2]; DWORD axes_data_[2];
LONG direction_data_[2]; LONG direction_data_[2];
// Dualshock4 functionality, if available.
std::unique_ptr<Dualshock4ControllerMac> dualshock4_;
}; };
} // namespace device } // namespace device
......
...@@ -42,7 +42,10 @@ float NormalizeUInt32Axis(uint32_t value, uint32_t min, uint32_t max) { ...@@ -42,7 +42,10 @@ float NormalizeUInt32Axis(uint32_t value, uint32_t min, uint32_t max) {
namespace device { namespace device {
GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref) GamepadDeviceMac::GamepadDeviceMac(int location_id,
IOHIDDeviceRef device_ref,
int vendor_id,
int product_id)
: location_id_(location_id), : location_id_(location_id),
device_ref_(device_ref), device_ref_(device_ref),
ff_device_ref_(nullptr), ff_device_ref_(nullptr),
...@@ -55,9 +58,11 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref) ...@@ -55,9 +58,11 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref)
std::fill(axis_report_sizes_, axis_report_sizes_ + Gamepad::kAxesLengthCap, std::fill(axis_report_sizes_, axis_report_sizes_ + Gamepad::kAxesLengthCap,
0); 0);
if (device_ref != nullptr) { if (Dualshock4ControllerMac::IsDualshock4(vendor_id, product_id)) {
dualshock4_ = std::make_unique<Dualshock4ControllerMac>(device_ref);
} else if (device_ref) {
ff_device_ref_ = CreateForceFeedbackDevice(device_ref); ff_device_ref_ = CreateForceFeedbackDevice(device_ref);
if (ff_device_ref_ != nullptr) { if (ff_device_ref_) {
ff_effect_ref_ = CreateForceFeedbackEffect(ff_device_ref_, &ff_effect_, ff_effect_ref_ = CreateForceFeedbackEffect(ff_device_ref_, &ff_effect_,
&ff_custom_force_, force_data_, &ff_custom_force_, force_data_,
axes_data_, direction_data_); axes_data_, direction_data_);
...@@ -68,11 +73,16 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref) ...@@ -68,11 +73,16 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref)
GamepadDeviceMac::~GamepadDeviceMac() = default; GamepadDeviceMac::~GamepadDeviceMac() = default;
void GamepadDeviceMac::DoShutdown() { void GamepadDeviceMac::DoShutdown() {
SetZeroVibration(); if (ff_device_ref_) {
if (ff_device_ref_ != nullptr) { if (ff_effect_ref_)
if (ff_effect_ref_ != nullptr)
FFDeviceReleaseEffect(ff_device_ref_, ff_effect_ref_); FFDeviceReleaseEffect(ff_device_ref_, ff_effect_ref_);
FFReleaseDevice(ff_device_ref_); FFReleaseDevice(ff_device_ref_);
ff_effect_ref_ = nullptr;
ff_device_ref_ = nullptr;
}
if (dualshock4_) {
dualshock4_->Shutdown();
dualshock4_ = nullptr;
} }
} }
...@@ -240,8 +250,15 @@ void GamepadDeviceMac::UpdateGamepadForValue(IOHIDValueRef value, ...@@ -240,8 +250,15 @@ void GamepadDeviceMac::UpdateGamepadForValue(IOHIDValueRef value,
} }
} }
bool GamepadDeviceMac::SupportsVibration() {
return dualshock4_ || ff_device_ref_;
}
void GamepadDeviceMac::SetVibration(double strong_magnitude, void GamepadDeviceMac::SetVibration(double strong_magnitude,
double weak_magnitude) { double weak_magnitude) {
if (dualshock4_) {
dualshock4_->SetVibration(strong_magnitude, weak_magnitude);
} else if (ff_device_ref_) {
FFCUSTOMFORCE* ff_custom_force = FFCUSTOMFORCE* ff_custom_force =
static_cast<FFCUSTOMFORCE*>(ff_effect_.lpvTypeSpecificParams); static_cast<FFCUSTOMFORCE*>(ff_effect_.lpvTypeSpecificParams);
DCHECK(ff_custom_force); DCHECK(ff_custom_force);
...@@ -258,10 +275,15 @@ void GamepadDeviceMac::SetVibration(double strong_magnitude, ...@@ -258,10 +275,15 @@ void GamepadDeviceMac::SetVibration(double strong_magnitude,
FFEP_DURATION | FFEP_STARTDELAY | FFEP_TYPESPECIFICPARAMS); FFEP_DURATION | FFEP_STARTDELAY | FFEP_TYPESPECIFICPARAMS);
if (res == FF_OK) if (res == FF_OK)
FFEffectStart(ff_effect_ref_, 1, FFES_SOLO); FFEffectStart(ff_effect_ref_, 1, FFES_SOLO);
}
} }
void GamepadDeviceMac::SetZeroVibration() { void GamepadDeviceMac::SetZeroVibration() {
if (dualshock4_) {
dualshock4_->SetZeroVibration();
} else if (ff_effect_ref_) {
FFEffectStop(ff_effect_ref_); FFEffectStop(ff_effect_ref_);
}
} }
// static // static
......
...@@ -227,7 +227,8 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { ...@@ -227,7 +227,8 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) {
state->data.mapping[0] = 0; state->data.mapping[0] = 0;
} }
devices_[slot] = std::make_unique<GamepadDeviceMac>(location_int, device); devices_[slot] = std::make_unique<GamepadDeviceMac>(location_int, device,
vendor_int, product_int);
if (!devices_[slot]->AddButtonsAndAxes(&state->data)) { if (!devices_[slot]->AddButtonsAndAxes(&state->data)) {
devices_[slot]->Shutdown(); devices_[slot]->Shutdown();
devices_[slot] = nullptr; devices_[slot] = nullptr;
...@@ -235,8 +236,7 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { ...@@ -235,8 +236,7 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) {
} }
state->data.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble; state->data.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
state->data.vibration_actuator.not_null = state->data.vibration_actuator.not_null = devices_[slot]->SupportsVibration();
devices_[slot]->SupportsForceFeedback();
state->data.connected = true; state->data.connected = true;
} }
...@@ -286,7 +286,7 @@ void GamepadPlatformDataFetcherMac::GetGamepadData(bool) { ...@@ -286,7 +286,7 @@ void GamepadPlatformDataFetcherMac::GetGamepadData(bool) {
// Loop through and GetPadState to indicate the devices are still connected. // Loop through and GetPadState to indicate the devices are still connected.
for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) {
if (devices_[slot] != nullptr) if (devices_[slot])
GetPadState(devices_[slot]->GetLocationId()); GetPadState(devices_[slot]->GetLocationId());
} }
} }
...@@ -298,9 +298,11 @@ void GamepadPlatformDataFetcherMac::PlayEffect( ...@@ -298,9 +298,11 @@ void GamepadPlatformDataFetcherMac::PlayEffect(
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) { mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) {
size_t slot = GetSlotForLocation(source_id); size_t slot = GetSlotForLocation(source_id);
if (slot == Gamepads::kItemsLengthCap) { if (slot == Gamepads::kItemsLengthCap) {
// No connected gamepad with this location. // No connected gamepad with this location. Probably the effect was issued
// while the gamepad was still connected, so handle this as if it were
// preempted by a disconnect.
std::move(callback).Run( std::move(callback).Run(
mojom::GamepadHapticsResult::GamepadHapticsResultError); mojom::GamepadHapticsResult::GamepadHapticsResultPreempted);
return; return;
} }
devices_[slot]->PlayEffect(type, std::move(params), std::move(callback)); devices_[slot]->PlayEffect(type, std::move(params), std::move(callback));
...@@ -311,9 +313,10 @@ void GamepadPlatformDataFetcherMac::ResetVibration( ...@@ -311,9 +313,10 @@ void GamepadPlatformDataFetcherMac::ResetVibration(
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) { mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) {
size_t slot = GetSlotForLocation(source_id); size_t slot = GetSlotForLocation(source_id);
if (slot == Gamepads::kItemsLengthCap) { if (slot == Gamepads::kItemsLengthCap) {
// No connected gamepad with this location. // No connected gamepad with this location. Since the gamepad is already
// disconnected, allow the reset to report success.
std::move(callback).Run( std::move(callback).Run(
mojom::GamepadHapticsResult::GamepadHapticsResultError); mojom::GamepadHapticsResult::GamepadHapticsResultComplete);
return; return;
} }
devices_[slot]->ResetVibration(std::move(callback)); devices_[slot]->ResetVibration(std::move(callback));
......
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