// 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 #include "common/logging/log.h" #include "common/param_package.h" #include "common/quaternion.h" #include "common/vector_math.h" namespace Input { enum class AnalogDirection : u8 { RIGHT, LEFT, UP, DOWN, }; struct AnalogProperties { float deadzone; float range; float threshold; }; template struct InputCallback { std::function on_change; }; /// An abstract class template for an input device (a button, an analog input, etc.). template class InputDevice { public: virtual ~InputDevice() = default; virtual StatusType GetStatus() const { return {}; } virtual StatusType GetRawStatus() const { return GetStatus(); } virtual AnalogProperties GetAnalogProperties() const { return {}; } virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { return {}; } virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low, [[maybe_unused]] f32 amp_high, [[maybe_unused]] f32 freq_high) const { return {}; } void SetCallback(InputCallback callback_) { callback = std::move(callback_); } void TriggerOnChange() { if (callback.on_change) { callback.on_change(GetStatus()); } } 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 contains all parameters for creating the device */ template std::unique_ptr CreateDevice(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); } /** * A button device is an input device that returns bool as status. * true for pressed; false for released. */ using ButtonDevice = InputDevice; /** * An analog device is an input device that returns a tuple of x and y coordinates as status. The * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up * direction */ using AnalogDevice = InputDevice>; /** * A vibration device is an input device that returns an unsigned byte as status. * It represents whether the vibration device supports vibration or not. * If the status returns 1, it supports vibration. Otherwise, it does not support vibration. */ using VibrationDevice = InputDevice; /** * A motion status is an object that returns a tuple of accelerometer state vector, * gyroscope state vector, rotation state vector, orientation state matrix and quaterion state * vector. * * For both 3D vectors: * x+ is the same direction as RIGHT on D-pad. * y+ is normal to the touch screen, pointing outward. * z+ is the same direction as UP on D-pad. * * For accelerometer state vector * Units: g (gravitational acceleration) * * For gyroscope state vector: * Orientation is determined by right-hand rule. * Units: deg/sec * * For rotation state vector * Units: rotations * * For orientation state matrix * x vector * y vector * z vector * * For quaternion state vector * xyz vector * w float */ using MotionStatus = std::tuple, Common::Vec3, Common::Vec3, std::array, Common::Quaternion>; /** * A motion device is an input device that returns a motion status object */ using MotionDevice = InputDevice; /** * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool. * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is * pressed. */ using TouchStatus = std::array, 16>; /** * A touch device is an input device that returns a touch status object */ using TouchDevice = InputDevice; /** * A mouse device is an input device that returns a tuple of two floats and four ints. * The first two floats are X and Y device coordinates of the mouse (from 0-1). * The s32s are the mouse wheel. */ using MouseDevice = InputDevice>; } // namespace Input