Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/SDL/SDLJoystick.cpp
3185 views
1
#include <string>
2
3
#include "Common/File/FileUtil.h"
4
#include "Common/File/VFS/VFS.h"
5
#include "Common/StringUtils.h"
6
#include "Common/System/NativeApp.h"
7
#include "Common/System/System.h"
8
#include "Common/Log.h"
9
10
#include "Core/Config.h"
11
#include "Core/KeyMap.h"
12
#include "SDL/SDLJoystick.h"
13
14
using namespace std;
15
16
static int SDLJoystickEventHandlerWrapper(void* userdata, SDL_Event* event) {
17
static_cast<SDLJoystick *>(userdata)->ProcessInput(*event);
18
return 0;
19
}
20
21
SDLJoystick::SDLJoystick(bool init_SDL ) : registeredAsEventHandler(false) {
22
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
23
if (init_SDL) {
24
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
25
}
26
27
const char *dbPath = "gamecontrollerdb.txt";
28
INFO_LOG(Log::System, "loading control pad mappings from %s:", dbPath);
29
30
size_t size;
31
u8 *mappingData = g_VFS.ReadFile(dbPath, &size);
32
if (mappingData) {
33
SDL_RWops *rw = SDL_RWFromConstMem(mappingData, size);
34
// 1 to free the rw after use
35
if (SDL_GameControllerAddMappingsFromRW(rw, 1) == -1) {
36
ERROR_LOG(Log::System, "Failed to read mapping data - corrupt?");
37
}
38
delete[] mappingData;
39
} else {
40
WARN_LOG(Log::System, "gamecontrollerdb.txt missing?");
41
}
42
setUpControllers();
43
}
44
45
void SDLJoystick::setUpControllers() {
46
int numjoys = SDL_NumJoysticks();
47
for (int i = 0; i < numjoys; i++) {
48
setUpController(i);
49
}
50
if (controllers.size() > 0) {
51
INFO_LOG(Log::System, "pad 1 has been assigned to control pad: %s", SDL_GameControllerName(controllers.front()));
52
}
53
}
54
55
void SDLJoystick::setUpController(int deviceIndex) {
56
static constexpr int cbGUID = 33;
57
char pszGUID[cbGUID];
58
59
if (!SDL_IsGameController(deviceIndex)) {
60
WARN_LOG(Log::System, "Control pad device %d not supported by SDL game controller database, attempting to create default mapping...", deviceIndex);
61
SDL_Joystick *joystick = SDL_JoystickOpen(deviceIndex);
62
if (joystick) {
63
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID);
64
// create default mapping - this is the PS3 dual shock mapping
65
const std::string safeName = ReplaceAll(SDL_JoystickName(joystick), ",", "");
66
std::string mapping = std::string(pszGUID) + "," + safeName + ",x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4";
67
if (SDL_GameControllerAddMapping(mapping.c_str()) == 1){
68
INFO_LOG(Log::System, "Added default mapping ok");
69
} else {
70
ERROR_LOG(Log::System, "Failed to add default mapping");
71
}
72
SDL_JoystickClose(joystick);
73
} else {
74
ERROR_LOG(Log::System, "Failed to get joystick identifier. Read-only device? Control pad device %d", deviceIndex);
75
}
76
} else {
77
SDL_Joystick *joystick = SDL_JoystickOpen(deviceIndex);
78
if (joystick) {
79
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID);
80
SDL_JoystickClose(joystick);
81
}
82
}
83
SDL_GameController *controller = SDL_GameControllerOpen(deviceIndex);
84
if (controller) {
85
if (SDL_GameControllerGetAttached(controller)) {
86
controllers.push_back(controller);
87
controllerDeviceMap[SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))] = deviceIndex;
88
INFO_LOG(Log::System, "found control pad: %s, loading mapping", SDL_GameControllerName(controller));
89
// NOTE: The case to InputDeviceID here is wrong, we should do some kind of lookup.
90
KeyMap::NotifyPadConnected((InputDeviceID)deviceIndex, std::string(pszGUID) + ": " + SDL_GameControllerName(controller));
91
const char *mapping = SDL_GameControllerMapping(controller);
92
if (!mapping) {
93
WARN_LOG(Log::System, "Could not find mapping in SDL2 controller database");
94
} else {
95
INFO_LOG(Log::System, "SUCCESS, mapping is: %s", mapping);
96
}
97
} else {
98
SDL_GameControllerClose(controller);
99
}
100
}
101
}
102
103
SDLJoystick::~SDLJoystick() {
104
if (registeredAsEventHandler) {
105
SDL_DelEventWatch(SDLJoystickEventHandlerWrapper, this);
106
}
107
for (auto & controller : controllers) {
108
SDL_GameControllerClose(controller);
109
}
110
}
111
112
void SDLJoystick::registerEventHandler() {
113
SDL_AddEventWatch(SDLJoystickEventHandlerWrapper, this);
114
registeredAsEventHandler = true;
115
}
116
117
InputKeyCode SDLJoystick::getKeycodeForButton(SDL_GameControllerButton button) {
118
switch (button) {
119
case SDL_CONTROLLER_BUTTON_DPAD_UP:
120
return NKCODE_DPAD_UP;
121
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
122
return NKCODE_DPAD_DOWN;
123
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
124
return NKCODE_DPAD_LEFT;
125
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
126
return NKCODE_DPAD_RIGHT;
127
case SDL_CONTROLLER_BUTTON_A:
128
return NKCODE_BUTTON_2;
129
case SDL_CONTROLLER_BUTTON_B:
130
return NKCODE_BUTTON_3;
131
case SDL_CONTROLLER_BUTTON_X:
132
return NKCODE_BUTTON_4;
133
case SDL_CONTROLLER_BUTTON_Y:
134
return NKCODE_BUTTON_1;
135
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
136
return NKCODE_BUTTON_5;
137
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
138
return NKCODE_BUTTON_6;
139
case SDL_CONTROLLER_BUTTON_START:
140
return NKCODE_BUTTON_10;
141
case SDL_CONTROLLER_BUTTON_BACK:
142
return NKCODE_BUTTON_9; // select button
143
case SDL_CONTROLLER_BUTTON_GUIDE:
144
return NKCODE_BACK; // pause menu
145
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
146
return NKCODE_BUTTON_THUMBL;
147
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
148
return NKCODE_BUTTON_THUMBR;
149
150
// Found these limits by checking out the SDL2 branch of the SDL repo, doing git blame, then `git tag --contains (commit)` etc.
151
#if SDL_VERSION_ATLEAST(2, 0, 16)
152
case SDL_CONTROLLER_BUTTON_MISC1:
153
return NKCODE_BUTTON_11;
154
#endif
155
#if SDL_VERSION_ATLEAST(2, 0, 28)
156
case SDL_CONTROLLER_BUTTON_PADDLE1:
157
return NKCODE_BUTTON_12;
158
case SDL_CONTROLLER_BUTTON_PADDLE2:
159
return NKCODE_BUTTON_13;
160
case SDL_CONTROLLER_BUTTON_PADDLE3:
161
return NKCODE_BUTTON_14;
162
case SDL_CONTROLLER_BUTTON_PADDLE4:
163
return NKCODE_BUTTON_15;
164
#endif
165
#if SDL_VERSION_ATLEAST(2, 0, 14)
166
case SDL_CONTROLLER_BUTTON_TOUCHPAD:
167
return NKCODE_BUTTON_16;
168
#endif
169
case SDL_CONTROLLER_BUTTON_INVALID:
170
default:
171
return NKCODE_UNKNOWN;
172
}
173
}
174
175
void SDLJoystick::ProcessInput(const SDL_Event &event){
176
switch (event.type) {
177
case SDL_CONTROLLERBUTTONDOWN:
178
if (event.cbutton.state == SDL_PRESSED) {
179
auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);
180
if (code != NKCODE_UNKNOWN) {
181
KeyInput key;
182
key.flags = KEY_DOWN;
183
key.keyCode = code;
184
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);
185
NativeKey(key);
186
}
187
}
188
break;
189
case SDL_CONTROLLERBUTTONUP:
190
if (event.cbutton.state == SDL_RELEASED) {
191
auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);
192
if (code != NKCODE_UNKNOWN) {
193
KeyInput key;
194
key.flags = KEY_UP;
195
key.keyCode = code;
196
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);
197
NativeKey(key);
198
}
199
}
200
break;
201
case SDL_CONTROLLERAXISMOTION:
202
{
203
InputDeviceID deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which);
204
// TODO: Can we really cast axis IDs like that? Do they match?
205
InputAxis axisId = (InputAxis)event.caxis.axis;
206
float value = event.caxis.value * (1.f / 32767.f);
207
if (value > 1.0f) value = 1.0f;
208
if (value < -1.0f) value = -1.0f;
209
// Filter duplicate axis values.
210
auto key = std::pair<InputDeviceID, InputAxis>(deviceId, axisId);
211
auto iter = prevAxisValue_.find(key);
212
if (iter == prevAxisValue_.end()) {
213
prevAxisValue_[key] = value;
214
} else if (iter->second != value) {
215
iter->second = value;
216
AxisInput axis;
217
axis.axisId = axisId;
218
axis.value = value;
219
axis.deviceId = deviceId;
220
NativeAxis(&axis, 1);
221
} // else ignore event.
222
break;
223
}
224
case SDL_CONTROLLERDEVICEREMOVED:
225
// for removal events, "which" is the instance ID for SDL_CONTROLLERDEVICEREMOVED
226
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
227
if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(*it)) == event.cdevice.which) {
228
SDL_GameControllerClose(*it);
229
controllers.erase(it);
230
break;
231
}
232
}
233
break;
234
case SDL_CONTROLLERDEVICEADDED:
235
// for add events, "which" is the device index!
236
int prevNumControllers = controllers.size();
237
setUpController(event.cdevice.which);
238
if (prevNumControllers == 0 && controllers.size() > 0) {
239
INFO_LOG(Log::System, "pad 1 has been assigned to control pad: %s", SDL_GameControllerName(controllers.front()));
240
}
241
break;
242
}
243
}
244
245
int SDLJoystick::getDeviceIndex(int instanceId) {
246
auto it = controllerDeviceMap.find(instanceId);
247
if (it == controllerDeviceMap.end()) {
248
// could not find device
249
return -1;
250
}
251
return it->second;
252
}
253
254