CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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