duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

analog_joystick.cpp (17721B)


      1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> and contributors.
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "analog_joystick.h"
      5 #include "host.h"
      6 #include "system.h"
      7 
      8 #include "util/imgui_manager.h"
      9 #include "util/state_wrapper.h"
     10 
     11 #include "common/bitutils.h"
     12 #include "common/log.h"
     13 #include "common/string_util.h"
     14 
     15 #include "IconsFontAwesome5.h"
     16 #include "IconsPromptFont.h"
     17 
     18 Log_SetChannel(AnalogJoystick);
     19 
     20 AnalogJoystick::AnalogJoystick(u32 index) : Controller(index)
     21 {
     22   m_axis_state.fill(0x80);
     23   Reset();
     24 }
     25 
     26 AnalogJoystick::~AnalogJoystick() = default;
     27 
     28 ControllerType AnalogJoystick::GetType() const
     29 {
     30   return ControllerType::AnalogJoystick;
     31 }
     32 
     33 bool AnalogJoystick::InAnalogMode() const
     34 {
     35   return m_analog_mode;
     36 }
     37 
     38 void AnalogJoystick::Reset()
     39 {
     40   m_transfer_state = TransferState::Idle;
     41 }
     42 
     43 bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state)
     44 {
     45   if (!Controller::DoState(sw, apply_input_state))
     46     return false;
     47 
     48   const bool old_analog_mode = m_analog_mode;
     49 
     50   sw.Do(&m_analog_mode);
     51 
     52   u16 button_state = m_button_state;
     53   auto axis_state = m_axis_state;
     54   sw.Do(&button_state);
     55   sw.Do(&axis_state);
     56 
     57   if (apply_input_state)
     58   {
     59     m_button_state = button_state;
     60     m_axis_state = axis_state;
     61   }
     62 
     63   sw.Do(&m_transfer_state);
     64 
     65   if (sw.IsReading() && (old_analog_mode != m_analog_mode))
     66   {
     67     Host::AddIconOSDMessage(
     68       fmt::format("analog_mode_toggle_{}", m_index), ICON_FA_GAMEPAD,
     69       m_analog_mode ? fmt::format(TRANSLATE_FS("Controller", "Controller {} switched to analog mode."), m_index + 1u) :
     70                       fmt::format(TRANSLATE_FS("Controller", "Controller {} switched to digital mode."), m_index + 1u));
     71   }
     72   return true;
     73 }
     74 
     75 float AnalogJoystick::GetBindState(u32 index) const
     76 {
     77   if (index >= static_cast<u32>(Button::Count))
     78   {
     79     const u32 sub_index = index - static_cast<u32>(Button::Count);
     80     if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
     81       return 0.0f;
     82 
     83     return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
     84   }
     85   else if (index < static_cast<u32>(Button::Mode))
     86   {
     87     return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
     88   }
     89   else
     90   {
     91     return 0.0f;
     92   }
     93 }
     94 
     95 void AnalogJoystick::SetBindState(u32 index, float value)
     96 {
     97   if (index == static_cast<s32>(Button::Mode))
     98   {
     99     // analog toggle
    100     if (value >= 0.5f)
    101       ToggleAnalogMode();
    102 
    103     return;
    104   }
    105   else if (index >= static_cast<u32>(Button::Count))
    106   {
    107     const u32 sub_index = index - static_cast<u32>(Button::Count);
    108     if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
    109       return;
    110 
    111     const u8 u8_value = static_cast<u8>(std::clamp(value * m_analog_sensitivity * 255.0f, 0.0f, 255.0f));
    112     if (u8_value != m_half_axis_state[sub_index])
    113       System::SetRunaheadReplayFlag();
    114 
    115     m_half_axis_state[sub_index] = u8_value;
    116 
    117 #define MERGE(pos, neg)                                                                                                \
    118   ((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
    119                                                      (127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
    120 
    121     switch (static_cast<HalfAxis>(sub_index))
    122     {
    123       case HalfAxis::LLeft:
    124       case HalfAxis::LRight:
    125         m_axis_state[static_cast<u8>(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ?
    126                                                        MERGE(HalfAxis::LLeft, HalfAxis::LRight) :
    127                                                        MERGE(HalfAxis::LRight, HalfAxis::LLeft);
    128         break;
    129 
    130       case HalfAxis::LDown:
    131       case HalfAxis::LUp:
    132         m_axis_state[static_cast<u8>(Axis::LeftY)] = ((m_invert_left_stick & 2u) != 0u) ?
    133                                                        MERGE(HalfAxis::LUp, HalfAxis::LDown) :
    134                                                        MERGE(HalfAxis::LDown, HalfAxis::LUp);
    135         break;
    136 
    137       case HalfAxis::RLeft:
    138       case HalfAxis::RRight:
    139         m_axis_state[static_cast<u8>(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ?
    140                                                         MERGE(HalfAxis::RLeft, HalfAxis::RRight) :
    141                                                         MERGE(HalfAxis::RRight, HalfAxis::RLeft);
    142         break;
    143 
    144       case HalfAxis::RDown:
    145       case HalfAxis::RUp:
    146         m_axis_state[static_cast<u8>(Axis::RightY)] = ((m_invert_right_stick & 2u) != 0u) ?
    147                                                         MERGE(HalfAxis::RUp, HalfAxis::RDown) :
    148                                                         MERGE(HalfAxis::RDown, HalfAxis::RUp);
    149         break;
    150 
    151       default:
    152         break;
    153     }
    154 
    155     if (m_analog_deadzone > 0.0f)
    156     {
    157 #define MERGE_F(pos, neg)                                                                                              \
    158   ((m_half_axis_state[static_cast<u32>(pos)] != 0) ?                                                                   \
    159      (static_cast<float>(m_half_axis_state[static_cast<u32>(pos)]) / 255.0f) :                                         \
    160      (static_cast<float>(m_half_axis_state[static_cast<u32>(neg)]) / -255.0f))
    161 
    162       float pos_x, pos_y;
    163       if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
    164       {
    165         pos_x = ((m_invert_left_stick & 1u) != 0u) ? MERGE_F(HalfAxis::LLeft, HalfAxis::LRight) :
    166                                                      MERGE_F(HalfAxis::LRight, HalfAxis::LLeft);
    167         pos_y = ((m_invert_left_stick & 2u) != 0u) ? MERGE_F(HalfAxis::LUp, HalfAxis::LDown) :
    168                                                      MERGE_F(HalfAxis::LDown, HalfAxis::LUp);
    169       }
    170       else
    171       {
    172         pos_x = ((m_invert_right_stick & 1u) != 0u) ? MERGE_F(HalfAxis::RLeft, HalfAxis::RRight) :
    173                                                       MERGE_F(HalfAxis::RRight, HalfAxis::RLeft);
    174         ;
    175         pos_y = ((m_invert_right_stick & 2u) != 0u) ? MERGE_F(HalfAxis::RUp, HalfAxis::RDown) :
    176                                                       MERGE_F(HalfAxis::RDown, HalfAxis::RUp);
    177       }
    178 
    179       if (InCircularDeadzone(m_analog_deadzone, pos_x, pos_y))
    180       {
    181         // Set to 127 (center).
    182         if (static_cast<HalfAxis>(sub_index) < HalfAxis::RLeft)
    183           m_axis_state[static_cast<u8>(Axis::LeftX)] = m_axis_state[static_cast<u8>(Axis::LeftY)] = 127;
    184         else
    185           m_axis_state[static_cast<u8>(Axis::RightX)] = m_axis_state[static_cast<u8>(Axis::RightY)] = 127;
    186       }
    187 #undef MERGE_F
    188     }
    189 
    190 #undef MERGE
    191 
    192     return;
    193   }
    194 
    195   const u16 bit = u16(1) << static_cast<u8>(index);
    196 
    197   if (value >= 0.5f)
    198   {
    199     if (m_button_state & bit)
    200       System::SetRunaheadReplayFlag();
    201 
    202     m_button_state &= ~(bit);
    203   }
    204   else
    205   {
    206     if (!(m_button_state & bit))
    207       System::SetRunaheadReplayFlag();
    208 
    209     m_button_state |= bit;
    210   }
    211 }
    212 
    213 u32 AnalogJoystick::GetButtonStateBits() const
    214 {
    215   return m_button_state ^ 0xFFFF;
    216 }
    217 
    218 std::optional<u32> AnalogJoystick::GetAnalogInputBytes() const
    219 {
    220   return m_axis_state[static_cast<size_t>(Axis::LeftY)] << 24 | m_axis_state[static_cast<size_t>(Axis::LeftX)] << 16 |
    221          m_axis_state[static_cast<size_t>(Axis::RightY)] << 8 | m_axis_state[static_cast<size_t>(Axis::RightX)];
    222 }
    223 
    224 void AnalogJoystick::ResetTransferState()
    225 {
    226   m_transfer_state = TransferState::Idle;
    227 }
    228 
    229 u16 AnalogJoystick::GetID() const
    230 {
    231   static constexpr u16 DIGITAL_MODE_ID = 0x5A41;
    232   static constexpr u16 ANALOG_MODE_ID = 0x5A53;
    233 
    234   return m_analog_mode ? ANALOG_MODE_ID : DIGITAL_MODE_ID;
    235 }
    236 
    237 void AnalogJoystick::ToggleAnalogMode()
    238 {
    239   m_analog_mode = !m_analog_mode;
    240 
    241   INFO_LOG("Joystick {} switched to {} mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
    242   Host::AddIconOSDMessage(
    243     fmt::format("analog_mode_toggle_{}", m_index), ICON_FA_GAMEPAD,
    244     m_analog_mode ? fmt::format(TRANSLATE_FS("Controller", "Controller {} switched to analog mode."), m_index + 1u) :
    245                     fmt::format(TRANSLATE_FS("Controller", "Controller {} switched to digital mode."), m_index + 1u));
    246 }
    247 
    248 bool AnalogJoystick::Transfer(const u8 data_in, u8* data_out)
    249 {
    250   switch (m_transfer_state)
    251   {
    252     case TransferState::Idle:
    253     {
    254       *data_out = 0xFF;
    255 
    256       if (data_in == 0x01)
    257       {
    258         m_transfer_state = TransferState::Ready;
    259         return true;
    260       }
    261       return false;
    262     }
    263 
    264     case TransferState::Ready:
    265     {
    266       if (data_in == 0x42)
    267       {
    268         *data_out = Truncate8(GetID());
    269         m_transfer_state = TransferState::IDMSB;
    270         return true;
    271       }
    272 
    273       *data_out = 0xFF;
    274       return false;
    275     }
    276 
    277     case TransferState::IDMSB:
    278     {
    279       *data_out = Truncate8(GetID() >> 8);
    280       m_transfer_state = TransferState::ButtonsLSB;
    281       return true;
    282     }
    283 
    284     case TransferState::ButtonsLSB:
    285     {
    286       *data_out = Truncate8(m_button_state);
    287       m_transfer_state = TransferState::ButtonsMSB;
    288       return true;
    289     }
    290 
    291     case TransferState::ButtonsMSB:
    292     {
    293       *data_out = Truncate8(m_button_state >> 8);
    294 
    295       m_transfer_state = m_analog_mode ? TransferState::RightAxisX : TransferState::Idle;
    296       return m_analog_mode;
    297     }
    298 
    299     case TransferState::RightAxisX:
    300     {
    301       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::RightX)]);
    302       m_transfer_state = TransferState::RightAxisY;
    303       return true;
    304     }
    305 
    306     case TransferState::RightAxisY:
    307     {
    308       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::RightY)]);
    309       m_transfer_state = TransferState::LeftAxisX;
    310       return true;
    311     }
    312 
    313     case TransferState::LeftAxisX:
    314     {
    315       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::LeftX)]);
    316       m_transfer_state = TransferState::LeftAxisY;
    317       return true;
    318     }
    319 
    320     case TransferState::LeftAxisY:
    321     {
    322       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::LeftY)]);
    323       m_transfer_state = TransferState::Idle;
    324       return false;
    325     }
    326 
    327     default:
    328     {
    329       UnreachableCode();
    330       return false;
    331     }
    332   }
    333 }
    334 
    335 std::unique_ptr<AnalogJoystick> AnalogJoystick::Create(u32 index)
    336 {
    337   return std::make_unique<AnalogJoystick>(index);
    338 }
    339 
    340 static const Controller::ControllerBindingInfo s_binding_info[] = {
    341 #define BUTTON(name, display_name, icon_name, button, genb)                                                            \
    342   {                                                                                                                    \
    343     name, display_name, icon_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb                      \
    344   }
    345 #define AXIS(name, display_name, icon_name, halfaxis, genb)                                                            \
    346   {                                                                                                                    \
    347     name, display_name, icon_name, static_cast<u32>(AnalogJoystick::Button::Count) + static_cast<u32>(halfaxis),       \
    348       InputBindingInfo::Type::HalfAxis, genb                                                                           \
    349   }
    350 
    351   // clang-format off
    352   BUTTON("Up", TRANSLATE_NOOP("AnalogJoystick", "D-Pad Up"), ICON_PF_DPAD_UP, AnalogJoystick::Button::Up, GenericInputBinding::DPadUp),
    353   BUTTON("Right", TRANSLATE_NOOP("AnalogJoystick", "D-Pad Right"), ICON_PF_DPAD_RIGHT, AnalogJoystick::Button::Right, GenericInputBinding::DPadRight),
    354   BUTTON("Down", TRANSLATE_NOOP("AnalogJoystick", "D-Pad Down"), ICON_PF_DPAD_DOWN, AnalogJoystick::Button::Down, GenericInputBinding::DPadDown),
    355   BUTTON("Left", TRANSLATE_NOOP("AnalogJoystick", "D-Pad Left"), ICON_PF_DPAD_LEFT, AnalogJoystick::Button::Left, GenericInputBinding::DPadLeft),
    356   BUTTON("Triangle", TRANSLATE_NOOP("AnalogJoystick", "Triangle"), ICON_PF_BUTTON_TRIANGLE, AnalogJoystick::Button::Triangle, GenericInputBinding::Triangle),
    357   BUTTON("Circle", TRANSLATE_NOOP("AnalogJoystick", "Circle"), ICON_PF_BUTTON_CIRCLE, AnalogJoystick::Button::Circle, GenericInputBinding::Circle),
    358   BUTTON("Cross", TRANSLATE_NOOP("AnalogJoystick", "Cross"), ICON_PF_BUTTON_CROSS, AnalogJoystick::Button::Cross, GenericInputBinding::Cross),
    359   BUTTON("Square", TRANSLATE_NOOP("AnalogJoystick", "Square"), ICON_PF_BUTTON_SQUARE, AnalogJoystick::Button::Square, GenericInputBinding::Square),
    360   BUTTON("Select", TRANSLATE_NOOP("AnalogJoystick", "Select"), ICON_PF_SELECT_SHARE, AnalogJoystick::Button::Select, GenericInputBinding::Select),
    361   BUTTON("Start", TRANSLATE_NOOP("AnalogJoystick", "Start"), ICON_PF_START, AnalogJoystick::Button::Start, GenericInputBinding::Start),
    362   BUTTON("Mode", TRANSLATE_NOOP("AnalogJoystick", "Mode Toggle"), ICON_PF_ANALOG_LEFT_RIGHT, AnalogJoystick::Button::Mode, GenericInputBinding::System),
    363   BUTTON("L1", TRANSLATE_NOOP("AnalogJoystick", "L1"), ICON_PF_LEFT_SHOULDER_L1, AnalogJoystick::Button::L1, GenericInputBinding::L1),
    364   BUTTON("R1", TRANSLATE_NOOP("AnalogJoystick", "R1"), ICON_PF_RIGHT_SHOULDER_R1, AnalogJoystick::Button::R1, GenericInputBinding::R1),
    365   BUTTON("L2", TRANSLATE_NOOP("AnalogJoystick", "L2"), ICON_PF_LEFT_TRIGGER_L2, AnalogJoystick::Button::L2, GenericInputBinding::L2),
    366   BUTTON("R2", TRANSLATE_NOOP("AnalogJoystick", "R2"), ICON_PF_RIGHT_TRIGGER_R2, AnalogJoystick::Button::R2, GenericInputBinding::R2),
    367   BUTTON("L3", TRANSLATE_NOOP("AnalogJoystick", "L3"), ICON_PF_LEFT_ANALOG_CLICK, AnalogJoystick::Button::L3, GenericInputBinding::L3),
    368   BUTTON("R3", TRANSLATE_NOOP("AnalogJoystick", "R3"), ICON_PF_RIGHT_ANALOG_CLICK, AnalogJoystick::Button::R3, GenericInputBinding::R3),
    369 
    370   AXIS("LLeft", TRANSLATE_NOOP("AnalogJoystick", "Left Stick Left"), ICON_PF_LEFT_ANALOG_LEFT, AnalogJoystick::HalfAxis::LLeft, GenericInputBinding::LeftStickLeft),
    371   AXIS("LRight", TRANSLATE_NOOP("AnalogJoystick", "Left Stick Right"), ICON_PF_LEFT_ANALOG_RIGHT, AnalogJoystick::HalfAxis::LRight, GenericInputBinding::LeftStickRight),
    372   AXIS("LDown", TRANSLATE_NOOP("AnalogJoystick", "Left Stick Down"), ICON_PF_LEFT_ANALOG_DOWN, AnalogJoystick::HalfAxis::LDown, GenericInputBinding::LeftStickDown),
    373   AXIS("LUp", TRANSLATE_NOOP("AnalogJoystick", "Left Stick Up"), ICON_PF_LEFT_ANALOG_UP, AnalogJoystick::HalfAxis::LUp, GenericInputBinding::LeftStickUp),
    374   AXIS("RLeft", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Left"), ICON_PF_RIGHT_ANALOG_LEFT, AnalogJoystick::HalfAxis::RLeft, GenericInputBinding::RightStickLeft),
    375   AXIS("RRight", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Right"), ICON_PF_RIGHT_ANALOG_RIGHT, AnalogJoystick::HalfAxis::RRight, GenericInputBinding::RightStickRight),
    376   AXIS("RDown", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Down"), ICON_PF_RIGHT_ANALOG_DOWN, AnalogJoystick::HalfAxis::RDown, GenericInputBinding::RightStickDown),
    377   AXIS("RUp", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Up"), ICON_PF_RIGHT_ANALOG_UP, AnalogJoystick::HalfAxis::RUp, GenericInputBinding::RightStickUp),
    378 // clang-format on
    379 
    380 #undef AXIS
    381 #undef BUTTON
    382 };
    383 
    384 static const char* s_invert_settings[] = {TRANSLATE_NOOP("AnalogJoystick", "Not Inverted"),
    385                                           TRANSLATE_NOOP("AnalogJoystick", "Invert Left/Right"),
    386                                           TRANSLATE_NOOP("AnalogJoystick", "Invert Up/Down"),
    387                                           TRANSLATE_NOOP("AnalogJoystick", "Invert Left/Right + Up/Down"), nullptr};
    388 
    389 static const SettingInfo s_settings[] = {
    390   {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATE_NOOP("AnalogJoystick", "Analog Deadzone"),
    391    TRANSLATE_NOOP("AnalogJoystick",
    392                   "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."),
    393    "1.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", nullptr, 100.0f},
    394   {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATE_NOOP("AnalogJoystick", "Analog Sensitivity"),
    395    TRANSLATE_NOOP(
    396      "AnalogJoystick",
    397      "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent "
    398      "controllers, e.g. DualShock 4, Xbox One Controller."),
    399    "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", nullptr, 100.0f},
    400   {SettingInfo::Type::IntegerList, "InvertLeftStick", TRANSLATE_NOOP("AnalogJoystick", "Invert Left Stick"),
    401    TRANSLATE_NOOP("AnalogJoystick", "Inverts the direction of the left analog stick."), "0", "0", "3", nullptr, nullptr,
    402    s_invert_settings, 0.0f},
    403   {SettingInfo::Type::IntegerList, "InvertRightStick", TRANSLATE_NOOP("AnalogJoystick", "Invert Right Stick"),
    404    TRANSLATE_NOOP("AnalogJoystick", "Inverts the direction of the right analog stick."), "0", "0", "3", nullptr,
    405    nullptr, s_invert_settings, 0.0f},
    406 };
    407 
    408 const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick,
    409                                                          "AnalogJoystick",
    410                                                          TRANSLATE_NOOP("ControllerType", "Analog Joystick"),
    411                                                          ICON_PF_GAMEPAD,
    412                                                          s_binding_info,
    413                                                          s_settings,
    414                                                          Controller::VibrationCapabilities::NoVibration};
    415 
    416 void AnalogJoystick::LoadSettings(SettingsInterface& si, const char* section, bool initial)
    417 {
    418   Controller::LoadSettings(si, section, initial);
    419   m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f);
    420   m_analog_sensitivity =
    421     std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f);
    422   m_invert_left_stick = static_cast<u8>(si.GetIntValue(section, "InvertLeftStick", 0));
    423   m_invert_right_stick = static_cast<u8>(si.GetIntValue(section, "InvertRightStick", 0));
    424 }