InputManager: Add ForceFeedbackDevice interface
This commit is contained in:
parent
d7d028ac5c
commit
f9c125c1a1
|
@ -1651,7 +1651,7 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn
|
||||||
if (!visible)
|
if (!visible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (oneline)
|
if (oneline && type != InputBindingInfo::Type::Pointer && type != InputBindingInfo::Type::Device)
|
||||||
InputManager::PrettifyInputBinding(value);
|
InputManager::PrettifyInputBinding(value);
|
||||||
|
|
||||||
if (show_type)
|
if (show_type)
|
||||||
|
@ -1677,6 +1677,9 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn
|
||||||
case InputBindingInfo::Type::Macro:
|
case InputBindingInfo::Type::Macro:
|
||||||
title.format(ICON_FA_PIZZA_SLICE " {}", display_name);
|
title.format(ICON_FA_PIZZA_SLICE " {}", display_name);
|
||||||
break;
|
break;
|
||||||
|
case InputBindingInfo::Type::Device:
|
||||||
|
title.format(ICON_FA_GAMEPAD " {}", display_name);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
title = display_name;
|
title = display_name;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct InputBindingInfo
|
||||||
Motor,
|
Motor,
|
||||||
Pointer, // Absolute pointer, does not receive any events, but is queryable.
|
Pointer, // Absolute pointer, does not receive any events, but is queryable.
|
||||||
RelativePointer, // Receive relative mouse movement events, bind_index is offset by the axis.
|
RelativePointer, // Receive relative mouse movement events, bind_index is offset by the axis.
|
||||||
|
Device, // Used for special-purpose device selection, e.g. force feedback.
|
||||||
Macro,
|
Macro,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -405,7 +405,8 @@ void ControllerBindingWidget::createBindingWidgets(QWidget* parent)
|
||||||
for (const Controller::ControllerBindingInfo& bi : m_controller_info->bindings)
|
for (const Controller::ControllerBindingInfo& bi : m_controller_info->bindings)
|
||||||
{
|
{
|
||||||
if (bi.type == InputBindingInfo::Type::Axis || bi.type == InputBindingInfo::Type::HalfAxis ||
|
if (bi.type == InputBindingInfo::Type::Axis || bi.type == InputBindingInfo::Type::HalfAxis ||
|
||||||
bi.type == InputBindingInfo::Type::Pointer || bi.type == InputBindingInfo::Type::RelativePointer)
|
bi.type == InputBindingInfo::Type::Pointer || bi.type == InputBindingInfo::Type::RelativePointer ||
|
||||||
|
bi.type == InputBindingInfo::Type::Device)
|
||||||
{
|
{
|
||||||
if (!axis_gbox)
|
if (!axis_gbox)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "platform_misc.h"
|
#include "platform_misc.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
@ -338,6 +339,11 @@ void DInputSource::UpdateMotorState(InputBindingKey large_key, InputBindingKey s
|
||||||
// not supported
|
// not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DInputSource::ContainsDevice(std::string_view device) const
|
||||||
|
{
|
||||||
|
return device.starts_with("DInput-");
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<InputBindingKey> DInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
std::optional<InputBindingKey> DInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
||||||
{
|
{
|
||||||
if (!device.starts_with("DInput-") || binding.empty())
|
if (!device.starts_with("DInput-") || binding.empty())
|
||||||
|
@ -444,6 +450,12 @@ TinyString DInputSource::ConvertKeyToIcon(InputBindingKey key)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> DInputSource::CreateForceFeedbackDevice(std::string_view device, Error* error)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Not supported on this input source.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void DInputSource::CheckForStateChanges(size_t index, const DIJOYSTATE& new_state)
|
void DInputSource::CheckForStateChanges(size_t index, const DIJOYSTATE& new_state)
|
||||||
{
|
{
|
||||||
ControllerData& cd = m_controllers[index];
|
ControllerData& cd = m_controllers[index];
|
||||||
|
|
|
@ -46,10 +46,13 @@ public:
|
||||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||||
float small_intensity) override;
|
float small_intensity) override;
|
||||||
|
|
||||||
|
bool ContainsDevice(std::string_view device) const override;
|
||||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||||
TinyString ConvertKeyToString(InputBindingKey key) override;
|
TinyString ConvertKeyToString(InputBindingKey key) override;
|
||||||
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
#include "imgui_manager.h"
|
||||||
|
#include "input_source.h"
|
||||||
|
|
||||||
|
#include "core/controller.h"
|
||||||
|
#include "core/host.h"
|
||||||
|
#include "core/system.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/path.h"
|
#include "common/path.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "core/controller.h"
|
|
||||||
#include "core/host.h"
|
|
||||||
#include "core/system.h"
|
|
||||||
#include "imgui_manager.h"
|
|
||||||
#include "input_source.h"
|
|
||||||
|
|
||||||
#include "IconsPromptFont.h"
|
#include "IconsPromptFont.h"
|
||||||
|
|
||||||
|
@ -303,7 +306,8 @@ bool InputManager::ParseBindingAndGetSource(std::string_view binding, InputBindi
|
||||||
|
|
||||||
std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key)
|
std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key)
|
||||||
{
|
{
|
||||||
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::RelativePointer)
|
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::RelativePointer ||
|
||||||
|
binding_type == InputBindingInfo::Type::Device)
|
||||||
{
|
{
|
||||||
// pointer and device bindings don't have a data part
|
// pointer and device bindings don't have a data part
|
||||||
if (key.source_type == InputSourceType::Pointer)
|
if (key.source_type == InputSourceType::Pointer)
|
||||||
|
@ -356,7 +360,8 @@ std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type
|
||||||
const InputBindingKey* keys, size_t num_keys)
|
const InputBindingKey* keys, size_t num_keys)
|
||||||
{
|
{
|
||||||
// can't have a chord of devices/pointers
|
// can't have a chord of devices/pointers
|
||||||
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Pointer)
|
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::RelativePointer ||
|
||||||
|
binding_type == InputBindingInfo::Type::Device)
|
||||||
{
|
{
|
||||||
// so only take the first
|
// so only take the first
|
||||||
if (num_keys > 0)
|
if (num_keys > 0)
|
||||||
|
@ -888,6 +893,8 @@ void InputManager::AddPadBindings(const SettingsInterface& si, const std::string
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InputBindingInfo::Type::Pointer:
|
case InputBindingInfo::Type::Pointer:
|
||||||
|
case InputBindingInfo::Type::Device:
|
||||||
|
// handled in device
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1583,6 +1590,19 @@ void InputManager::OnInputDeviceDisconnected(InputBindingKey key, std::string_vi
|
||||||
Host::OnInputDeviceDisconnected(key, identifier);
|
Host::OnInputDeviceDisconnected(key, identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> InputManager::CreateForceFeedbackDevice(const std::string_view device,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++)
|
||||||
|
{
|
||||||
|
if (s_input_sources[i] && s_input_sources[i]->ContainsDevice(device))
|
||||||
|
return s_input_sources[i]->CreateForceFeedbackDevice(device, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::SetStringFmt(error, "No input source matched device '{}'", device);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Vibration
|
// Vibration
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -2104,3 +2124,7 @@ void InputManager::ReloadSources(const SettingsInterface& si, std::unique_lock<s
|
||||||
|
|
||||||
UpdatePointerCount();
|
UpdatePointerCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForceFeedbackDevice::~ForceFeedbackDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "core/input_types.h"
|
#include "core/input_types.h"
|
||||||
#include "window_info.h"
|
#include "window_info.h"
|
||||||
|
|
||||||
|
class Error;
|
||||||
class SmallStringBase;
|
class SmallStringBase;
|
||||||
|
|
||||||
/// Class, or source of an input event.
|
/// Class, or source of an input event.
|
||||||
|
@ -170,6 +171,22 @@ enum class InputPointerAxis : u8
|
||||||
/// External input source class.
|
/// External input source class.
|
||||||
class InputSource;
|
class InputSource;
|
||||||
|
|
||||||
|
/// Force feedback interface.
|
||||||
|
class ForceFeedbackDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Effect
|
||||||
|
{
|
||||||
|
Constant,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~ForceFeedbackDevice();
|
||||||
|
|
||||||
|
virtual void SetConstantForce(s32 level) = 0;
|
||||||
|
|
||||||
|
virtual void DisableForce(Effect force) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
namespace InputManager {
|
namespace InputManager {
|
||||||
/// Minimum interval between vibration updates when the effect is continuous.
|
/// Minimum interval between vibration updates when the effect is continuous.
|
||||||
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
|
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
|
||||||
|
@ -360,6 +377,9 @@ void OnInputDeviceConnected(std::string_view identifier, std::string_view device
|
||||||
|
|
||||||
/// Called when an input device is disconnected.
|
/// Called when an input device is disconnected.
|
||||||
void OnInputDeviceDisconnected(InputBindingKey key, std::string_view identifier);
|
void OnInputDeviceDisconnected(InputBindingKey key, std::string_view identifier);
|
||||||
|
|
||||||
|
/// Creates a force feedback device interface for the specified source and device.
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(const std::string_view device, Error* error = nullptr);
|
||||||
} // namespace InputManager
|
} // namespace InputManager
|
||||||
|
|
||||||
namespace Host {
|
namespace Host {
|
||||||
|
|
|
@ -14,8 +14,11 @@
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
|
class Error;
|
||||||
class SettingsInterface;
|
class SettingsInterface;
|
||||||
|
|
||||||
|
class ForceFeedbackDevice;
|
||||||
|
|
||||||
class InputSource
|
class InputSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -29,6 +32,7 @@ public:
|
||||||
|
|
||||||
virtual void PollEvents() = 0;
|
virtual void PollEvents() = 0;
|
||||||
|
|
||||||
|
virtual bool ContainsDevice(std::string_view device) const = 0;
|
||||||
virtual std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) = 0;
|
virtual std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) = 0;
|
||||||
virtual TinyString ConvertKeyToString(InputBindingKey key) = 0;
|
virtual TinyString ConvertKeyToString(InputBindingKey key) = 0;
|
||||||
virtual TinyString ConvertKeyToIcon(InputBindingKey key) = 0;
|
virtual TinyString ConvertKeyToIcon(InputBindingKey key) = 0;
|
||||||
|
@ -50,6 +54,9 @@ public:
|
||||||
virtual void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
virtual void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||||
float small_intensity);
|
float small_intensity);
|
||||||
|
|
||||||
|
/// Creates a force-feedback device from this source.
|
||||||
|
virtual std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) = 0;
|
||||||
|
|
||||||
/// Creates a key for a generic controller axis event.
|
/// Creates a key for a generic controller axis event.
|
||||||
static InputBindingKey MakeGenericControllerAxisKey(InputSourceType clazz, u32 controller_index, s32 axis_index);
|
static InputBindingKey MakeGenericControllerAxisKey(InputSourceType clazz, u32 controller_index, s32 axis_index);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bitutils.h"
|
#include "common/bitutils.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/path.h"
|
#include "common/path.h"
|
||||||
|
@ -360,6 +361,11 @@ std::vector<std::pair<std::string, std::string>> SDLInputSource::EnumerateDevice
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SDLInputSource::ContainsDevice(std::string_view device) const
|
||||||
|
{
|
||||||
|
return device.starts_with("SDL-");
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<InputBindingKey> SDLInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
std::optional<InputBindingKey> SDLInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
||||||
{
|
{
|
||||||
if (!device.starts_with("SDL-") || binding.empty())
|
if (!device.starts_with("SDL-") || binding.empty())
|
||||||
|
@ -1092,3 +1098,126 @@ std::unique_ptr<InputSource> InputSource::CreateSDLSource()
|
||||||
{
|
{
|
||||||
return std::make_unique<SDLInputSource>();
|
return std::make_unique<SDLInputSource>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> SDLInputSource::CreateForceFeedbackDevice(std::string_view device, Error* error)
|
||||||
|
{
|
||||||
|
SDL_Joystick* joystick = GetJoystickForDevice(device);
|
||||||
|
if (!joystick)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "No SDL_Joystick for {}", device);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
|
||||||
|
if (!haptic)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Haptic is not supported on {} ({})", device, SDL_JoystickName(joystick));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unique_ptr<SDLForceFeedbackDevice>(new SDLForceFeedbackDevice(joystick, haptic));
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLForceFeedbackDevice::SDLForceFeedbackDevice(SDL_Joystick* joystick, SDL_Haptic* haptic) : m_haptic(haptic)
|
||||||
|
{
|
||||||
|
std::memset(&m_constant_effect, 0, sizeof(m_constant_effect));
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLForceFeedbackDevice::~SDLForceFeedbackDevice()
|
||||||
|
{
|
||||||
|
if (m_haptic)
|
||||||
|
{
|
||||||
|
DestroyEffects();
|
||||||
|
|
||||||
|
SDL_HapticClose(m_haptic);
|
||||||
|
m_haptic = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLForceFeedbackDevice::CreateEffects(SDL_Joystick* joystick)
|
||||||
|
{
|
||||||
|
constexpr u32 length = 10000; // 10 seconds since NFS games seem to not issue new commands while rotating.
|
||||||
|
|
||||||
|
const unsigned int supported = SDL_HapticQuery(m_haptic);
|
||||||
|
if (supported & SDL_HAPTIC_CONSTANT)
|
||||||
|
{
|
||||||
|
m_constant_effect.type = SDL_HAPTIC_CONSTANT;
|
||||||
|
m_constant_effect.constant.direction.type = SDL_HAPTIC_STEERING_AXIS;
|
||||||
|
m_constant_effect.constant.length = length;
|
||||||
|
|
||||||
|
m_constant_effect_id = SDL_HapticNewEffect(m_haptic, &m_constant_effect);
|
||||||
|
if (m_constant_effect_id < 0)
|
||||||
|
ERROR_LOG("SDL_HapticNewEffect() for constant failed: {}", SDL_GetError());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WARNING_LOG("Constant effect is not supported on '{}'", SDL_JoystickName(joystick));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLForceFeedbackDevice::DestroyEffects()
|
||||||
|
{
|
||||||
|
if (m_constant_effect_id >= 0)
|
||||||
|
{
|
||||||
|
if (m_constant_effect_running)
|
||||||
|
{
|
||||||
|
SDL_HapticStopEffect(m_haptic, m_constant_effect_id);
|
||||||
|
m_constant_effect_running = false;
|
||||||
|
}
|
||||||
|
SDL_HapticDestroyEffect(m_haptic, m_constant_effect_id);
|
||||||
|
m_constant_effect_id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[maybe_unused]] static u16 ClampU16(T val)
|
||||||
|
{
|
||||||
|
return static_cast<u16>(std::clamp<T>(val, 0, 65535));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[maybe_unused]] static u16 ClampS16(T val)
|
||||||
|
{
|
||||||
|
return static_cast<s16>(std::clamp<T>(val, -32768, 32767));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLForceFeedbackDevice::SetConstantForce(s32 level)
|
||||||
|
{
|
||||||
|
if (m_constant_effect_id < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const s16 new_level = ClampS16(level);
|
||||||
|
if (m_constant_effect.constant.level != new_level)
|
||||||
|
{
|
||||||
|
m_constant_effect.constant.level = new_level;
|
||||||
|
if (SDL_HapticUpdateEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0)
|
||||||
|
ERROR_LOG("SDL_HapticUpdateEffect() for constant failed: {}", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_constant_effect_running)
|
||||||
|
{
|
||||||
|
if (SDL_HapticRunEffect(m_haptic, m_constant_effect_id, SDL_HAPTIC_INFINITY) == 0)
|
||||||
|
m_constant_effect_running = true;
|
||||||
|
else
|
||||||
|
ERROR_LOG("SDL_HapticRunEffect() for constant failed: {}", SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLForceFeedbackDevice::DisableForce(Effect force)
|
||||||
|
{
|
||||||
|
switch (force)
|
||||||
|
{
|
||||||
|
case Effect::Constant:
|
||||||
|
{
|
||||||
|
if (m_constant_effect_running)
|
||||||
|
{
|
||||||
|
SDL_HapticStopEffect(m_haptic, m_constant_effect_id);
|
||||||
|
m_constant_effect_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,10 +34,13 @@ public:
|
||||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||||
float small_intensity) override;
|
float small_intensity) override;
|
||||||
|
|
||||||
|
bool ContainsDevice(std::string_view device) const override;
|
||||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||||
TinyString ConvertKeyToString(InputBindingKey key) override;
|
TinyString ConvertKeyToString(InputBindingKey key) override;
|
||||||
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) override;
|
||||||
|
|
||||||
bool ProcessSDLEvent(const SDL_Event* event);
|
bool ProcessSDLEvent(const SDL_Event* event);
|
||||||
|
|
||||||
SDL_Joystick* GetJoystickForDevice(std::string_view device);
|
SDL_Joystick* GetJoystickForDevice(std::string_view device);
|
||||||
|
@ -103,3 +106,23 @@ private:
|
||||||
bool m_enable_mfi_driver = false;
|
bool m_enable_mfi_driver = false;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SDLForceFeedbackDevice : public ForceFeedbackDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SDLForceFeedbackDevice(SDL_Joystick* joystick, SDL_Haptic* haptic);
|
||||||
|
~SDLForceFeedbackDevice() override;
|
||||||
|
|
||||||
|
void SetConstantForce(s32 level) override;
|
||||||
|
void DisableForce(Effect force) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateEffects(SDL_Joystick* joystick);
|
||||||
|
void DestroyEffects();
|
||||||
|
|
||||||
|
SDL_Haptic* m_haptic = nullptr;
|
||||||
|
|
||||||
|
SDL_HapticEffect m_constant_effect;
|
||||||
|
int m_constant_effect_id = -1;
|
||||||
|
bool m_constant_effect_running = false;
|
||||||
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
@ -90,6 +91,11 @@ void Win32RawInputSource::UpdateMotorState(InputBindingKey large_key, InputBindi
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Win32RawInputSource::ContainsDevice(std::string_view device) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<InputBindingKey> Win32RawInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
std::optional<InputBindingKey> Win32RawInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -105,6 +111,13 @@ TinyString Win32RawInputSource::ConvertKeyToIcon(InputBindingKey key)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> Win32RawInputSource::CreateForceFeedbackDevice(std::string_view device,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Not supported on this input source.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<InputBindingKey> Win32RawInputSource::EnumerateMotors()
|
std::vector<InputBindingKey> Win32RawInputSource::EnumerateMotors()
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -30,10 +30,13 @@ public:
|
||||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||||
float small_intensity) override;
|
float small_intensity) override;
|
||||||
|
|
||||||
|
bool ContainsDevice(std::string_view device) const override;
|
||||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||||
TinyString ConvertKeyToString(InputBindingKey key) override;
|
TinyString ConvertKeyToString(InputBindingKey key) override;
|
||||||
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct MouseState
|
struct MouseState
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
@ -251,6 +252,11 @@ std::vector<std::pair<std::string, std::string>> XInputSource::EnumerateDevices(
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool XInputSource::ContainsDevice(std::string_view device) const
|
||||||
|
{
|
||||||
|
return device.starts_with("XInput-");
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<InputBindingKey> XInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
std::optional<InputBindingKey> XInputSource::ParseKeyString(std::string_view device, std::string_view binding)
|
||||||
{
|
{
|
||||||
if (!device.starts_with("XInput-") || binding.empty())
|
if (!device.starts_with("XInput-") || binding.empty())
|
||||||
|
@ -364,6 +370,12 @@ TinyString XInputSource::ConvertKeyToIcon(InputBindingKey key)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> XInputSource::CreateForceFeedbackDevice(std::string_view device, Error* error)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Not supported on this input source.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<InputBindingKey> XInputSource::EnumerateMotors()
|
std::vector<InputBindingKey> XInputSource::EnumerateMotors()
|
||||||
{
|
{
|
||||||
std::vector<InputBindingKey> ret;
|
std::vector<InputBindingKey> ret;
|
||||||
|
|
|
@ -48,10 +48,13 @@ public:
|
||||||
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
|
||||||
float small_intensity) override;
|
float small_intensity) override;
|
||||||
|
|
||||||
|
bool ContainsDevice(std::string_view device) const override;
|
||||||
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
std::optional<InputBindingKey> ParseKeyString(std::string_view device, std::string_view binding) override;
|
||||||
TinyString ConvertKeyToString(InputBindingKey key) override;
|
TinyString ConvertKeyToString(InputBindingKey key) override;
|
||||||
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
TinyString ConvertKeyToIcon(InputBindingKey key) override;
|
||||||
|
|
||||||
|
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ControllerData
|
struct ControllerData
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue