diff options
| author | JMC47 <JMC4789@gmail.com> | 2022-10-03 16:25:40 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-03 16:25:40 -0400 |
| commit | 052c7395fbc0789147703a4c5cc754ae28f8f8bd (patch) | |
| tree | c6e206df257937e8373aeeb012e29ea74f0d0fc7 /Source/Core/InputCommon | |
| parent | cb6d476538afe85c25d30e8679b6f15abe5d1760 (diff) | |
| parent | 51ee05cb35f401c82adf815cbb4f4c3133e7ea84 (diff) | |
Merge pull request #9624 from JosJuice/input-override
Add new "input override" system for TAS input and Android touch controls
Diffstat (limited to 'Source/Core/InputCommon')
20 files changed, 578 insertions, 57 deletions
diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 06ffb37a2d..53929b7f6c 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -140,6 +140,8 @@ elseif(ANDROID) ControllerInterface/Android/Android.h ControllerInterface/Touch/ButtonManager.cpp ControllerInterface/Touch/ButtonManager.h + ControllerInterface/Touch/InputOverrider.cpp + ControllerInterface/Touch/InputOverrider.h ControllerInterface/Touch/Touchscreen.cpp ControllerInterface/Touch/Touchscreen.h ) diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp index 539e02b449..0f30b50f63 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp @@ -4,6 +4,7 @@ #include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h" #include <cmath> +#include <optional> #include "Common/Common.h" #include "Common/MathUtil.h" @@ -48,6 +49,34 @@ AnalogStick::StateData AnalogStick::GetState() const return GetReshapableState(true); } +AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func) const +{ + bool override_occurred = false; + return GetState(override_func, &override_occurred); +} + +AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func, + bool* override_occurred) const +{ + StateData state = GetState(); + if (!override_func) + return state; + + if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x)) + { + state.x = *x_override; + *override_occurred = true; + } + + if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y)) + { + state.y = *y_override; + *override_occurred = true; + } + + return state; +} + ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const { return m_stick_gate->GetRadiusAtAngle(ang); diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h index 47e3ad5743..377e63251d 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h @@ -21,6 +21,8 @@ public: ControlState GetGateRadiusAtAngle(double ang) const override; StateData GetState() const; + StateData GetState(const InputOverrideFunction& override_func) const; + StateData GetState(const InputOverrideFunction& override_func, bool* override_occurred) const; private: Control* GetModifierInput() const override; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h index 2d02adabf1..652f64ae5f 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h @@ -3,6 +3,7 @@ #pragma once +#include <cmath> #include <string> #include "InputCommon/ControlReference/ControlReference.h" @@ -24,5 +25,21 @@ public: for (auto& control : controls) *buttons |= *(bitmasks++) * control->GetState<bool>(); } + + template <typename C> + void GetState(C* const buttons, const C* bitmasks, + const InputOverrideFunction& override_func) const + { + if (!override_func) + return GetState(buttons, bitmasks); + + for (auto& control : controls) + { + ControlState state = control->GetState(); + if (std::optional<ControlState> state_override = override_func(name, control->name, state)) + state = *state_override; + *buttons |= *(bitmasks++) * (std::lround(state) > 0); + } + } }; } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h index c08c072aba..fb10bab074 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h @@ -5,14 +5,18 @@ #include <algorithm> #include <cmath> +#include <functional> #include <memory> +#include <optional> #include <string> +#include <string_view> #include <type_traits> #include <vector> #include "Common/CommonTypes.h" #include "Common/IniFile.h" #include "InputCommon/ControllerEmu/Control/Control.h" +#include "InputCommon/ControllerInterface/CoreDevice.h" namespace ControllerEmu { @@ -27,6 +31,9 @@ class NumericSetting; template <typename T> class SettingValue; +using InputOverrideFunction = std::function<std::optional<ControlState>( + const std::string_view group_name, const std::string_view control_name, ControlState state)>; + enum class GroupType { Other, diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp index 043ef37356..7359a4e66d 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp @@ -82,15 +82,28 @@ ControlState Cursor::GetGateRadiusAtAngle(double ang) const Cursor::StateData Cursor::GetState(const bool adjusted) { - if (!adjusted) - { - const auto raw_input = GetReshapableState(false); + const ReshapeData input = GetReshapableState(adjusted); + const StateData state = adjusted ? UpdateState(input) : StateData{input.x, input.y}; + return state; +} - return {raw_input.x, raw_input.y}; - } +Cursor::StateData Cursor::GetState(const bool adjusted, + const ControllerEmu::InputOverrideFunction& override_func) +{ + StateData state = GetState(adjusted); + if (!override_func) + return state; + + if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x)) + state.x = *x_override; + if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y)) + state.y = *y_override; - const auto input = GetReshapableState(true); + return state; +} +Cursor::StateData Cursor::UpdateState(Cursor::ReshapeData input) +{ // TODO: Using system time is ugly. // Kill this after state is moved into wiimote rather than this class. const auto now = Clock::now(); diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h index 5e64ce86dc..3710a40d92 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h @@ -29,6 +29,7 @@ public: // Modifies the state StateData GetState(bool adjusted); + StateData GetState(bool adjusted, const ControllerEmu::InputOverrideFunction& override_func); // Yaw movement in radians. ControlState GetTotalYaw() const; @@ -40,6 +41,8 @@ public: ControlState GetVerticalOffset() const; private: + Cursor::StateData UpdateState(Cursor::ReshapeData input); + // This is used to reduce the cursor speed for relative input // to something that makes sense with the default range. static constexpr double STEP_PER_SEC = 0.01 * 200; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp index 8c2f4cbac5..8783087650 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp @@ -4,6 +4,7 @@ #include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" #include <algorithm> +#include <cmath> #include <cstddef> #include <memory> #include <string> @@ -63,6 +64,53 @@ void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlSta } } +void MixedTriggers::GetState(u16* digital, const u16* bitmasks, ControlState* analog, + const InputOverrideFunction& override_func, bool adjusted) const +{ + if (!override_func) + return GetState(digital, bitmasks, analog, adjusted); + + const ControlState threshold = GetThreshold(); + ControlState deadzone = GetDeadzone(); + + // Return raw values. (used in UI) + if (!adjusted) + { + deadzone = 0.0; + } + + const int trigger_count = int(controls.size() / 2); + for (int i = 0; i != trigger_count; ++i) + { + bool button_bool = false; + const ControlState button_value = ApplyDeadzone(controls[i]->GetState(), deadzone); + ControlState analog_value = ApplyDeadzone(controls[trigger_count + i]->GetState(), deadzone); + + // Apply threshold: + if (button_value > threshold) + { + analog_value = 1.0; + button_bool = true; + } + + if (const std::optional<ControlState> button_override = + override_func(name, controls[i]->name, static_cast<ControlState>(button_bool))) + { + button_bool = std::lround(*button_override) > 0; + } + + if (const std::optional<ControlState> analog_override = + override_func(name, controls[trigger_count + i]->name, analog_value)) + { + analog_value = *analog_override; + } + + if (button_bool) + *digital |= bitmasks[i]; + analog[i] = std::min(analog_value, 1.0); + } +} + ControlState MixedTriggers::GetDeadzone() const { return m_deadzone_setting.GetValue() / 100; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h index fcd98b279e..2e1014d047 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h @@ -17,6 +17,8 @@ public: void GetState(u16* digital, const u16* bitmasks, ControlState* analog, bool adjusted = true) const; + void GetState(u16* digital, const u16* bitmasks, ControlState* analog, + const InputOverrideFunction& override_func, bool adjusted = true) const; ControlState GetDeadzone() const; ControlState GetThreshold() const; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp index f0516a567f..708831a4c3 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp @@ -35,4 +35,21 @@ Slider::StateData Slider::GetState() const return {std::clamp(ApplyDeadzone(state, deadzone), -1.0, 1.0)}; } + +Slider::StateData Slider::GetState(const InputOverrideFunction& override_func) const +{ + if (!override_func) + return GetState(); + + const ControlState deadzone = m_deadzone_setting.GetValue() / 100; + ControlState state = controls[1]->GetState() - controls[0]->GetState(); + + state = ApplyDeadzone(state, deadzone); + + if (std::optional<ControlState> state_override = override_func(name, X_INPUT_OVERRIDE, state)) + state = *state_override; + + return {std::clamp(state, -1.0, 1.0)}; +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h index 9d1bf697a5..0e635da235 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h @@ -23,6 +23,9 @@ public: explicit Slider(const std::string& name_); StateData GetState() const; + StateData GetState(const InputOverrideFunction& override_func) const; + + static constexpr const char* X_INPUT_OVERRIDE = "X"; private: SettingValue<double> m_deadzone_setting; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp index 17e64d2a8b..321c9272d0 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp @@ -6,6 +6,7 @@ #include <algorithm> #include <cstddef> #include <memory> +#include <optional> #include <string> #include "Common/Common.h" @@ -31,4 +32,25 @@ Triggers::StateData Triggers::GetState() const return result; } + +Triggers::StateData Triggers::GetState(const InputOverrideFunction& override_func) const +{ + if (!override_func) + return GetState(); + + const size_t trigger_count = controls.size(); + const ControlState deadzone = m_deadzone_setting.GetValue() / 100; + + StateData result(trigger_count); + for (size_t i = 0; i < trigger_count; ++i) + { + ControlState state = ApplyDeadzone(controls[i]->GetState(), deadzone); + if (std::optional<ControlState> state_override = override_func(name, controls[i]->name, state)) + state = *state_override; + result.data[i] = std::min(state, 1.0); + } + + return result; +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h index 2ea02bedb5..c73dee9cfa 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h @@ -26,6 +26,7 @@ public: explicit Triggers(const std::string& name); StateData GetState() const; + StateData GetState(const InputOverrideFunction& override_func) const; private: SettingValue<double> m_deadzone_setting; diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp index 0a3de3e391..dc9830de3f 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp @@ -6,6 +6,7 @@ #include <memory> #include <mutex> #include <string> +#include <utility> #include "Common/IniFile.h" @@ -176,4 +177,15 @@ void EmulatedController::LoadDefaults(const ControllerInterface& ciface) SetDefaultDevice(default_device_string); } } + +void EmulatedController::SetInputOverrideFunction(InputOverrideFunction override_func) +{ + m_input_override_function = std::move(override_func); +} + +void EmulatedController::ClearInputOverrideFunction() +{ + m_input_override_function = {}; +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h index 2db991eb82..e7e957e608 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h @@ -15,12 +15,18 @@ #include "Common/IniFile.h" #include "Common/MathUtil.h" #include "InputCommon/ControlReference/ExpressionParser.h" +#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "InputCommon/ControllerInterface/CoreDevice.h" class ControllerInterface; -const char* const named_directions[] = {_trans("Up"), _trans("Down"), _trans("Left"), - _trans("Right")}; +constexpr const char* DIRECTION_UP = _trans("Up"); +constexpr const char* DIRECTION_DOWN = _trans("Down"); +constexpr const char* DIRECTION_LEFT = _trans("Left"); +constexpr const char* DIRECTION_RIGHT = _trans("Right"); + +constexpr const char* named_directions[] = {DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, + DIRECTION_RIGHT}; class ControlReference; @@ -184,6 +190,9 @@ public: void SetDefaultDevice(const std::string& device); void SetDefaultDevice(ciface::Core::DeviceQualifier devq); + void SetInputOverrideFunction(InputOverrideFunction override_func); + void ClearInputOverrideFunction(); + void UpdateReferences(const ControllerInterface& devi); void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref); @@ -200,7 +209,7 @@ public: std::vector<std::unique_ptr<ControlGroup>> groups; - // Maps a float from -1.0..+1.0 to an integer of the provided values. + // Maps a float from -1.0..+1.0 to an integer in the provided range. template <typename T, typename F> static T MapFloat(F input_value, T zero_value, T neg_1_value = std::numeric_limits<T>::min(), T pos_1_value = std::numeric_limits<T>::max()) @@ -223,11 +232,28 @@ public: return T(std::llround((zero_value - neg_1_value) * input_value + zero_value)); } + // The inverse of the function above. + // Maps an integer in the provided range to a float in the range -1.0..1.0. + template <typename F, typename T> + static F MapToFloat(T input_value, T zero_value, T neg_1_value = std::numeric_limits<T>::min(), + T pos_1_value = std::numeric_limits<T>::max()) + { + static_assert(std::is_integral<T>(), "T is only sane for int types."); + static_assert(std::is_floating_point<F>(), "F is only sane for float types."); + + if (input_value >= zero_value) + return F(input_value - zero_value) / F(pos_1_value - zero_value); + else + return -F(zero_value - input_value) / F(zero_value - neg_1_value); + } + protected: // TODO: Wiimote attachments actually end up using their parent controller value for this, // so theirs won't be used (and thus shouldn't even exist). ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars; + InputOverrideFunction m_input_override_function; + void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env); private: diff --git a/Source/Core/InputCommon/ControllerEmu/StickGate.h b/Source/Core/InputCommon/ControllerEmu/StickGate.h index 528aa4a5c8..831762fda3 100644 --- a/Source/Core/InputCommon/ControllerEmu/StickGate.h +++ b/Source/Core/InputCommon/ControllerEmu/StickGate.h @@ -106,6 +106,10 @@ public: const ReshapeData& GetCenter() const; void SetCenter(ReshapeData center); + static constexpr const char* X_INPUT_OVERRIDE = "X"; + static constexpr const char* Y_INPUT_OVERRIDE = "Y"; + static constexpr const char* Z_INPUT_OVERRIDE = "Z"; + protected: ReshapeData Reshape(ControlState x, ControlState y, ControlState modifier = 0.0, ControlState clamp = 1.0) const; diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp b/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp index 7b15f92de9..d55284a65a 100644 --- a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp @@ -9,23 +9,11 @@ #include <unordered_map> #include <vector> -#include "Common/Assert.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/StringUtil.h" #include "Common/Thread.h" -#include "Core/Core.h" -#include "Core/HW/GCPad.h" -#include "Core/HW/GCPadEmu.h" -#include "Core/HW/Wiimote.h" -#include "Core/HW/WiimoteEmu/Extension/Classic.h" -#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h" -#include "Core/HW/WiimoteEmu/WiimoteEmu.h" - -#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" -#include "InputCommon/ControllerEmu/StickGate.h" - namespace ButtonManager { namespace @@ -700,39 +688,6 @@ float GetAxisValue(int pad_id, ButtonType axis) return value; } -double GetInputRadiusAtAngle(int pad_id, ButtonType stick, double angle) -{ - // To avoid a crash, don't access controllers before they've been initialized by the boot process - if (!Core::IsRunningAndStarted()) - return 0; - - ControllerEmu::ControlGroup* group; - - switch (stick) - { - case STICK_MAIN: - group = Pad::GetGroup(pad_id, PadGroup::MainStick); - break; - case STICK_C: - group = Pad::GetGroup(pad_id, PadGroup::CStick); - break; - case NUNCHUK_STICK: - group = Wiimote::GetNunchukGroup(pad_id, WiimoteEmu::NunchukGroup::Stick); - break; - case CLASSIC_STICK_LEFT: - group = Wiimote::GetClassicGroup(pad_id, WiimoteEmu::ClassicGroup::LeftStick); - break; - case CLASSIC_STICK_RIGHT: - group = Wiimote::GetClassicGroup(pad_id, WiimoteEmu::ClassicGroup::RightStick); - break; - default: - ASSERT(false); - return 0; - } - - return static_cast<ControllerEmu::ReshapableInput*>(group)->GetInputRadiusAtAngle(angle); -} - bool GamepadEvent(const std::string& dev, int button, int action) { auto it = m_controllers.find(dev); diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h b/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h index 136d15915f..08c4349c84 100644 --- a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h +++ b/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h @@ -272,9 +272,6 @@ void Init(const std::string&); bool GetButtonPressed(int pad_id, ButtonType button); float GetAxisValue(int pad_id, ButtonType axis); -// emu_pad_id is numbered 0 to 3 for both GC pads and Wiimotes -double GetInputRadiusAtAngle(int emu_pad_id, ButtonType stick, double angle); - bool GamepadEvent(const std::string& dev, int button, int action); void GamepadAxisEvent(const std::string& dev, int axis, float value); diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/InputOverrider.cpp b/Source/Core/InputCommon/ControllerInterface/Touch/InputOverrider.cpp new file mode 100644 index 0000000000..9ddbaffcd6 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Touch/InputOverrider.cpp @@ -0,0 +1,272 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "InputCommon/ControllerInterface/Touch/InputOverrider.h" + +#include <array> +#include <map> +#include <optional> +#include <string> +#include <string_view> +#include <utility> + +#include "Common/Assert.h" + +#include "Core/HW/GCPad.h" +#include "Core/HW/GCPadEmu.h" +#include "Core/HW/Wiimote.h" +#include "Core/HW/WiimoteEmu/Extension/Classic.h" +#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h" +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" + +#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" +#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" +#include "InputCommon/ControllerEmu/ControllerEmu.h" +#include "InputCommon/ControllerEmu/StickGate.h" +#include "InputCommon/ControllerInterface/CoreDevice.h" +#include "InputCommon/InputConfig.h" + +namespace ciface::Touch +{ +namespace +{ +struct InputState +{ + ControlState normal_state = 0; + ControlState override_state = 0; + bool overriding = false; +}; + +using ControlsMap = std::map<std::pair<std::string_view, std::string_view>, ControlID>; +using StateArray = std::array<InputState, ControlID::NUMBER_OF_CONTROLS>; + +std::array<StateArray, 4> s_state_arrays; + +const ControlsMap s_gcpad_controls_map = {{ + {{GCPad::BUTTONS_GROUP, GCPad::A_BUTTON}, ControlID::GCPAD_A_BUTTON}, + {{GCPad::BUTTONS_GROUP, GCPad::B_BUTTON}, ControlID::GCPAD_B_BUTTON}, + {{GCPad::BUTTONS_GROUP, GCPad::X_BUTTON}, ControlID::GCPAD_X_BUTTON}, + {{GCPad::BUTTONS_GROUP, GCPad::Y_BUTTON}, ControlID::GCPAD_Y_BUTTON}, + {{GCPad::BUTTONS_GROUP, GCPad::Z_BUTTON}, ControlID::GCPAD_Z_BUTTON}, + {{GCPad::BUTTONS_GROUP, GCPad::START_BUTTON}, ControlID::GCPAD_START_BUTTON}, + {{GCPad::DPAD_GROUP, DIRECTION_UP}, ControlID::GCPAD_DPAD_UP}, + {{GCPad::DPAD_GROUP, DIRECTION_DOWN}, ControlID::GCPAD_DPAD_DOWN}, + {{GCPad::DPAD_GROUP, DIRECTION_LEFT}, ControlID::GCPAD_DPAD_LEFT}, + {{GCPad::DPAD_GROUP, DIRECTION_RIGHT}, ControlID::GCPAD_DPAD_RIGHT}, + {{GCPad::TRIGGERS_GROUP, GCPad::L_DIGITAL}, ControlID::GCPAD_L_DIGITAL}, + {{GCPad::TRIGGERS_GROUP, GCPad::R_DIGITAL}, ControlID::GCPAD_R_DIGITAL}, + {{GCPad::TRIGGERS_GROUP, GCPad::L_ANALOG}, ControlID::GCPAD_L_ANALOG}, + {{GCPad::TRIGGERS_GROUP, GCPad::R_ANALOG}, ControlID::GCPAD_R_ANALOG}, + {{GCPad::MAIN_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE}, + ControlID::GCPAD_MAIN_STICK_X}, + {{GCPad::MAIN_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE}, + ControlID::GCPAD_MAIN_STICK_Y}, + {{GCPad::C_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE}, + ControlID::GCPAD_C_STICK_X}, + {{GCPad::C_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE}, + ControlID::GCPAD_C_STICK_Y}, +}}; + +const ControlsMap s_wiimote_controls_map = {{ + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::A_BUTTON}, + ControlID::WIIMOTE_A_BUTTON}, + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::B_BUTTON}, + ControlID::WIIMOTE_B_BUTTON}, + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::ONE_BUTTON}, + ControlID::WIIMOTE_ONE_BUTTON}, + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::TWO_BUTTON}, + ControlID::WIIMOTE_TWO_BUTTON}, + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::PLUS_BUTTON}, + ControlID::WIIMOTE_PLUS_BUTTON}, + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::MINUS_BUTTON}, + ControlID::WIIMOTE_MINUS_BUTTON}, + {{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::HOME_BUTTON}, + ControlID::WIIMOTE_HOME_BUTTON}, + {{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_UP}, ControlID::WIIMOTE_DPAD_UP}, + {{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_DOWN}, ControlID::WIIMOTE_DPAD_DOWN}, + {{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_LEFT}, ControlID::WIIMOTE_DPAD_LEFT}, + {{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_RIGHT}, ControlID::WIIMOTE_DPAD_RIGHT}, + {{WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE}, + ControlID::WIIMOTE_IR_X}, + {{WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE}, + ControlID::WIIMOTE_IR_Y}, +}}; + +const ControlsMap s_nunchuk_controls_map = {{ + {{WiimoteEmu::Nunchuk::BUTTONS_GROUP, WiimoteEmu::Nunchuk::C_BUTTON}, + ControlID::NUNCHUK_C_BUTTON}, + {{WiimoteEmu::Nunchuk::BUTTONS_GROUP, WiimoteEmu::Nunchuk::Z_BUTTON}, + ControlID::NUNCHUK_Z_BUTTON}, + {{WiimoteEmu::Nunchuk::STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE}, + ControlID::NUNCHUK_STICK_X}, + {{WiimoteEmu::Nunchuk::STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE}, + ControlID::NUNCHUK_STICK_Y}, +}}; + +const ControlsMap s_classic_controls_map = {{ + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::A_BUTTON}, + ControlID::CLASSIC_A_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::B_BUTTON}, + ControlID::CLASSIC_B_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::X_BUTTON}, + ControlID::CLASSIC_X_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::Y_BUTTON}, + ControlID::CLASSIC_Y_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::ZL_BUTTON}, + ControlID::CLASSIC_ZL_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::ZR_BUTTON}, + ControlID::CLASSIC_ZR_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::PLUS_BUTTON}, + ControlID::CLASSIC_PLUS_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::MINUS_BUTTON}, + ControlID::CLASSIC_MINUS_BUTTON}, + {{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::HOME_BUTTON}, + ControlID::CLASSIC_HOME_BUTTON}, + {{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_UP}, ControlID::CLASSIC_DPAD_UP}, + {{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_DOWN}, ControlID::CLASSIC_DPAD_DOWN}, + {{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_LEFT}, ControlID::CLASSIC_DPAD_LEFT}, + {{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_RIGHT}, ControlID::CLASSIC_DPAD_RIGHT}, + {{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::L_DIGITAL}, + ControlID::CLASSIC_L_DIGITAL}, + {{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::R_DIGITAL}, + ControlID::CLASSIC_R_DIGITAL}, + {{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::L_ANALOG}, + ControlID::CLASSIC_L_ANALOG}, + {{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::R_ANALOG}, + ControlID::CLASSIC_R_ANALOG}, + {{WiimoteEmu::Classic::LEFT_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE}, + ControlID::CLASSIC_LEFT_STICK_X}, + {{WiimoteEmu::Classic::LEFT_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE}, + ControlID::CLASSIC_LEFT_STICK_Y}, + {{WiimoteEmu::Classic::RIGHT_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE}, + ControlID::CLASSIC_RIGHT_STICK_X}, + {{WiimoteEmu::Classic::RIGHT_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE}, + ControlID::CLASSIC_RIGHT_STICK_Y}, +}}; + +ControllerEmu::InputOverrideFunction GetInputOverrideFunction(const ControlsMap& controls_map, + size_t i) +{ + StateArray& state_array = s_state_arrays[i]; + + return [&](std::string_view group_name, std::string_view control_name, + ControlState controller_state) -> std::optional<ControlState> { + const auto it = controls_map.find(std::make_pair(group_name, control_name)); + if (it == controls_map.end()) + return std::nullopt; + + const ControlID control = it->second; + InputState& input_state = state_array[control]; + if (input_state.normal_state != controller_state) + { + input_state.normal_state = controller_state; + input_state.overriding = false; + } + + return input_state.overriding ? std::make_optional(input_state.override_state) : std::nullopt; + }; +} + +} // namespace + +void RegisterGameCubeInputOverrider(int controller_index) +{ + Pad::GetConfig() + ->GetController(controller_index) + ->SetInputOverrideFunction(GetInputOverrideFunction(s_gcpad_controls_map, controller_index)); +} + +void RegisterWiiInputOverrider(int controller_index) +{ + auto* wiimote = + static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(controller_index)); + + wiimote->SetInputOverrideFunction( + GetInputOverrideFunction(s_wiimote_controls_map, controller_index)); + + auto& attachments = static_cast<ControllerEmu::Attachments*>( + wiimote->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments)) + ->GetAttachmentList(); + + attachments[WiimoteEmu::ExtensionNumber::NUNCHUK]->SetInputOverrideFunction( + GetInputOverrideFunction(s_nunchuk_controls_map, controller_index)); + attachments[WiimoteEmu::ExtensionNumber::CLASSIC]->SetInputOverrideFunction( + GetInputOverrideFunction(s_classic_controls_map, controller_index)); +} + +void UnregisterGameCubeInputOverrider(int controller_index) +{ + Pad::GetConfig()->GetController(controller_index)->ClearInputOverrideFunction(); + + for (size_t i = ControlID::FIRST_GC_CONTROL; i <= ControlID::LAST_GC_CONTROL; ++i) + s_state_arrays[controller_index][i].overriding = false; +} + +void UnregisterWiiInputOverrider(int controller_index) +{ + auto* wiimote = + static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(controller_index)); + + wiimote->ClearInputOverrideFunction(); + + auto& attachments = static_cast<ControllerEmu::Attachments*>( + wiimote->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments)) + ->GetAttachmentList(); + + attachments[WiimoteEmu::ExtensionNumber::NUNCHUK]->ClearInputOverrideFunction(); + attachments[WiimoteEmu::ExtensionNumber::CLASSIC]->ClearInputOverrideFunction(); + + for (size_t i = ControlID::FIRST_WII_CONTROL; i <= ControlID::LAST_WII_CONTROL; ++i) + s_state_arrays[controller_index][i].overriding = false; +} + +void SetControlState(int controller_index, ControlID control, double state) +{ + InputState& input_state = s_state_arrays[controller_index][control]; + + input_state.override_state = state; + input_state.overriding = true; +} + +void ClearControlState(int controller_index, ControlID control) +{ + InputState& input_state = s_state_arrays[controller_index][control]; + + input_state.overriding = false; +} + +double GetGateRadiusAtAngle(int controller_index, ControlID stick, double angle) +{ + ControllerEmu::ControlGroup* group; + + switch (stick) + { + case ControlID::GCPAD_MAIN_STICK_X: + case ControlID::GCPAD_MAIN_STICK_Y: + group = Pad::GetGroup(controller_index, PadGroup::MainStick); + break; + case ControlID::GCPAD_C_STICK_X: + case ControlID::GCPAD_C_STICK_Y: + group = Pad::GetGroup(controller_index, PadGroup::CStick); + break; + case ControlID::NUNCHUK_STICK_X: + case ControlID::NUNCHUK_STICK_Y: + group = Wiimote::GetNunchukGroup(controller_index, WiimoteEmu::NunchukGroup::Stick); + break; + case ControlID::CLASSIC_LEFT_STICK_X: + case ControlID::CLASSIC_LEFT_STICK_Y: + group = Wiimote::GetClassicGroup(controller_index, WiimoteEmu::ClassicGroup::LeftStick); + break; + case ControlID::CLASSIC_RIGHT_STICK_X: + case ControlID::CLASSIC_RIGHT_STICK_Y: + group = Wiimote::GetClassicGroup(controller_index, WiimoteEmu::ClassicGroup::RightStick); + break; + default: + ASSERT(false); + return 0; + } + + return static_cast<ControllerEmu::ReshapableInput*>(group)->GetGateRadiusAtAngle(angle); +} +} // namespace ciface::Touch diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/InputOverrider.h b/Source/Core/InputCommon/ControllerInterface/Touch/InputOverrider.h new file mode 100644 index 0000000000..6217b44d4e --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Touch/InputOverrider.h @@ -0,0 +1,89 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +namespace ciface::Touch +{ +enum ControlID +{ + GCPAD_A_BUTTON = 0, + GCPAD_B_BUTTON = 1, + GCPAD_X_BUTTON = 2, + GCPAD_Y_BUTTON = 3, + GCPAD_Z_BUTTON = 4, + GCPAD_START_BUTTON = 5, + GCPAD_DPAD_UP = 6, + GCPAD_DPAD_DOWN = 7, + GCPAD_DPAD_LEFT = 8, + GCPAD_DPAD_RIGHT = 9, + GCPAD_L_DIGITAL = 10, + GCPAD_R_DIGITAL = 11, + GCPAD_L_ANALOG = 12, + GCPAD_R_ANALOG = 13, + GCPAD_MAIN_STICK_X = 14, + GCPAD_MAIN_STICK_Y = 15, + GCPAD_C_STICK_X = 16, + GCPAD_C_STICK_Y = 17, + + WIIMOTE_A_BUTTON = 18, + WIIMOTE_B_BUTTON = 19, + WIIMOTE_ONE_BUTTON = 20, + WIIMOTE_TWO_BUTTON = 21, + WIIMOTE_PLUS_BUTTON = 22, + WIIMOTE_MINUS_BUTTON = 23, + WIIMOTE_HOME_BUTTON = 24, + WIIMOTE_DPAD_UP = 25, + WIIMOTE_DPAD_DOWN = 26, + WIIMOTE_DPAD_LEFT = 27, + WIIMOTE_DPAD_RIGHT = 28, + WIIMOTE_IR_X = 29, + WIIMOTE_IR_Y = 30, + + NUNCHUK_C_BUTTON = 31, + NUNCHUK_Z_BUTTON = 32, + NUNCHUK_STICK_X = 33, + NUNCHUK_STICK_Y = 34, + + CLASSIC_A_BUTTON = 35, + CLASSIC_B_BUTTON = 36, + CLASSIC_X_BUTTON = 37, + CLASSIC_Y_BUTTON = 38, + CLASSIC_ZL_BUTTON = 39, + CLASSIC_ZR_BUTTON = 40, + CLASSIC_PLUS_BUTTON = 41, + CLASSIC_MINUS_BUTTON = 42, + CLASSIC_HOME_BUTTON = 43, + CLASSIC_DPAD_UP = 44, + CLASSIC_DPAD_DOWN = 45, + CLASSIC_DPAD_LEFT = 46, + CLASSIC_DPAD_RIGHT = 47, + CLASSIC_L_DIGITAL = 48, + CLASSIC_R_DIGITAL = 49, + CLASSIC_L_ANALOG = 50, + CLASSIC_R_ANALOG = 51, + CLASSIC_LEFT_STICK_X = 52, + CLASSIC_LEFT_STICK_Y = 53, + CLASSIC_RIGHT_STICK_X = 54, + CLASSIC_RIGHT_STICK_Y = 55, + + NUMBER_OF_CONTROLS, + + FIRST_GC_CONTROL = GCPAD_A_BUTTON, + LAST_GC_CONTROL = GCPAD_C_STICK_Y, + FIRST_WII_CONTROL = WIIMOTE_A_BUTTON, + LAST_WII_CONTROL = CLASSIC_RIGHT_STICK_Y, + +}; +void RegisterGameCubeInputOverrider(int controller_index); +void RegisterWiiInputOverrider(int controller_index); +void UnregisterGameCubeInputOverrider(int controller_index); +void UnregisterWiiInputOverrider(int controller_index); + +void SetControlState(int controller_index, ControlID control, double state); +void ClearControlState(int controller_index, ControlID control); + +// Angle is in radians and should be non-negative +double GetGateRadiusAtAngle(int controller_index, ControlID stick, double angle); +} // namespace ciface::Touch |
