src/platform/sdl/sdl-events.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "sdl-events.h"
7
8#include "debugger/debugger.h"
9#include "gba/io.h"
10#include "gba/supervisor/rr.h"
11#include "gba/serialize.h"
12#include "gba/video.h"
13#include "gba/renderers/video-software.h"
14#include "util/configuration.h"
15#include "util/formatting.h"
16#include "util/vfs.h"
17
18#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)
19#define GUI_MOD KMOD_GUI
20#else
21#define GUI_MOD KMOD_CTRL
22#endif
23
24#define GYRO_STEPS 100
25
26static void _GBASDLSetRumble(struct GBARumble* rumble, int enable);
27static int32_t _GBASDLReadTiltX(struct GBARotationSource* rumble);
28static int32_t _GBASDLReadTiltY(struct GBARotationSource* rumble);
29static int32_t _GBASDLReadGyroZ(struct GBARotationSource* rumble);
30static void _GBASDLRotationSample(struct GBARotationSource* source);
31
32bool GBASDLInitEvents(struct GBASDLEvents* context) {
33 int subsystem = SDL_INIT_JOYSTICK;
34#if SDL_VERSION_ATLEAST(2, 0, 0)
35 subsystem |= SDL_INIT_HAPTIC | SDL_INIT_VIDEO;
36
37 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
38#endif
39 if (SDL_InitSubSystem(subsystem) < 0) {
40 return false;
41 }
42
43 SDL_JoystickEventState(SDL_ENABLE);
44 int nJoysticks = SDL_NumJoysticks();
45 if (nJoysticks > 0) {
46 context->nJoysticks = nJoysticks;
47 context->joysticks = calloc(context->nJoysticks, sizeof(SDL_Joystick*));
48#if SDL_VERSION_ATLEAST(2, 0, 0)
49 context->haptic = calloc(context->nJoysticks, sizeof(SDL_Haptic*));
50#endif
51 size_t i;
52 for (i = 0; i < context->nJoysticks; ++i) {
53 context->joysticks[i] = SDL_JoystickOpen(i);
54#if SDL_VERSION_ATLEAST(2, 0, 0)
55 context->haptic[i] = SDL_HapticOpenFromJoystick(context->joysticks[i]);
56#endif
57 }
58 } else {
59 context->nJoysticks = 0;
60 context->joysticks = 0;
61 }
62
63 context->playersAttached = 0;
64
65 size_t i;
66 for (i = 0; i < MAX_PLAYERS; ++i) {
67 context->preferredJoysticks[i] = 0;
68 context->joysticksClaimed[i] = SIZE_MAX;
69 }
70
71#if !SDL_VERSION_ATLEAST(2, 0, 0)
72 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
73#else
74 context->screensaverSuspendDepth = 0;
75#endif
76 return true;
77}
78
79void GBASDLDeinitEvents(struct GBASDLEvents* context) {
80 size_t i;
81 for (i = 0; i < context->nJoysticks; ++i) {
82#if SDL_VERSION_ATLEAST(2, 0, 0)
83 SDL_HapticClose(context->haptic[i]);
84#endif
85 SDL_JoystickClose(context->joysticks[i]);
86 }
87
88 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
89}
90
91void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) {
92 context->preferredJoysticks[0] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 0);
93 context->preferredJoysticks[1] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 1);
94 context->preferredJoysticks[2] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 2);
95 context->preferredJoysticks[3] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 3);
96}
97
98void GBASDLInitBindings(struct GBAInputMap* inputMap) {
99#ifdef BUILD_PANDORA
100 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_PAGEDOWN, GBA_KEY_A);
101 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_END, GBA_KEY_B);
102 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RSHIFT, GBA_KEY_L);
103 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RCTRL, GBA_KEY_R);
104 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LALT, GBA_KEY_START);
105 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LCTRL, GBA_KEY_SELECT);
106 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_UP, GBA_KEY_UP);
107 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_DOWN, GBA_KEY_DOWN);
108 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT);
109 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT);
110#elif SDL_VERSION_ATLEAST(2, 0, 0)
111 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_X, GBA_KEY_A);
112 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_Z, GBA_KEY_B);
113 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_A, GBA_KEY_L);
114 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_S, GBA_KEY_R);
115 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_RETURN, GBA_KEY_START);
116 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_BACKSPACE, GBA_KEY_SELECT);
117 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_UP, GBA_KEY_UP);
118 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_DOWN, GBA_KEY_DOWN);
119 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_LEFT, GBA_KEY_LEFT);
120 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_RIGHT, GBA_KEY_RIGHT);
121#else
122 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_x, GBA_KEY_A);
123 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_z, GBA_KEY_B);
124 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_a, GBA_KEY_L);
125 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_s, GBA_KEY_R);
126 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RETURN, GBA_KEY_START);
127 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_BACKSPACE, GBA_KEY_SELECT);
128 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_UP, GBA_KEY_UP);
129 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_DOWN, GBA_KEY_DOWN);
130 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT);
131 GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT);
132#endif
133
134 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 13, GBA_KEY_A);
135 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 14, GBA_KEY_B);
136 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 10, GBA_KEY_L);
137 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 11, GBA_KEY_R);
138 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 3, GBA_KEY_START);
139 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 0, GBA_KEY_SELECT);
140 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 4, GBA_KEY_UP);
141 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 6, GBA_KEY_DOWN);
142 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 7, GBA_KEY_LEFT);
143 GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 5, GBA_KEY_RIGHT);
144
145 struct GBAAxis description = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x4000, -0x4000 };
146 GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 0, &description);
147 description = (struct GBAAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 };
148 GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description);
149}
150
151bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
152 player->joystick = 0;
153 player->joystickIndex = SIZE_MAX;
154
155#if SDL_VERSION_ATLEAST(2, 0, 0)
156 player->rumble.d.setRumble = _GBASDLSetRumble;
157 player->rumble.p = player;
158#endif
159
160 player->rotation.d.readTiltX = _GBASDLReadTiltX;
161 player->rotation.d.readTiltY = _GBASDLReadTiltY;
162 player->rotation.d.readGyroZ = _GBASDLReadGyroZ;
163 player->rotation.d.sample = _GBASDLRotationSample;
164 player->rotation.axisX = 2;
165 player->rotation.axisY = 3;
166 player->rotation.gyroSensitivity = 2.2e9f;
167 player->rotation.gyroX = 0;
168 player->rotation.gyroY = 1;
169 player->rotation.zDelta = 0;
170 CircleBufferInit(&player->rotation.zHistory, sizeof(float) * GYRO_STEPS);
171 player->rotation.p = player;
172
173 if (events->playersAttached >= MAX_PLAYERS) {
174 return false;
175 }
176
177 player->playerId = events->playersAttached;
178 size_t firstUnclaimed = SIZE_MAX;
179
180 size_t i;
181 for (i = 0; i < events->nJoysticks; ++i) {
182 bool claimed = false;
183
184 int p;
185 for (p = 0; p < events->playersAttached; ++p) {
186 if (events->joysticksClaimed[p] == i) {
187 claimed = true;
188 break;
189 }
190 }
191 if (claimed) {
192 continue;
193 }
194
195 if (firstUnclaimed == SIZE_MAX) {
196 firstUnclaimed = i;
197 }
198
199 const char* joystickName;
200#if SDL_VERSION_ATLEAST(2, 0, 0)
201 joystickName = SDL_JoystickName(events->joysticks[i]);
202#else
203 joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i]));
204#endif
205 if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
206 player->joystickIndex = i;
207 break;
208 }
209 }
210
211 if (player->joystickIndex == SIZE_MAX && firstUnclaimed != SIZE_MAX) {
212 player->joystickIndex = firstUnclaimed;
213 }
214
215 if (player->joystickIndex != SIZE_MAX) {
216 player->joystick = events->joysticks[player->joystickIndex];
217 events->joysticksClaimed[player->playerId] = player->joystickIndex;
218
219#if SDL_VERSION_ATLEAST(2, 0, 0)
220 player->haptic = events->haptic[player->joystickIndex];
221 if (player->haptic) {
222 SDL_HapticRumbleInit(player->haptic);
223 }
224#endif
225 }
226
227 ++events->playersAttached;
228 return true;
229}
230
231void GBASDLDetachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
232 events->joysticksClaimed[player->playerId] = SIZE_MAX;
233 CircleBufferDeinit(&player->rotation.zHistory);
234}
235
236void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configuration* config) {
237 GBAInputMapLoad(context->bindings, SDL_BINDING_KEY, config);
238 if (context->joystick) {
239 GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
240#if SDL_VERSION_ATLEAST(2, 0, 0)
241 const char* name = SDL_JoystickName(context->joystick);
242#else
243 const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
244#endif
245 GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
246
247 const char* value;
248 char* end;
249 int numAxes = SDL_JoystickNumAxes(context->joystick);
250 int axis;
251 value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", name);
252 if (value) {
253 axis = strtol(value, &end, 0);
254 if (axis >= 0 && axis < numAxes && end && !*end) {
255 context->rotation.axisX = axis;
256 }
257 }
258 value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisY", name);
259 if (value) {
260 axis = strtol(value, &end, 0);
261 if (axis >= 0 && axis < numAxes && end && !*end) {
262 context->rotation.axisY = axis;
263 }
264 }
265 value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisX", name);
266 if (value) {
267 axis = strtol(value, &end, 0);
268 if (axis >= 0 && axis < numAxes && end && !*end) {
269 context->rotation.gyroX = axis;
270 }
271 }
272 value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisY", name);
273 if (value) {
274 axis = strtol(value, &end, 0);
275 if (axis >= 0 && axis < numAxes && end && !*end) {
276 context->rotation.gyroY = axis;
277 }
278 }
279 value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "gyroSensitivity", name);
280 if (value) {
281 float sensitivity = strtof_u(value, &end);
282 if (end && !*end) {
283 context->rotation.gyroSensitivity = sensitivity;
284 }
285 }
286 }
287}
288
289void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configuration* config) {
290 if (context->joystick) {
291#if SDL_VERSION_ATLEAST(2, 0, 0)
292 const char* name = SDL_JoystickName(context->joystick);
293#else
294 const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
295#endif
296 char value[12];
297 snprintf(value, sizeof(value), "%i", context->rotation.axisX);
298 GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", value, name);
299 snprintf(value, sizeof(value), "%i", context->rotation.axisY);
300 GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisY", value, name);
301 snprintf(value, sizeof(value), "%i", context->rotation.gyroX);
302 GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisX", value, name);
303 snprintf(value, sizeof(value), "%i", context->rotation.gyroY);
304 GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "gyroAxisY", value, name);
305 snprintf(value, sizeof(value), "%g", context->rotation.gyroSensitivity);
306 GBAInputSetCustomValue(config, SDL_BINDING_BUTTON, "gyroSensitivity", value, name);
307 }
308}
309
310void GBASDLPlayerChangeJoystick(struct GBASDLEvents* events, struct GBASDLPlayer* player, size_t index) {
311 if (player->playerId >= MAX_PLAYERS || index >= events->nJoysticks) {
312 return;
313 }
314 events->joysticksClaimed[player->playerId] = index;
315 player->joystickIndex = index;
316 player->joystick = events->joysticks[index];
317#if SDL_VERSION_ATLEAST(2, 0, 0)
318 player->haptic = events->haptic[index];
319#endif
320}
321
322static void _pauseAfterFrame(struct GBAThread* context) {
323 context->frameCallback = 0;
324 GBAThreadPauseFromThread(context);
325}
326
327static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
328 enum GBAKey key = GBA_KEY_NONE;
329 if (!event->keysym.mod) {
330#if !defined(BUILD_PANDORA) && SDL_VERSION_ATLEAST(2, 0, 0)
331 key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.scancode);
332#else
333 key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.sym);
334#endif
335 }
336 if (key != GBA_KEY_NONE) {
337 if (event->type == SDL_KEYDOWN) {
338 context->activeKeys |= 1 << key;
339 } else {
340 context->activeKeys &= ~(1 << key);
341 }
342 return;
343 }
344 if (event->keysym.sym == SDLK_TAB) {
345 context->sync.audioWait = event->type != SDL_KEYDOWN;
346 return;
347 }
348 if (event->type == SDL_KEYDOWN) {
349 switch (event->keysym.sym) {
350 case SDLK_F11:
351 if (context->debugger) {
352 ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL, 0);
353 }
354 return;
355#ifdef USE_PNG
356 case SDLK_F12:
357 GBAThreadInterrupt(context);
358 GBAThreadTakeScreenshot(context);
359 GBAThreadContinue(context);
360 return;
361#endif
362 case SDLK_BACKSLASH:
363 GBAThreadPause(context);
364 context->frameCallback = _pauseAfterFrame;
365 GBAThreadUnpause(context);
366 return;
367 case SDLK_BACKQUOTE:
368 GBAThreadInterrupt(context);
369 GBARewind(context, 10);
370 GBAThreadContinue(context);
371 return;
372#ifdef BUILD_PANDORA
373 case SDLK_ESCAPE:
374 GBAThreadEnd(context);
375 return;
376#endif
377 default:
378 if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) {
379 switch (event->keysym.sym) {
380#if SDL_VERSION_ATLEAST(2, 0, 0)
381 case SDLK_f:
382 SDL_SetWindowFullscreen(sdlContext->window, sdlContext->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
383 sdlContext->fullscreen = !sdlContext->fullscreen;
384 sdlContext->windowUpdated = 1;
385 break;
386#endif
387 case SDLK_p:
388 GBAThreadTogglePause(context);
389 break;
390 case SDLK_n:
391 GBAThreadPause(context);
392 context->frameCallback = _pauseAfterFrame;
393 GBAThreadUnpause(context);
394 break;
395 case SDLK_r:
396 GBAThreadReset(context);
397 break;
398 default:
399 break;
400 }
401 }
402 if (event->keysym.mod & KMOD_SHIFT) {
403 switch (event->keysym.sym) {
404 case SDLK_F1:
405 case SDLK_F2:
406 case SDLK_F3:
407 case SDLK_F4:
408 case SDLK_F5:
409 case SDLK_F6:
410 case SDLK_F7:
411 case SDLK_F8:
412 case SDLK_F9:
413 GBAThreadInterrupt(context);
414 GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
415 GBAThreadContinue(context);
416 break;
417 default:
418 break;
419 }
420 } else {
421 switch (event->keysym.sym) {
422 case SDLK_F1:
423 case SDLK_F2:
424 case SDLK_F3:
425 case SDLK_F4:
426 case SDLK_F5:
427 case SDLK_F6:
428 case SDLK_F7:
429 case SDLK_F8:
430 case SDLK_F9:
431 GBAThreadInterrupt(context);
432 GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1);
433 GBAThreadContinue(context);
434 break;
435 default:
436 break;
437 }
438 }
439 return;
440 }
441 }
442}
443
444static void _GBASDLHandleJoyButton(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_JoyButtonEvent* event) {
445 enum GBAKey key = 0;
446 key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_BUTTON, event->button);
447 if (key == GBA_KEY_NONE) {
448 return;
449 }
450
451 if (event->type == SDL_JOYBUTTONDOWN) {
452 context->activeKeys |= 1 << key;
453 } else {
454 context->activeKeys &= ~(1 << key);
455 }
456}
457
458static void _GBASDLHandleJoyHat(struct GBAThread* context, const struct SDL_JoyHatEvent* event) {
459 enum GBAKey key = 0;
460
461 if (event->value & SDL_HAT_UP) {
462 key |= 1 << GBA_KEY_UP;
463 }
464 if (event->value & SDL_HAT_LEFT) {
465 key |= 1 << GBA_KEY_LEFT;
466 }
467 if (event->value & SDL_HAT_DOWN) {
468 key |= 1 << GBA_KEY_DOWN;
469 }
470 if (event->value & SDL_HAT_RIGHT) {
471 key |= 1 << GBA_KEY_RIGHT;
472 }
473
474 context->activeKeys &= ~((1 << GBA_KEY_UP) | (1 << GBA_KEY_LEFT) | (1 << GBA_KEY_DOWN) | (1 << GBA_KEY_RIGHT));
475 context->activeKeys |= key;
476}
477
478static void _GBASDLHandleJoyAxis(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_JoyAxisEvent* event) {
479 int keys = context->activeKeys;
480
481 keys = GBAInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, keys);
482 enum GBAKey key = GBAInputMapAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, event->value);
483 if (key != GBA_KEY_NONE) {
484 keys |= 1 << key;
485 }
486
487 context->activeKeys = keys;
488}
489
490#if SDL_VERSION_ATLEAST(2, 0, 0)
491static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_WindowEvent* event) {
492 UNUSED(context);
493 switch (event->event) {
494 case SDL_WINDOWEVENT_SIZE_CHANGED:
495 sdlContext->windowUpdated = 1;
496 break;
497 }
498}
499#endif
500
501void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const union SDL_Event* event) {
502 switch (event->type) {
503 case SDL_QUIT:
504 GBAThreadEnd(context);
505 break;
506#if SDL_VERSION_ATLEAST(2, 0, 0)
507 case SDL_WINDOWEVENT:
508 _GBASDLHandleWindowEvent(context, sdlContext, &event->window);
509 break;
510#endif
511 case SDL_KEYDOWN:
512 case SDL_KEYUP:
513 _GBASDLHandleKeypress(context, sdlContext, &event->key);
514 break;
515 case SDL_JOYBUTTONDOWN:
516 case SDL_JOYBUTTONUP:
517 _GBASDLHandleJoyButton(context, sdlContext, &event->jbutton);
518 break;
519 case SDL_JOYHATMOTION:
520 _GBASDLHandleJoyHat(context, &event->jhat);
521 break;
522 case SDL_JOYAXISMOTION:
523 _GBASDLHandleJoyAxis(context, sdlContext, &event->jaxis);
524 break;
525 }
526}
527
528#if SDL_VERSION_ATLEAST(2, 0, 0)
529static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) {
530 struct GBASDLRumble* sdlRumble = (struct GBASDLRumble*) rumble;
531 if (!sdlRumble->p->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->haptic)) {
532 return;
533 }
534 if (enable) {
535 SDL_HapticRumblePlay(sdlRumble->p->haptic, 1.0f, 20);
536 } else {
537 SDL_HapticRumbleStop(sdlRumble->p->haptic);
538 }
539}
540#endif
541
542static int32_t _readTilt(struct GBASDLPlayer* player, int axis) {
543 return SDL_JoystickGetAxis(player->joystick, axis) * 0x3800;
544}
545
546static int32_t _GBASDLReadTiltX(struct GBARotationSource* source) {
547 struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
548 return _readTilt(rotation->p, rotation->axisX);
549}
550
551static int32_t _GBASDLReadTiltY(struct GBARotationSource* source) {
552 struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
553 return _readTilt(rotation->p, rotation->axisY);
554}
555
556static int32_t _GBASDLReadGyroZ(struct GBARotationSource* source) {
557 struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
558 float z = rotation->zDelta;
559 return z * rotation->gyroSensitivity;
560}
561
562static void _GBASDLRotationSample(struct GBARotationSource* source) {
563 struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
564 SDL_JoystickUpdate();
565
566 int x = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroX);
567 int y = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroY);
568 union {
569 float f;
570 int32_t i;
571 } theta = { .f = atan2f(y, x) - atan2f(rotation->oldY, rotation->oldX) };
572 if (isnan(theta.f)) {
573 theta.f = 0.0f;
574 } else if (theta.f > M_PI) {
575 theta.f -= 2.0f * M_PI;
576 } else if (theta.f < -M_PI) {
577 theta.f += 2.0f * M_PI;
578 }
579 rotation->oldX = x;
580 rotation->oldY = y;
581
582 float oldZ = 0;
583 if (CircleBufferSize(&rotation->zHistory) == GYRO_STEPS * sizeof(float)) {
584 CircleBufferRead32(&rotation->zHistory, (int32_t*) &oldZ);
585 }
586 CircleBufferWrite32(&rotation->zHistory, theta.i);
587 rotation->zDelta += theta.f - oldZ;
588}
589
590#if SDL_VERSION_ATLEAST(2, 0, 0)
591void GBASDLSuspendScreensaver(struct GBASDLEvents* events) {
592 if (events->screensaverSuspendDepth == 0 && events->screensaverSuspendable) {
593 SDL_DisableScreenSaver();
594 }
595 ++events->screensaverSuspendDepth;
596}
597
598void GBASDLResumeScreensaver(struct GBASDLEvents* events) {
599 --events->screensaverSuspendDepth;
600 if (events->screensaverSuspendDepth == 0 && events->screensaverSuspendable) {
601 SDL_EnableScreenSaver();
602 }
603}
604
605void GBASDLSetScreensaverSuspendable(struct GBASDLEvents* events, bool suspendable) {
606 bool wasSuspendable = events->screensaverSuspendable;
607 events->screensaverSuspendable = suspendable;
608 if (events->screensaverSuspendDepth > 0) {
609 if (suspendable && !wasSuspendable) {
610 SDL_DisableScreenSaver();
611 } else if (!suspendable && wasSuspendable) {
612 SDL_EnableScreenSaver();
613 }
614 } else {
615 SDL_EnableScreenSaver();
616 }
617}
618#endif