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") {
"dualshock4_controller_base.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",
......
......@@ -17,6 +17,7 @@ AbstractHapticGamepad::~AbstractHapticGamepad() {
void AbstractHapticGamepad::Shutdown() {
if (playing_effect_callback_) {
sequence_id_++;
SetZeroVibration();
RunCallbackOnMojoThread(
mojom::GamepadHapticsResult::GamepadHapticsResultPreempted);
......@@ -95,7 +96,7 @@ void AbstractHapticGamepad::StartVibration(int sequence_id,
double duration,
double strong_magnitude,
double weak_magnitude) {
if (sequence_id != sequence_id_)
if (is_shut_down_ || sequence_id != sequence_id_)
return;
SetVibration(strong_magnitude, weak_magnitude);
......@@ -107,7 +108,7 @@ void AbstractHapticGamepad::StartVibration(int sequence_id,
}
void AbstractHapticGamepad::StopVibration(int sequence_id) {
if (sequence_id != sequence_id_)
if (is_shut_down_ || sequence_id != sequence_id_)
return;
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 @@
#include <stddef.h>
#include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/dualshock4_controller_mac.h"
#include "device/gamepad/public/cpp/gamepad.h"
namespace device {
......@@ -18,9 +19,15 @@ namespace device {
// GamepadDeviceMac represents a single gamepad device. Gamepad enumeration
// and state polling is handled through the raw HID interface, while haptics
// 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 {
public:
GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref);
GamepadDeviceMac(int location_id,
IOHIDDeviceRef device_ref,
int vendor_id,
int product_id);
~GamepadDeviceMac() override;
// Initialize |gamepad| with the number of buttons and axes described in the
......@@ -39,7 +46,7 @@ class GamepadDeviceMac : public AbstractHapticGamepad {
// Return true if this device supports force feedback through the
// ForceFeedback framework.
bool SupportsForceFeedback() { return ff_device_ref_ != nullptr; }
bool SupportsVibration();
// Starts vibrating the device with the specified magnitudes.
void SetVibration(double strong_magnitude, double weak_magnitude) override;
......@@ -89,6 +96,9 @@ class GamepadDeviceMac : public AbstractHapticGamepad {
LONG force_data_[2];
DWORD axes_data_[2];
LONG direction_data_[2];
// Dualshock4 functionality, if available.
std::unique_ptr<Dualshock4ControllerMac> dualshock4_;
};
} // namespace device
......
......@@ -42,7 +42,10 @@ float NormalizeUInt32Axis(uint32_t value, uint32_t min, uint32_t max) {
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),
device_ref_(device_ref),
ff_device_ref_(nullptr),
......@@ -55,9 +58,11 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref)
std::fill(axis_report_sizes_, axis_report_sizes_ + Gamepad::kAxesLengthCap,
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);
if (ff_device_ref_ != nullptr) {
if (ff_device_ref_) {
ff_effect_ref_ = CreateForceFeedbackEffect(ff_device_ref_, &ff_effect_,
&ff_custom_force_, force_data_,
axes_data_, direction_data_);
......@@ -68,11 +73,16 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id, IOHIDDeviceRef device_ref)
GamepadDeviceMac::~GamepadDeviceMac() = default;
void GamepadDeviceMac::DoShutdown() {
SetZeroVibration();
if (ff_device_ref_ != nullptr) {
if (ff_effect_ref_ != nullptr)
if (ff_device_ref_) {
if (ff_effect_ref_)
FFDeviceReleaseEffect(ff_device_ref_, ff_effect_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,
}
}
bool GamepadDeviceMac::SupportsVibration() {
return dualshock4_ || ff_device_ref_;
}
void GamepadDeviceMac::SetVibration(double strong_magnitude,
double weak_magnitude) {
if (dualshock4_) {
dualshock4_->SetVibration(strong_magnitude, weak_magnitude);
} else if (ff_device_ref_) {
FFCUSTOMFORCE* ff_custom_force =
static_cast<FFCUSTOMFORCE*>(ff_effect_.lpvTypeSpecificParams);
DCHECK(ff_custom_force);
......@@ -258,10 +275,15 @@ void GamepadDeviceMac::SetVibration(double strong_magnitude,
FFEP_DURATION | FFEP_STARTDELAY | FFEP_TYPESPECIFICPARAMS);
if (res == FF_OK)
FFEffectStart(ff_effect_ref_, 1, FFES_SOLO);
}
}
void GamepadDeviceMac::SetZeroVibration() {
if (dualshock4_) {
dualshock4_->SetZeroVibration();
} else if (ff_effect_ref_) {
FFEffectStop(ff_effect_ref_);
}
}
// static
......
......@@ -227,7 +227,8 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) {
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)) {
devices_[slot]->Shutdown();
devices_[slot] = nullptr;
......@@ -235,8 +236,7 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) {
}
state->data.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
state->data.vibration_actuator.not_null =
devices_[slot]->SupportsForceFeedback();
state->data.vibration_actuator.not_null = devices_[slot]->SupportsVibration();
state->data.connected = true;
}
......@@ -286,7 +286,7 @@ void GamepadPlatformDataFetcherMac::GetGamepadData(bool) {
// Loop through and GetPadState to indicate the devices are still connected.
for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) {
if (devices_[slot] != nullptr)
if (devices_[slot])
GetPadState(devices_[slot]->GetLocationId());
}
}
......@@ -298,9 +298,11 @@ void GamepadPlatformDataFetcherMac::PlayEffect(
mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) {
size_t slot = GetSlotForLocation(source_id);
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(
mojom::GamepadHapticsResult::GamepadHapticsResultError);
mojom::GamepadHapticsResult::GamepadHapticsResultPreempted);
return;
}
devices_[slot]->PlayEffect(type, std::move(params), std::move(callback));
......@@ -311,9 +313,10 @@ void GamepadPlatformDataFetcherMac::ResetVibration(
mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) {
size_t slot = GetSlotForLocation(source_id);
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(
mojom::GamepadHapticsResult::GamepadHapticsResultError);
mojom::GamepadHapticsResult::GamepadHapticsResultComplete);
return;
}
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