Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/controller.cpp
4214 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "controller.h"
5
#include "analog_controller.h"
6
#include "analog_joystick.h"
7
#include "ddgo_controller.h"
8
#include "digital_controller.h"
9
#include "game_database.h"
10
#include "guncon.h"
11
#include "host.h"
12
#include "jogcon.h"
13
#include "justifier.h"
14
#include "negcon.h"
15
#include "negcon_rumble.h"
16
#include "playstation_mouse.h"
17
#include "system.h"
18
19
#include "util/state_wrapper.h"
20
21
#include "IconsPromptFont.h"
22
#include "fmt/format.h"
23
24
static const Controller::ControllerInfo s_none_info = {
25
ControllerType::None, "None", TRANSLATE_NOOP("ControllerType", "Not Connected"), ICON_PF_NO_CONTROLLER, {}, {}};
26
27
static constexpr std::array<const Controller::ControllerInfo*, static_cast<size_t>(ControllerType::Count)>
28
s_controller_info = {{
29
&s_none_info,
30
&DigitalController::INFO,
31
&AnalogController::INFO,
32
&AnalogJoystick::INFO,
33
&GunCon::INFO,
34
&PlayStationMouse::INFO,
35
&NeGcon::INFO,
36
&NeGconRumble::INFO,
37
&Justifier::INFO,
38
&DigitalController::INFO_POPN,
39
&DDGoController::INFO,
40
&JogCon::INFO,
41
}};
42
43
const std::array<u32, NUM_CONTROLLER_AND_CARD_PORTS> Controller::PortDisplayOrder = {{0, 2, 3, 4, 1, 5, 6, 7}};
44
45
std::string_view Controller::ControllerInfo::GetDisplayName() const
46
{
47
return Host::TranslateToStringView("ControllerType", display_name);
48
}
49
50
std::string_view Controller::ControllerInfo::GetBindingDisplayName(const ControllerBindingInfo& bi) const
51
{
52
return Host::TranslateToStringView(name, bi.display_name);
53
}
54
55
Controller::Controller(u32 index) : m_index(index)
56
{
57
}
58
59
Controller::~Controller() = default;
60
61
void Controller::Reset()
62
{
63
}
64
65
bool Controller::DoState(StateWrapper& sw, bool apply_input_state)
66
{
67
return !sw.HasError();
68
}
69
70
void Controller::ResetTransferState()
71
{
72
}
73
74
bool Controller::Transfer(const u8 data_in, u8* data_out)
75
{
76
*data_out = 0xFF;
77
return false;
78
}
79
80
float Controller::GetBindState(u32 index) const
81
{
82
return 0.0f;
83
}
84
85
void Controller::SetBindState(u32 index, float value)
86
{
87
}
88
89
u32 Controller::GetButtonStateBits() const
90
{
91
return 0;
92
}
93
94
float Controller::GetVibrationMotorState(u32 index) const
95
{
96
return 0.0f;
97
}
98
99
bool Controller::InAnalogMode() const
100
{
101
return false;
102
}
103
104
std::optional<u32> Controller::GetAnalogInputBytes() const
105
{
106
return std::nullopt;
107
}
108
109
u32 Controller::GetInputOverlayIconColor() const
110
{
111
return 0xFFFFFFFFu;
112
}
113
114
void Controller::LoadSettings(const SettingsInterface& si, const char* section, bool initial)
115
{
116
}
117
118
std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
119
{
120
switch (type)
121
{
122
case ControllerType::DigitalController:
123
case ControllerType::PopnController:
124
return DigitalController::Create(index, type);
125
126
case ControllerType::AnalogController:
127
return AnalogController::Create(index);
128
129
case ControllerType::AnalogJoystick:
130
return AnalogJoystick::Create(index);
131
132
case ControllerType::GunCon:
133
return GunCon::Create(index);
134
135
case ControllerType::Justifier:
136
return Justifier::Create(index);
137
138
case ControllerType::PlayStationMouse:
139
return PlayStationMouse::Create(index);
140
141
case ControllerType::NeGcon:
142
return NeGcon::Create(index);
143
144
case ControllerType::NeGconRumble:
145
return NeGconRumble::Create(index);
146
147
case ControllerType::DDGoController:
148
return DDGoController::Create(index);
149
150
case ControllerType::JogCon:
151
return JogCon::Create(index);
152
153
case ControllerType::None:
154
default:
155
return {};
156
}
157
}
158
159
const Controller::ControllerInfo& Controller::GetControllerInfo(ControllerType type)
160
{
161
DebugAssert(type < ControllerType::Count && s_controller_info[static_cast<size_t>(type)]);
162
return *s_controller_info[static_cast<size_t>(type)];
163
}
164
165
const Controller::ControllerInfo* Controller::GetControllerInfo(std::string_view name)
166
{
167
for (const ControllerInfo* info : s_controller_info)
168
{
169
if (name == info->name)
170
return info;
171
}
172
173
return nullptr;
174
}
175
176
const std::array<const Controller::ControllerInfo*, static_cast<size_t>(ControllerType::Count)>&
177
Controller::GetControllerInfoList()
178
{
179
return s_controller_info;
180
}
181
182
std::tuple<u32, u32> Controller::ConvertPadToPortAndSlot(u32 index)
183
{
184
if (index > 4) // [5,6,7]
185
return std::make_tuple(1, index - 4); // 2B,2C,2D
186
else if (index > 1) // [2,3,4]
187
return std::make_tuple(0, index - 1); // 1B,1C,1D
188
else // [0,1]
189
return std::make_tuple(index, 0); // 1A,2A
190
}
191
192
u32 Controller::ConvertPortAndSlotToPad(u32 port, u32 slot)
193
{
194
if (slot == 0)
195
return port;
196
else if (port == 0) // slot=[0,1]
197
return slot + 1; // 2,3,4
198
else
199
return slot + 4; // 5,6,7
200
}
201
202
bool Controller::PadIsMultitapSlot(u32 index)
203
{
204
return (index >= 2);
205
}
206
207
bool Controller::PortAndSlotIsMultitap(u32 port, u32 slot)
208
{
209
return (slot != 0);
210
}
211
212
const char* Controller::GetPortDisplayName(u32 port, u32 slot, bool mtap)
213
{
214
static constexpr const std::array<const char*, NUM_MULTITAPS> no_mtap_labels = {{"1", "2"}};
215
static constexpr const std::array<std::array<const char*, NUM_CONTROLLER_AND_CARD_PORTS_PER_MULTITAP>, NUM_MULTITAPS>
216
mtap_labels = {{{{"1A", "1B", "1C", "1D"}}, {{"2A", "2B", "2C", "2D"}}}};
217
218
DebugAssert(port < 2 && slot < 4);
219
return mtap ? mtap_labels[port][slot] : no_mtap_labels[port];
220
}
221
222
const char* Controller::GetPortDisplayName(u32 index)
223
{
224
const auto& [port, slot] = ConvertPadToPortAndSlot(index);
225
return GetPortDisplayName(port, slot, g_settings.IsMultitapPortEnabled(port));
226
}
227
228
std::string Controller::GetSettingsSection(u32 pad)
229
{
230
return fmt::format("Pad{}", pad + 1u);
231
}
232
233
bool Controller::InCircularDeadzone(float deadzone, float pos_x, float pos_y)
234
{
235
if (pos_x == 0.0f && pos_y == 0.0f)
236
return false;
237
238
// Compute the angle at the given position in the stick's square bounding box.
239
const float theta = std::atan2(pos_y, pos_x);
240
241
// Compute the position that the edge of the circle would be at, given the angle.
242
const float dz_x = std::cos(theta) * deadzone;
243
const float dz_y = std::sin(theta) * deadzone;
244
245
// We're in the deadzone if our position is less than the circle edge.
246
const bool in_x = (pos_x < 0.0f) ? (pos_x > dz_x) : (pos_x <= dz_x);
247
const bool in_y = (pos_y < 0.0f) ? (pos_y > dz_y) : (pos_y <= dz_y);
248
return (in_x && in_y);
249
}
250
251
bool Controller::CanStartInAnalogMode(ControllerType ctype)
252
{
253
if (!g_settings.apply_compatibility_settings)
254
return true;
255
256
const GameDatabase::Entry* dbentry = System::GetGameDatabaseEntry();
257
if (!dbentry)
258
return false;
259
260
return ((dbentry->supported_controllers & (1u << static_cast<u8>(ctype))) != 0 &&
261
!dbentry->HasTrait(GameDatabase::Trait::DisableAutoAnalogMode));
262
}
263
264