all repos — mgba @ f2829eef0133941c69d1b5ceea739548c0cd37fb

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