Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/pc/controller/controller_sdl2.c
7861 views
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <stdbool.h>
4
#include <math.h>
5
6
#ifdef TARGET_MACOS
7
#include <SDL.h>
8
#else
9
#include <SDL2/SDL.h>
10
#endif
11
12
#include <ultra64.h>
13
14
#include "controller_api.h"
15
#include "controller_sdl.h"
16
17
#include "game/settings.h"
18
19
#define DEADZONE_LEFT gControllerLeftDeadzone * 10
20
#define DEADZONE_RIGHT gControllerRightDeadzone * 10
21
22
// Checks if the button is pressed, and if so adds it to the pressedButtons variable
23
#define SET_BUTTON(SDL_BUTTON) if (SDL_GameControllerGetButton(sdl_cntrl, SDL_BUTTON)) pressedButtons |= 1 << (SDL_BUTTON + 1)
24
//Checks if the button is in the pressedButtons variable, and if so passes it to the emulated controls
25
#define CHECK_BUTTON(CONFIG_BUTTON, GAME_BUTTON) if ((pressedButtons & CONFIG_BUTTON) != 0) pad->button |= GAME_BUTTON
26
27
static bool init_ok;
28
static SDL_GameController *sdl_cntrl;
29
static bool haptics_enabled;
30
static SDL_Haptic *sdl_haptic;
31
static SDL_Haptic *controller_sdl_init_haptics(const int joy);
32
33
static void controller_sdl_ensure_open(void) {
34
if (!init_ok) {
35
return;
36
}
37
38
SDL_GameControllerUpdate();
39
40
if (sdl_cntrl != NULL && !SDL_GameControllerGetAttached(sdl_cntrl)) {
41
SDL_HapticClose(sdl_haptic);
42
SDL_GameControllerClose(sdl_cntrl);
43
sdl_cntrl = NULL;
44
sdl_haptic = NULL;
45
}
46
47
if (sdl_cntrl == NULL) {
48
for (int i = 0; i < SDL_NumJoysticks(); i++) {
49
if (SDL_IsGameController(i)) {
50
sdl_cntrl = SDL_GameControllerOpen(i);
51
if (sdl_cntrl != NULL) {
52
sdl_haptic = controller_sdl_init_haptics(i);
53
break;
54
}
55
}
56
}
57
}
58
}
59
60
static u32 controller_sdl_get_raw_buttons(void) {
61
int16_t ltrig;
62
int16_t rtrig;
63
u32 pressedButtons = 0;
64
65
if (!init_ok || sdl_cntrl == NULL) {
66
return 0;
67
}
68
69
ltrig = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
70
rtrig = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
71
72
SET_BUTTON(SDL_CONTROLLER_BUTTON_START);
73
SET_BUTTON(SDL_CONTROLLER_BUTTON_BACK);
74
SET_BUTTON(SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
75
SET_BUTTON(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
76
SET_BUTTON(SDL_CONTROLLER_BUTTON_A);
77
SET_BUTTON(SDL_CONTROLLER_BUTTON_B);
78
SET_BUTTON(SDL_CONTROLLER_BUTTON_X);
79
SET_BUTTON(SDL_CONTROLLER_BUTTON_Y);
80
SET_BUTTON(SDL_CONTROLLER_BUTTON_LEFTSTICK);
81
SET_BUTTON(SDL_CONTROLLER_BUTTON_RIGHTSTICK);
82
SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_UP);
83
SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_DOWN);
84
SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_LEFT);
85
SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
86
if (ltrig > 30 * 256) pressedButtons |= 1 << 22;
87
if (rtrig > 30 * 256) pressedButtons |= 1 << 23;
88
89
return pressedButtons;
90
}
91
92
static void controller_sdl_init(void) {
93
#if defined(_WIN32) || defined(_WIN64)
94
if (configBlockNonXinputControllers) {
95
// Prioritize XInput by disabling other joystick backends.
96
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
97
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "0");
98
}
99
#endif
100
101
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
102
fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
103
return;
104
}
105
106
int added = -1;
107
char *basePath = SDL_GetBasePath();
108
if (basePath != NULL) {
109
char dbPath[4096];
110
snprintf(dbPath, sizeof(dbPath), "%sgamecontrollerdb.txt", basePath);
111
added = SDL_GameControllerAddMappingsFromFile(dbPath);
112
SDL_free(basePath);
113
}
114
115
// Also try the current working directory just in case
116
if (added < 0) {
117
if (SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt") < 0) {
118
printf("Failed to load gamecontrollerdb.txt: %s\n", SDL_GetError());
119
}
120
}
121
122
haptics_enabled = (SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0);
123
if (!haptics_enabled) {
124
printf("Haptic init failed (ignoring): %s\n", SDL_GetError());
125
}
126
127
init_ok = true;
128
}
129
130
static SDL_Haptic *controller_sdl_init_haptics(const int joy) {
131
if (!haptics_enabled) return NULL;
132
133
SDL_Haptic *hap = SDL_HapticOpen(joy);
134
if (hap == NULL) return NULL;
135
136
if (SDL_HapticRumbleSupported(hap) != SDL_TRUE) {
137
SDL_HapticClose(hap);
138
return NULL;
139
}
140
141
if (SDL_HapticRumbleInit(hap) != 0) {
142
SDL_HapticClose(hap);
143
return NULL;
144
}
145
146
printf("controller %s has haptics support, rumble enabled\n", SDL_JoystickNameForIndex(joy));
147
return hap;
148
}
149
150
static void controller_sdl_read(OSContPad *pad) {
151
if (!init_ok) {
152
return;
153
}
154
155
controller_sdl_ensure_open();
156
if (sdl_cntrl == NULL) {
157
if (sdl_cntrl == NULL) {
158
return;
159
}
160
}
161
162
u32 pressedButtons = controller_sdl_get_raw_buttons();
163
164
CHECK_BUTTON(configButtonA, A_BUTTON);
165
CHECK_BUTTON(configButtonB, B_BUTTON);
166
CHECK_BUTTON(configButtonZ, Z_TRIG);
167
CHECK_BUTTON(configButtonStart, START_BUTTON);
168
CHECK_BUTTON(configButtonL, L_TRIG);
169
CHECK_BUTTON(configButtonR, R_TRIG);
170
CHECK_BUTTON(configButtonCUp, U_CBUTTONS);
171
CHECK_BUTTON(configButtonCDown, D_CBUTTONS);
172
CHECK_BUTTON(configButtonCLeft, L_CBUTTONS);
173
CHECK_BUTTON(configButtonCRight, R_CBUTTONS);
174
175
if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_UP))
176
pad->button |= U_JPAD;
177
if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_DOWN))
178
pad->button |= D_JPAD;
179
if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_LEFT))
180
pad->button |= L_JPAD;
181
if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
182
pad->button |= R_JPAD;
183
184
int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX);
185
int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY);
186
int16_t rightx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_RIGHTX);
187
int16_t righty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_RIGHTY);
188
189
#ifdef TARGET_WEB
190
// Firefox has a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1606562
191
// It sets down y to 32768.0f / 32767.0f, which is greater than the allowed 1.0f,
192
// which SDL then converts to a int16_t by multiplying by 32767.0f, which overflows into -32768.
193
// Maximum up will hence never become -32768 with the current version of SDL2,
194
// so this workaround should be safe in compliant browsers.
195
if (lefty == -32768) {
196
lefty = 32767;
197
}
198
if (righty == -32768) {
199
righty = 32767;
200
}
201
#endif
202
203
if (configImprovedCamera) {
204
uint32_t magnitude_sq2 = (uint32_t)(rightx * rightx);
205
if (magnitude_sq2 > (uint32_t)(DEADZONE_RIGHT * DEADZONE_RIGHT)) {
206
pad->stick2_x = rightx / 409;
207
}
208
}
209
else {
210
if (rightx < -0x4000) pad->button |= L_CBUTTONS;
211
if (rightx > 0x4000) pad->button |= R_CBUTTONS;
212
}
213
214
if (configVerticalCamera) {
215
uint32_t magnitude_sq2 = (uint32_t)(righty * righty);
216
if (magnitude_sq2 > (uint32_t)(DEADZONE_RIGHT * DEADZONE_RIGHT)) {
217
pad->stick2_y = righty / 409;
218
}
219
}
220
else {
221
if (righty < -0x4000) pad->button |= U_CBUTTONS;
222
if (righty > 0x4000) pad->button |= D_CBUTTONS;
223
}
224
225
//if (ltrig > 30 * 256) pad->button |= configButtonZL;
226
//if (rtrig > 30 * 256) pad->button |= configButtonZR;
227
228
uint32_t magnitude_sq = (uint32_t)(leftx * leftx) + (uint32_t)(lefty * lefty);
229
if (magnitude_sq > (uint32_t)(DEADZONE_LEFT * DEADZONE_LEFT)) {
230
// Game expects stick coordinates within -80..80
231
// 32768 / 409 = ~80
232
pad->stick_x = leftx / 409;
233
pad->stick_y = -lefty / 409;
234
}
235
}
236
237
static void controller_sdl_rumble_play(f32 strength, f32 length) {
238
if (sdl_haptic) {
239
SDL_HapticRumblePlay(sdl_haptic, strength, (u32)(length * 1000.0f));
240
}
241
else {
242
#if SDL_VERSION_ATLEAST(2, 0, 18)
243
uint16_t scaled_strength = strength * pow(2, 16) - 1;
244
if (SDL_GameControllerHasRumble(sdl_cntrl) == SDL_TRUE) {
245
SDL_GameControllerRumble(sdl_cntrl, scaled_strength, scaled_strength, (u32)(length * 1000.0f));
246
}
247
#endif
248
}
249
}
250
251
static void controller_sdl_rumble_stop(void) {
252
if (sdl_haptic) {
253
SDL_HapticRumbleStop(sdl_haptic);
254
}
255
else {
256
#if SDL_VERSION_ATLEAST(2, 0, 18)
257
if (SDL_GameControllerHasRumble(sdl_cntrl) == SDL_TRUE) {
258
SDL_GameControllerRumble(sdl_cntrl, 0, 0, 0);
259
}
260
#endif
261
}
262
}
263
264
struct ControllerAPI controller_sdl = {
265
controller_sdl_init,
266
controller_sdl_read,
267
controller_sdl_rumble_play,
268
controller_sdl_rumble_stop
269
};
270