diff options
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface/CoreDevice.h')
| -rw-r--r-- | Source/Core/InputCommon/ControllerInterface/CoreDevice.h | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h new file mode 100644 index 0000000000..2612f59627 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -0,0 +1,228 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include <chrono> +#include <memory> +#include <mutex> +#include <optional> +#include <string> +#include <string_view> +#include <vector> + +#include "Common/CommonTypes.h" + +// idk in case I wanted to change it to double or something, idk what's best +typedef double ControlState; + +namespace ciface +{ +// 100Hz which homebrew docs very roughly imply is within WiiMote normal +// range, used for periodic haptic effects though often ignored by devices +// TODO: Make this configurable. +constexpr int RUMBLE_PERIOD_MS = 10; + +// This needs to be at least as long as the longest rumble that might ever be played. +// Too short and it's going to stop in the middle of a long effect. +// Infinite values are invalid for ramp effects and probably not sensible. +constexpr int RUMBLE_LENGTH_MS = 1000 * 10; + +// All inputs (other than accel/gyro) return 1.0 as their maximum value. +// Battery inputs will almost always be mapped to the "Battery" setting which is a percentage. +// If someone actually wants to map a battery input to a regular control they can divide by 100. +// I think this is better than requiring multiplication by 100 for the most common usage. +constexpr ControlState BATTERY_INPUT_MAX_VALUE = 100.0; + +namespace Core +{ +class Device +{ +public: + class Input; + class Output; + + // + // Control + // + // Control includes inputs and outputs + // + class Control // input or output + { + public: + virtual ~Control() = default; + virtual std::string GetName() const = 0; + virtual Input* ToInput() { return nullptr; } + virtual Output* ToOutput() { return nullptr; } + + // May be overridden to allow multiple valid names. + // Useful for backwards-compatible configurations when names change. + virtual bool IsMatchingName(std::string_view name) const; + }; + + // + // Input + // + // An input on a device + // + class Input : public Control + { + public: + // Things like absolute axes/ absolute mouse position should override this to prevent + // undesirable behavior in our mapping logic. + virtual bool IsDetectable() const { return true; } + + // Implementations should return a value from 0.0 to 1.0 across their normal range. + // One input should be provided for each "direction". (e.g. 2 for each axis) + // If possible, negative values may be returned in situations where an opposing input is + // activated. (e.g. When an underlying axis, X, is currently negative, "Axis X-", will return a + // positive value and "Axis X+" may return a negative value.) + // Doing so is solely to allow our input detection logic to better detect false positives. + // This is necessary when making use of "FullAnalogSurface" as multiple inputs will be seen + // increasing from 0.0 to 1.0 as a user tries to map just one. The negative values provide a + // view of the underlying axis. (Negative values are clamped off before they reach + // expression-parser or controller-emu) + virtual ControlState GetState() const = 0; + + Input* ToInput() override { return this; } + + // Overridden by CombinedInput, + // so hotkey logic knows Ctrl, L_Ctrl, and R_Ctrl are the same, + // and so input detection can return the parent name. + virtual bool IsChild(const Input*) const { return false; } + }; + + // + // Output + // + // An output on a device + // + class Output : public Control + { + public: + virtual ~Output() = default; + virtual void SetState(ControlState state) = 0; + Output* ToOutput() override { return this; } + }; + + virtual ~Device(); + + int GetId() const { return m_id; } + void SetId(int id) { m_id = id; } + virtual std::string GetName() const = 0; + virtual std::string GetSource() const = 0; + std::string GetQualifiedName() const; + virtual void UpdateInput() {} + + // May be overridden to implement hotplug removal. + // Currently handled on a per-backend basis but this could change. + virtual bool IsValid() const { return true; } + + // (e.g. Xbox 360 controllers have controller number LEDs which should match the ID we use.) + virtual std::optional<int> GetPreferredId() const; + + const std::vector<Input*>& Inputs() const { return m_inputs; } + const std::vector<Output*>& Outputs() const { return m_outputs; } + + Input* GetParentMostInput(Input* input) const; + + Input* FindInput(std::string_view name) const; + Output* FindOutput(std::string_view name) const; + +protected: + void AddInput(Input* const i); + void AddOutput(Output* const o); + + class FullAnalogSurface final : public Input + { + public: + FullAnalogSurface(Input* low, Input* high) : m_low(*low), m_high(*high) {} + ControlState GetState() const override; + std::string GetName() const override; + bool IsMatchingName(std::string_view name) const override; + + private: + Input& m_low; + Input& m_high; + }; + + void AddAnalogInputs(Input* low, Input* high) + { + AddInput(low); + AddInput(high); + AddInput(new FullAnalogSurface(low, high)); + AddInput(new FullAnalogSurface(high, low)); + } + + void AddCombinedInput(std::string name, const std::pair<std::string, std::string>& inputs); + +private: + int m_id; + std::vector<Input*> m_inputs; + std::vector<Output*> m_outputs; +}; + +// +// DeviceQualifier +// +// Device qualifier used to match devices. +// Currently has ( source, id, name ) properties which match a device +// +class DeviceQualifier +{ +public: + DeviceQualifier() : cid(-1) {} + DeviceQualifier(std::string source_, const int id_, std::string name_) + : source(std::move(source_)), cid(id_), name(std::move(name_)) + { + } + void FromDevice(const Device* const dev); + void FromString(const std::string& str); + std::string ToString() const; + + bool operator==(const DeviceQualifier& devq) const; + bool operator!=(const DeviceQualifier& devq) const; + + bool operator==(const Device* dev) const; + bool operator!=(const Device* dev) const; + + std::string source; + int cid; + std::string name; +}; + +class DeviceContainer +{ +public: + using Clock = std::chrono::steady_clock; + + struct InputDetection + { + std::shared_ptr<Device> device; + Device::Input* input; + Clock::time_point press_time; + std::optional<Clock::time_point> release_time; + ControlState smoothness; + }; + + Device::Input* FindInput(std::string_view name, const Device* def_dev) const; + Device::Output* FindOutput(std::string_view name, const Device* def_dev) const; + + std::vector<std::string> GetAllDeviceStrings() const; + std::string GetDefaultDeviceString() const; + std::shared_ptr<Device> FindDevice(const DeviceQualifier& devq) const; + + bool HasConnectedDevice(const DeviceQualifier& qualifier) const; + + std::vector<InputDetection> DetectInput(const std::vector<std::string>& device_strings, + std::chrono::milliseconds initial_wait, + std::chrono::milliseconds confirmation_wait, + std::chrono::milliseconds maximum_wait) const; + +protected: + mutable std::recursive_mutex m_devices_mutex; + std::vector<std::shared_ptr<Device>> m_devices; +}; +} // namespace Core +} // namespace ciface |
