// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "controller.h" #include "analog_controller.h" #include "analog_joystick.h" #include "digital_controller.h" #include "fmt/format.h" #include "guncon.h" #include "host.h" #include "negcon.h" #include "playstation_mouse.h" #include "util/state_wrapper.h" static const Controller::ControllerInfo s_none_info = {ControllerType::None, "None", TRANSLATE_NOOP("ControllerType", "Not Connected"), nullptr, {}, {}, Controller::VibrationCapabilities::NoVibration}; static const Controller::ControllerInfo* s_controller_info[] = { &s_none_info, &DigitalController::INFO, &AnalogController::INFO, &AnalogJoystick::INFO, &NeGcon::INFO, &GunCon::INFO, &PlayStationMouse::INFO, }; Controller::Controller(u32 index) : m_index(index) { } Controller::~Controller() = default; void Controller::Reset() { } bool Controller::DoState(StateWrapper& sw, bool apply_input_state) { return !sw.HasError(); } void Controller::ResetTransferState() { } bool Controller::Transfer(const u8 data_in, u8* data_out) { *data_out = 0xFF; return false; } float Controller::GetBindState(u32 index) const { return 0.0f; } void Controller::SetBindState(u32 index, float value) { } u32 Controller::GetButtonStateBits() const { return 0; } bool Controller::InAnalogMode() const { return false; } std::optional Controller::GetAnalogInputBytes() const { return std::nullopt; } void Controller::LoadSettings(SettingsInterface& si, const char* section) { } std::unique_ptr Controller::Create(ControllerType type, u32 index) { switch (type) { case ControllerType::DigitalController: return DigitalController::Create(index); case ControllerType::AnalogController: return AnalogController::Create(index); case ControllerType::AnalogJoystick: return AnalogJoystick::Create(index); case ControllerType::GunCon: return GunCon::Create(index); case ControllerType::PlayStationMouse: return PlayStationMouse::Create(index); case ControllerType::NeGcon: return NeGcon::Create(index); case ControllerType::None: default: return {}; } } const char* Controller::GetDefaultPadType(u32 pad) { return Settings::GetControllerTypeName((pad == 0) ? Settings::DEFAULT_CONTROLLER_1_TYPE : Settings::DEFAULT_CONTROLLER_2_TYPE); } const Controller::ControllerInfo* Controller::GetControllerInfo(ControllerType type) { for (const ControllerInfo* info : s_controller_info) { if (type == info->type) return info; } return nullptr; } const Controller::ControllerInfo* Controller::GetControllerInfo(const std::string_view& name) { for (const ControllerInfo* info : s_controller_info) { if (name == info->name) return info; } return nullptr; } std::vector> Controller::GetControllerTypeNames() { std::vector> ret; for (const ControllerInfo* info : s_controller_info) ret.emplace_back(info->name, Host::TranslateToString("ControllerType", info->display_name)); return ret; } std::optional Controller::GetBindIndex(ControllerType type, const std::string_view& bind_name) { const ControllerInfo* info = GetControllerInfo(type); if (!info) return std::nullopt; for (u32 i = 0; i < static_cast(info->bindings.size()); i++) { if (bind_name == info->bindings[i].name) return i; } return std::nullopt; } Controller::VibrationCapabilities Controller::GetControllerVibrationCapabilities(const std::string_view& type) { const ControllerInfo* info = GetControllerInfo(type); return info ? info->vibration_caps : VibrationCapabilities::NoVibration; } std::tuple Controller::ConvertPadToPortAndSlot(u32 index) { if (index > 4) // [5,6,7] return std::make_tuple(1, index - 4); // 2B,2C,2D else if (index > 1) // [2,3,4] return std::make_tuple(0, index - 1); // 1B,1C,1D else // [0,1] return std::make_tuple(index, 0); // 1A,2A } u32 Controller::ConvertPortAndSlotToPad(u32 port, u32 slot) { if (slot == 0) return port; else if (port == 0) // slot=[0,1] return slot + 1; // 2,3,4 else return slot + 4; // 5,6,7 } bool Controller::PadIsMultitapSlot(u32 index) { return (index >= 2); } bool Controller::PortAndSlotIsMultitap(u32 port, u32 slot) { return (slot != 0); } std::string Controller::GetSettingsSection(u32 pad) { return fmt::format("Pad{}", pad + 1u); } bool Controller::InCircularDeadzone(float deadzone, float pos_x, float pos_y) { if (pos_x == 0.0f && pos_y == 0.0f) return false; // Compute the angle at the given position in the stick's square bounding box. const float theta = std::atan2(pos_y, pos_x); // Compute the position that the edge of the circle would be at, given the angle. const float dz_x = std::cos(theta) * deadzone; const float dz_y = std::sin(theta) * deadzone; // We're in the deadzone if our position is less than the circle edge. const bool in_x = (pos_x < 0.0f) ? (pos_x > dz_x) : (pos_x <= dz_x); const bool in_y = (pos_y < 0.0f) ? (pos_y > dz_y) : (pos_y <= dz_y); return (in_x && in_y); }