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