all repos — mgba @ b0b0b464131d25c8cab45594db6137c589dfc71e

mGBA Game Boy Advance Emulator

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 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->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
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->stateDir, event->keysym.sym - SDLK_F1 + 1);
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