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