input_common: Create input_engine

This commit is contained in:
german77 2021-09-20 16:34:05 -05:00 committed by Narr the Reg
parent 449576df93
commit ea7b1fbc67
2 changed files with 585 additions and 0 deletions

View File

@ -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

View File

@ -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