Large diffs are not rendered by default.

@@ -5,31 +5,48 @@

#include "DolphinQt/TAS/TASInputWindow.h"

namespace WiimoteCommon
{
class DataReportBuilder;
}

namespace WiimoteEmu
{
class EncryptionKey;
}
#include "Core/HW/WiimoteEmu/ExtensionPort.h"

class QGroupBox;
class QHideEvent;
class QShowEvent;
class QSpinBox;
class TASCheckBox;

namespace WiimoteEmu
{
class Extension;
class Wiimote;
} // namespace WiimoteEmu

namespace ControllerEmu
{
class Attachments;
}

class WiiTASInputWindow : public TASInputWindow
{
Q_OBJECT
public:
explicit WiiTASInputWindow(QWidget* parent, int num);
void GetValues(WiimoteCommon::DataReportBuilder& rpt, int ext,
const WiimoteEmu::EncryptionKey& key);

void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;

private:
void UpdateExt(u8 ext);
WiimoteEmu::Wiimote* GetWiimote();
ControllerEmu::Attachments* GetAttachments();
WiimoteEmu::Extension* GetExtension();

void UpdateExt();

WiimoteEmu::ExtensionNumber m_active_extension;
int m_num;

InputOverrider m_wiimote_overrider;
InputOverrider m_nunchuk_overrider;
InputOverrider m_classic_overrider;

TASCheckBox* m_a_button;
TASCheckBox* m_b_button;
TASCheckBox* m_1_button;
@@ -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
)
@@ -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);
@@ -21,6 +21,8 @@ class AnalogStick : public ReshapableInput
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;
@@ -3,6 +3,7 @@

#pragma once

#include <cmath>
#include <string>

#include "InputCommon/ControlReference/ControlReference.h"
@@ -24,5 +25,21 @@ class Buttons : public ControlGroup
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
@@ -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,
@@ -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();
@@ -29,6 +29,7 @@ class Cursor : public ReshapableInput

// 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 @@ class Cursor : public ReshapableInput
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;
@@ -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;
@@ -17,6 +17,8 @@ class MixedTriggers : public ControlGroup

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;
@@ -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
@@ -23,6 +23,9 @@ class Slider : public ControlGroup
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;
@@ -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
@@ -26,6 +26,7 @@ class Triggers : public ControlGroup
explicit Triggers(const std::string& name);

StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;

private:
SettingValue<double> m_deadzone_setting;
@@ -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
@@ -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 @@ class EmulatedController
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 @@ class EmulatedController

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 @@ class EmulatedController
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:
@@ -106,6 +106,10 @@ class ReshapableInput : public ControlGroup
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;
@@ -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);
@@ -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);

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