committed by
Narr the Reg
2 changed files with 585 additions and 0 deletions
@ -0,0 +1,361 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included
|
|||
|
|||
#include "common/logging/log.h"
|
|||
#include "common/param_package.h"
|
|||
#include "input_common/input_engine.h"
|
|||
|
|||
namespace InputCommon { |
|||
|
|||
void InputEngine::PreSetController(const PadIdentifier& identifier) { |
|||
std::lock_guard lock{mutex}; |
|||
if (!controller_list.contains(identifier)) { |
|||
controller_list.insert_or_assign(identifier, ControllerData{}); |
|||
} |
|||
} |
|||
|
|||
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) { |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!controller.buttons.contains(button)) { |
|||
controller.buttons.insert_or_assign(button, false); |
|||
} |
|||
} |
|||
|
|||
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) { |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!controller.hat_buttons.contains(button)) { |
|||
controller.hat_buttons.insert_or_assign(button, u8{0}); |
|||
} |
|||
} |
|||
|
|||
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) { |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!controller.axes.contains(axis)) { |
|||
controller.axes.insert_or_assign(axis, 0.0f); |
|||
} |
|||
} |
|||
|
|||
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) { |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!controller.motions.contains(motion)) { |
|||
controller.motions.insert_or_assign(motion, BasicMotion{}); |
|||
} |
|||
} |
|||
|
|||
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) { |
|||
{ |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!configuring) { |
|||
controller.buttons.insert_or_assign(button, value); |
|||
} |
|||
} |
|||
TriggerOnButtonChange(identifier, button, value); |
|||
} |
|||
|
|||
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) { |
|||
{ |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!configuring) { |
|||
controller.hat_buttons.insert_or_assign(button, value); |
|||
} |
|||
} |
|||
TriggerOnHatButtonChange(identifier, button, value); |
|||
} |
|||
|
|||
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) { |
|||
{ |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!configuring) { |
|||
controller.axes.insert_or_assign(axis, value); |
|||
} |
|||
} |
|||
TriggerOnAxisChange(identifier, axis, value); |
|||
} |
|||
|
|||
void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) { |
|||
{ |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!configuring) { |
|||
controller.battery = value; |
|||
} |
|||
} |
|||
TriggerOnBatteryChange(identifier, value); |
|||
} |
|||
|
|||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value) { |
|||
{ |
|||
std::lock_guard lock{mutex}; |
|||
ControllerData& controller = controller_list.at(identifier); |
|||
if (!configuring) { |
|||
controller.motions.insert_or_assign(motion, value); |
|||
} |
|||
} |
|||
TriggerOnMotionChange(identifier, motion, value); |
|||
} |
|||
|
|||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { |
|||
std::lock_guard lock{mutex}; |
|||
if (!controller_list.contains(identifier)) { |
|||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), |
|||
identifier.pad, identifier.port); |
|||
return false; |
|||
} |
|||
ControllerData controller = controller_list.at(identifier); |
|||
if (!controller.buttons.contains(button)) { |
|||
LOG_ERROR(Input, "Invalid button {}", button); |
|||
return false; |
|||
} |
|||
return controller.buttons.at(button); |
|||
} |
|||
|
|||
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const { |
|||
std::lock_guard lock{mutex}; |
|||
if (!controller_list.contains(identifier)) { |
|||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), |
|||
identifier.pad, identifier.port); |
|||
return false; |
|||
} |
|||
ControllerData controller = controller_list.at(identifier); |
|||
if (!controller.hat_buttons.contains(button)) { |
|||
LOG_ERROR(Input, "Invalid hat button {}", button); |
|||
return false; |
|||
} |
|||
return (controller.hat_buttons.at(button) & direction) != 0; |
|||
} |
|||
|
|||
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { |
|||
std::lock_guard lock{mutex}; |
|||
if (!controller_list.contains(identifier)) { |
|||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), |
|||
identifier.pad, identifier.port); |
|||
return 0.0f; |
|||
} |
|||
ControllerData controller = controller_list.at(identifier); |
|||
if (!controller.axes.contains(axis)) { |
|||
LOG_ERROR(Input, "Invalid axis {}", axis); |
|||
return 0.0f; |
|||
} |
|||
return controller.axes.at(axis); |
|||
} |
|||
|
|||
BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { |
|||
std::lock_guard lock{mutex}; |
|||
if (!controller_list.contains(identifier)) { |
|||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), |
|||
identifier.pad, identifier.port); |
|||
return BatteryLevel::Charging; |
|||
} |
|||
ControllerData controller = controller_list.at(identifier); |
|||
return controller.battery; |
|||
} |
|||
|
|||
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { |
|||
std::lock_guard lock{mutex}; |
|||
if (!controller_list.contains(identifier)) { |
|||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), |
|||
identifier.pad, identifier.port); |
|||
return {}; |
|||
} |
|||
ControllerData controller = controller_list.at(identifier); |
|||
return controller.motions.at(motion); |
|||
} |
|||
|
|||
void InputEngine::ResetButtonState() { |
|||
for (std::pair<PadIdentifier, ControllerData> controller : controller_list) { |
|||
for (std::pair<int, bool> button : controller.second.buttons) { |
|||
SetButton(controller.first, button.first, false); |
|||
} |
|||
for (std::pair<int, bool> button : controller.second.hat_buttons) { |
|||
SetHatButton(controller.first, button.first, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void InputEngine::ResetAnalogState() { |
|||
for (std::pair<PadIdentifier, ControllerData> controller : controller_list) { |
|||
for (std::pair<int, float> axis : controller.second.axes) { |
|||
SetAxis(controller.first, axis.first, 0.0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) { |
|||
const InputIdentifier& poller = poller_pair.second; |
|||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) { |
|||
continue; |
|||
} |
|||
if (poller.callback.on_change) { |
|||
poller.callback.on_change(); |
|||
} |
|||
} |
|||
if (!configuring || !mapping_callback.on_data) { |
|||
return; |
|||
} |
|||
if (value == GetButton(identifier, button)) { |
|||
return; |
|||
} |
|||
mapping_callback.on_data(MappingData{ |
|||
.engine = GetEngineName(), |
|||
.pad = identifier, |
|||
.type = EngineInputType::Button, |
|||
.index = button, |
|||
.button_value = value, |
|||
}); |
|||
} |
|||
|
|||
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) { |
|||
const InputIdentifier& poller = poller_pair.second; |
|||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) { |
|||
continue; |
|||
} |
|||
if (poller.callback.on_change) { |
|||
poller.callback.on_change(); |
|||
} |
|||
} |
|||
if (!configuring || !mapping_callback.on_data) { |
|||
return; |
|||
} |
|||
for (std::size_t index = 1; index < 0xff; index <<= 1) { |
|||
bool button_value = (value & index) != 0; |
|||
if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) { |
|||
continue; |
|||
} |
|||
mapping_callback.on_data(MappingData{ |
|||
.engine = GetEngineName(), |
|||
.pad = identifier, |
|||
.type = EngineInputType::HatButton, |
|||
.index = button, |
|||
.hat_name = GetHatButtonName(static_cast<u8>(index)), |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) { |
|||
const InputIdentifier& poller = poller_pair.second; |
|||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) { |
|||
continue; |
|||
} |
|||
if (poller.callback.on_change) { |
|||
poller.callback.on_change(); |
|||
} |
|||
} |
|||
if (!configuring || !mapping_callback.on_data) { |
|||
return; |
|||
} |
|||
if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) { |
|||
return; |
|||
} |
|||
mapping_callback.on_data(MappingData{ |
|||
.engine = GetEngineName(), |
|||
.pad = identifier, |
|||
.type = EngineInputType::Analog, |
|||
.index = axis, |
|||
.axis_value = value, |
|||
}); |
|||
} |
|||
|
|||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, |
|||
[[maybe_unused]] BatteryLevel value) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) { |
|||
const InputIdentifier& poller = poller_pair.second; |
|||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) { |
|||
continue; |
|||
} |
|||
if (poller.callback.on_change) { |
|||
poller.callback.on_change(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, |
|||
BasicMotion value) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) { |
|||
const InputIdentifier& poller = poller_pair.second; |
|||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) { |
|||
continue; |
|||
} |
|||
if (poller.callback.on_change) { |
|||
poller.callback.on_change(); |
|||
} |
|||
} |
|||
if (!configuring || !mapping_callback.on_data) { |
|||
return; |
|||
} |
|||
if (std::abs(value.gyro_x) < 1.0f && std::abs(value.gyro_y) < 1.0f && |
|||
std::abs(value.gyro_z) < 1.0f) { |
|||
return; |
|||
} |
|||
mapping_callback.on_data(MappingData{ |
|||
.engine = GetEngineName(), |
|||
.pad = identifier, |
|||
.type = EngineInputType::Motion, |
|||
.index = motion, |
|||
.motion_value = value, |
|||
}); |
|||
} |
|||
|
|||
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, |
|||
const PadIdentifier& identifier, EngineInputType type, |
|||
std::size_t index) const { |
|||
if (input_identifier.type != type) { |
|||
return false; |
|||
} |
|||
if (input_identifier.index != index) { |
|||
return false; |
|||
} |
|||
if (input_identifier.identifier != identifier) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void InputEngine::BeginConfiguration() { |
|||
configuring = true; |
|||
} |
|||
|
|||
void InputEngine::EndConfiguration() { |
|||
configuring = false; |
|||
} |
|||
|
|||
const std::string& InputEngine::GetEngineName() const { |
|||
return input_engine; |
|||
} |
|||
|
|||
int InputEngine::SetCallback(InputIdentifier input_identifier) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
callback_list.insert_or_assign(last_callback_key, input_identifier); |
|||
return last_callback_key++; |
|||
} |
|||
|
|||
void InputEngine::SetMappingCallback(MappingCallback callback) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
mapping_callback = std::move(callback); |
|||
} |
|||
|
|||
void InputEngine::DeleteCallback(int key) { |
|||
std::lock_guard lock{mutex_callback}; |
|||
if (!callback_list.contains(key)) { |
|||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); |
|||
return; |
|||
} |
|||
callback_list.erase(key); |
|||
} |
|||
|
|||
} // namespace InputCommon
|
|||
@ -0,0 +1,224 @@ |
|||
// Copyright 2021 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <mutex> |
|||
#include <unordered_map> |
|||
|
|||
#include "common/common_types.h" |
|||
#include "common/input.h" |
|||
#include "common/param_package.h" |
|||
#include "common/uuid.h" |
|||
#include "input_common/main.h" |
|||
|
|||
// Pad Identifier of data source |
|||
struct PadIdentifier { |
|||
Common::UUID guid{}; |
|||
std::size_t port{}; |
|||
std::size_t pad{}; |
|||
|
|||
friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default; |
|||
}; |
|||
|
|||
// Basic motion data containing data from the sensors and a timestamp in microsecons |
|||
struct BasicMotion { |
|||
float gyro_x; |
|||
float gyro_y; |
|||
float gyro_z; |
|||
float accel_x; |
|||
float accel_y; |
|||
float accel_z; |
|||
u64 delta_timestamp; |
|||
}; |
|||
|
|||
// Stages of a battery charge |
|||
enum class BatteryLevel { |
|||
Empty, |
|||
Critical, |
|||
Low, |
|||
Medium, |
|||
Full, |
|||
Charging, |
|||
}; |
|||
|
|||
// Types of input that are stored in the engine |
|||
enum class EngineInputType { |
|||
None, |
|||
Button, |
|||
HatButton, |
|||
Analog, |
|||
Motion, |
|||
Battery, |
|||
}; |
|||
|
|||
namespace std { |
|||
// Hash used to create lists from PadIdentifier data |
|||
template <> |
|||
struct hash<PadIdentifier> { |
|||
size_t operator()(const PadIdentifier& pad_id) const noexcept { |
|||
u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0]; |
|||
hash_value ^= (static_cast<u64>(pad_id.port) << 32); |
|||
hash_value ^= static_cast<u64>(pad_id.pad); |
|||
return static_cast<size_t>(hash_value); |
|||
} |
|||
}; |
|||
|
|||
} // namespace std |
|||
|
|||
namespace InputCommon { |
|||
|
|||
// Data from the engine and device needed for creating a ParamPackage |
|||
struct MappingData { |
|||
std::string engine{}; |
|||
PadIdentifier pad{}; |
|||
EngineInputType type{}; |
|||
int index{}; |
|||
bool button_value{}; |
|||
std::string hat_name{}; |
|||
f32 axis_value{}; |
|||
BasicMotion motion_value{}; |
|||
}; |
|||
|
|||
// Triggered if data changed on the controller |
|||
struct UpdateCallback { |
|||
std::function<void()> on_change; |
|||
}; |
|||
|
|||
// Triggered if data changed on the controller and the engine is on configuring mode |
|||
struct MappingCallback { |
|||
std::function<void(MappingData)> on_data; |
|||
}; |
|||
|
|||
// Input Identifier of data source |
|||
struct InputIdentifier { |
|||
PadIdentifier identifier; |
|||
EngineInputType type; |
|||
std::size_t index; |
|||
UpdateCallback callback; |
|||
}; |
|||
|
|||
class InputEngine { |
|||
public: |
|||
explicit InputEngine(const std::string& input_engine_) : input_engine(input_engine_) { |
|||
callback_list.clear(); |
|||
} |
|||
|
|||
virtual ~InputEngine() = default; |
|||
|
|||
// Enable configuring mode for mapping |
|||
void BeginConfiguration(); |
|||
|
|||
// Disable configuring mode for mapping |
|||
void EndConfiguration(); |
|||
|
|||
// Sets rumble to a controller |
|||
virtual bool SetRumble([[maybe_unused]] const PadIdentifier& identifier, |
|||
[[maybe_unused]] const Input::VibrationStatus vibration) { |
|||
return false; |
|||
} |
|||
|
|||
// Sets a led pattern for a controller |
|||
virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, |
|||
[[maybe_unused]] const Input::LedStatus led_status) { |
|||
return; |
|||
} |
|||
|
|||
// Returns the engine name |
|||
[[nodiscard]] const std::string& GetEngineName() const; |
|||
|
|||
/// Used for automapping features |
|||
virtual std::vector<Common::ParamPackage> GetInputDevices() const { |
|||
return {}; |
|||
}; |
|||
|
|||
/// Retrieves the button mappings for the given device |
|||
virtual InputCommon::ButtonMapping GetButtonMappingForDevice( |
|||
[[maybe_unused]] const Common::ParamPackage& params) { |
|||
return {}; |
|||
}; |
|||
|
|||
/// Retrieves the analog mappings for the given device |
|||
virtual InputCommon::AnalogMapping GetAnalogMappingForDevice( |
|||
[[maybe_unused]] const Common::ParamPackage& params) { |
|||
return {}; |
|||
}; |
|||
|
|||
/// Retrieves the motion mappings for the given device |
|||
virtual InputCommon::MotionMapping GetMotionMappingForDevice( |
|||
[[maybe_unused]] const Common::ParamPackage& params) { |
|||
return {}; |
|||
}; |
|||
|
|||
/// Retrieves the name of the given input. |
|||
virtual std::string GetUIName([[maybe_unused]] const Common::ParamPackage& params) const { |
|||
return GetEngineName(); |
|||
}; |
|||
|
|||
/// Retrieves the index number of the given hat button direction |
|||
virtual u8 GetHatButtonId([[maybe_unused]] const std::string direction_name) const { |
|||
return 0; |
|||
}; |
|||
|
|||
void PreSetController(const PadIdentifier& identifier); |
|||
void PreSetButton(const PadIdentifier& identifier, int button); |
|||
void PreSetHatButton(const PadIdentifier& identifier, int button); |
|||
void PreSetAxis(const PadIdentifier& identifier, int axis); |
|||
void PreSetMotion(const PadIdentifier& identifier, int motion); |
|||
void ResetButtonState(); |
|||
void ResetAnalogState(); |
|||
|
|||
bool GetButton(const PadIdentifier& identifier, int button) const; |
|||
bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; |
|||
f32 GetAxis(const PadIdentifier& identifier, int axis) const; |
|||
BatteryLevel GetBattery(const PadIdentifier& identifier) const; |
|||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; |
|||
|
|||
int SetCallback(InputIdentifier input_identifier); |
|||
void SetMappingCallback(MappingCallback callback); |
|||
void DeleteCallback(int key); |
|||
|
|||
protected: |
|||
void SetButton(const PadIdentifier& identifier, int button, bool value); |
|||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value); |
|||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value); |
|||
void SetBattery(const PadIdentifier& identifier, BatteryLevel value); |
|||
void SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value); |
|||
|
|||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { |
|||
return "Unknown"; |
|||
} |
|||
|
|||
private: |
|||
struct ControllerData { |
|||
std::unordered_map<int, bool> buttons; |
|||
std::unordered_map<int, u8> hat_buttons; |
|||
std::unordered_map<int, float> axes; |
|||
std::unordered_map<int, BasicMotion> motions; |
|||
BatteryLevel battery; |
|||
}; |
|||
|
|||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); |
|||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); |
|||
void TriggerOnAxisChange(const PadIdentifier& identifier, int button, f32 value); |
|||
void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value); |
|||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, BasicMotion value); |
|||
|
|||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, |
|||
const PadIdentifier& identifier, EngineInputType type, |
|||
std::size_t index) const; |
|||
|
|||
mutable std::mutex mutex; |
|||
mutable std::mutex mutex_callback; |
|||
bool configuring{false}; |
|||
bool is_callback_enabled{true}; |
|||
const std::string input_engine; |
|||
int last_callback_key = 0; |
|||
std::unordered_map<PadIdentifier, ControllerData> controller_list; |
|||
std::unordered_map<int, InputIdentifier> callback_list; |
|||
MappingCallback mapping_callback; |
|||
}; |
|||
|
|||
} // namespace InputCommon |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue