diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 23d43a394..919da4a53 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -73,6 +73,7 @@ add_library(common STATIC hex_util.h host_memory.cpp host_memory.h + input.h intrusive_red_black_tree.h literals.h logging/backend.cpp diff --git a/src/common/input.h b/src/common/input.h new file mode 100644 index 000000000..6eefc55f9 --- /dev/null +++ b/src/common/input.h @@ -0,0 +1,242 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "common/param_package.h" + +namespace Input { + +enum class InputType { + None, + Battery, + Button, + Stick, + Analog, + Trigger, + Motion, + Touch, + Color, + Vibration, + Nfc, + Ir, +}; + +enum class BatteryLevel { + Empty, + Critical, + Low, + Medium, + Full, + Charging, +}; + +struct AnalogProperties { + float deadzone{}; + float range{1.0f}; + float threshold{0.5f}; + float offset{}; + bool inverted{}; +}; + +struct AnalogStatus { + float value{}; + float raw_value{}; + AnalogProperties properties{}; +}; + +struct ButtonStatus { + bool value{}; + bool inverted{}; + bool toggle{}; + bool locked{}; +}; + +using BatteryStatus = BatteryLevel; + +struct StickStatus { + AnalogStatus x{}; + AnalogStatus y{}; + bool left{}; + bool right{}; + bool up{}; + bool down{}; +}; + +struct TriggerStatus { + AnalogStatus analog{}; + bool pressed{}; +}; + +struct MotionSensor { + AnalogStatus x{}; + AnalogStatus y{}; + AnalogStatus z{}; +}; + +struct MotionStatus { + MotionSensor gyro{}; + MotionSensor accel{}; + u64 delta_timestamp{}; +}; + +struct TouchStatus { + ButtonStatus pressed{}; + AnalogStatus x{}; + AnalogStatus y{}; + u32 id{}; +}; + +struct BodyColorStatus { + u32 body{}; + u32 buttons{}; +}; + +struct VibrationStatus { + f32 low_amplitude{}; + f32 low_frequency{}; + f32 high_amplitude{}; + f32 high_frequency{}; +}; + +struct LedStatus { + bool led_1{}; + bool led_2{}; + bool led_3{}; + bool led_4{}; +}; + +struct CallbackStatus { + InputType type{InputType::None}; + ButtonStatus button_status{}; + StickStatus stick_status{}; + AnalogStatus analog_status{}; + TriggerStatus trigger_status{}; + MotionStatus motion_status{}; + TouchStatus touch_status{}; + BodyColorStatus color_status{}; + BatteryStatus battery_status{}; + VibrationStatus vibration_status{}; +}; + +struct InputCallback { + std::function on_change; +}; + +/// An abstract class template for an input device (a button, an analog input, etc.). +class InputDevice { +public: + virtual ~InputDevice() = default; + + void SetCallback(InputCallback callback_) { + callback = std::move(callback_); + } + + void TriggerOnChange(CallbackStatus status) { + if (callback.on_change) { + callback.on_change(status); + } + } + +private: + InputCallback callback; +}; + +/// An abstract class template for a factory that can create input devices. +template +class Factory { +public: + virtual ~Factory() = default; + virtual std::unique_ptr Create(const Common::ParamPackage&) = 0; +}; + +namespace Impl { + +template +using FactoryListType = std::unordered_map>>; + +template +struct FactoryList { + static FactoryListType list; +}; + +template +FactoryListType FactoryList::list; + +} // namespace Impl + +/** + * Registers an input device factory. + * @tparam InputDeviceType the type of input devices the factory can create + * @param name the name of the factory. Will be used to match the "engine" parameter when creating + * a device + * @param factory the factory object to register + */ +template +void RegisterFactory(const std::string& name, std::shared_ptr> factory) { + auto pair = std::make_pair(name, std::move(factory)); + if (!Impl::FactoryList::list.insert(std::move(pair)).second) { + LOG_ERROR(Input, "Factory '{}' already registered", name); + } +} + +/** + * Unregisters an input device factory. + * @tparam InputDeviceType the type of input devices the factory can create + * @param name the name of the factory to unregister + */ +template +void UnregisterFactory(const std::string& name) { + if (Impl::FactoryList::list.erase(name) == 0) { + LOG_ERROR(Input, "Factory '{}' not registered", name); + } +} + +/** + * Create an input device from given paramters. + * @tparam InputDeviceType the type of input devices to create + * @param params a serialized ParamPackage string that contains all parameters for creating the + * device + */ +template +std::unique_ptr CreateDeviceFromString(const std::string& params) { + const Common::ParamPackage package(params); + const std::string engine = package.Get("engine", "null"); + const auto& factory_list = Impl::FactoryList::list; + const auto pair = factory_list.find(engine); + if (pair == factory_list.end()) { + if (engine != "null") { + LOG_ERROR(Input, "Unknown engine name: {}", engine); + } + return std::make_unique(); + } + return pair->second->Create(package); +} + +/** + * Create an input device from given paramters. + * @tparam InputDeviceType the type of input devices to create + * @param A ParamPackage that contains all parameters for creating the device + */ +template +std::unique_ptr CreateDevice(const Common::ParamPackage package) { + const std::string engine = package.Get("engine", "null"); + const auto& factory_list = Impl::FactoryList::list; + const auto pair = factory_list.find(engine); + if (pair == factory_list.end()) { + if (engine != "null") { + LOG_ERROR(Input, "Unknown engine name: {}", engine); + } + return std::make_unique(); + } + return pair->second->Create(package); +} + +} // namespace Input