all repos — mgba @ e93240f90c75a5f174efaf1647a18a8102ce95e5

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/vfs.h"
 16
 17#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)
 18#define GUI_MOD KMOD_GUI
 19#else
 20#define GUI_MOD KMOD_CTRL
 21#endif
 22
 23bool GBASDLInitEvents(struct GBASDLEvents* context) {
 24	if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
 25		return false;
 26	}
 27
 28	SDL_JoystickEventState(SDL_ENABLE);
 29	int nJoysticks = SDL_NumJoysticks();
 30	if (nJoysticks > 0) {
 31		context->nJoysticks = nJoysticks;
 32		context->joysticks = calloc(context->nJoysticks, sizeof(SDL_Joystick*));
 33		size_t i;
 34		for (i = 0; i < context->nJoysticks; ++i) {
 35			context->joysticks[i] = SDL_JoystickOpen(i);
 36		}
 37	} else {
 38		context->nJoysticks = 0;
 39		context->joysticks = 0;
 40	}
 41
 42	context->playersAttached = 0;
 43
 44	size_t i;
 45	for (i = 0; i < MAX_PLAYERS; ++i) {
 46		context->preferredJoysticks[i] = 0;
 47		context->joysticksClaimed[i] = SIZE_MAX;
 48	}
 49
 50#if !SDL_VERSION_ATLEAST(2, 0, 0)
 51	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
 52#endif
 53	return true;
 54}
 55
 56void GBASDLDeinitEvents(struct GBASDLEvents* context) {
 57	size_t i;
 58	for (i = 0; i < context->nJoysticks; ++i) {
 59		SDL_JoystickClose(context->joysticks[i]);
 60	}
 61
 62	SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
 63}
 64
 65void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) {
 66	context->preferredJoysticks[0] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 0);
 67	context->preferredJoysticks[1] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 1);
 68	context->preferredJoysticks[2] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 2);
 69	context->preferredJoysticks[3] = GBAInputGetPreferredDevice(config, SDL_BINDING_BUTTON, 3);
 70}
 71
 72void GBASDLInitBindings(struct GBAInputMap* inputMap) {
 73#ifdef BUILD_PANDORA
 74	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_PAGEDOWN, GBA_KEY_A);
 75	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_END, GBA_KEY_B);
 76	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RSHIFT, GBA_KEY_L);
 77	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RCTRL, GBA_KEY_R);
 78	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LALT, GBA_KEY_START);
 79	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LCTRL, GBA_KEY_SELECT);
 80	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_UP, GBA_KEY_UP);
 81	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_DOWN, GBA_KEY_DOWN);
 82	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT);
 83	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT);
 84#elif SDL_VERSION_ATLEAST(2, 0, 0)
 85	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_X, GBA_KEY_A);
 86	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_Z, GBA_KEY_B);
 87	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_A, GBA_KEY_L);
 88	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_S, GBA_KEY_R);
 89	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_RETURN, GBA_KEY_START);
 90	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_BACKSPACE, GBA_KEY_SELECT);
 91	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_UP, GBA_KEY_UP);
 92	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_DOWN, GBA_KEY_DOWN);
 93	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_LEFT, GBA_KEY_LEFT);
 94	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDL_SCANCODE_RIGHT, GBA_KEY_RIGHT);
 95#else
 96	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_x, GBA_KEY_A);
 97	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_z, GBA_KEY_B);
 98	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_a, GBA_KEY_L);
 99	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_s, GBA_KEY_R);
100	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RETURN, GBA_KEY_START);
101	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_BACKSPACE, GBA_KEY_SELECT);
102	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_UP, GBA_KEY_UP);
103	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_DOWN, GBA_KEY_DOWN);
104	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT);
105	GBAInputBindKey(inputMap, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT);
106#endif
107
108	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 13, GBA_KEY_A);
109	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 14, GBA_KEY_B);
110	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 10, GBA_KEY_L);
111	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 11, GBA_KEY_R);
112	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 3, GBA_KEY_START);
113	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 0, GBA_KEY_SELECT);
114	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 4, GBA_KEY_UP);
115	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 6, GBA_KEY_DOWN);
116	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 7, GBA_KEY_LEFT);
117	GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 5, GBA_KEY_RIGHT);
118
119	struct GBAAxis description = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x4000, -0x4000 };
120	GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 0, &description);
121	description = (struct GBAAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 };
122	GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description);
123}
124
125bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
126	player->joystick = 0;
127	player->joystickIndex = SIZE_MAX;
128
129	if (events->playersAttached >= MAX_PLAYERS) {
130		return false;
131	}
132
133	player->playerId = events->playersAttached;
134	size_t firstUnclaimed = SIZE_MAX;
135
136	size_t i;
137	for (i = 0; i < events->nJoysticks; ++i) {
138		bool claimed = false;
139
140		int p;
141		for (p = 0; p < events->playersAttached; ++p) {
142			if (events->joysticksClaimed[p] == i) {
143				claimed = true;
144				break;
145			}
146		}
147		if (claimed) {
148			continue;
149		}
150
151		if (firstUnclaimed == SIZE_MAX) {
152			firstUnclaimed = i;
153		}
154
155		const char* joystickName;
156#if SDL_VERSION_ATLEAST(2, 0, 0)
157		joystickName = SDL_JoystickName(events->joysticks[i]);
158#else
159		joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i]));
160#endif
161		if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
162			player->joystickIndex = i;
163			break;
164		}
165	}
166
167	if (player->joystickIndex == SIZE_MAX && firstUnclaimed != SIZE_MAX) {
168		player->joystickIndex = firstUnclaimed;
169	}
170
171	if (player->joystickIndex != SIZE_MAX) {
172		player->joystick = events->joysticks[player->joystickIndex];
173		events->joysticksClaimed[player->playerId] = player->joystickIndex;
174	}
175
176	++events->playersAttached;
177	return true;
178}
179
180void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configuration* config) {
181	GBAInputMapLoad(context->bindings, SDL_BINDING_KEY, config);
182	if (context->joystick) {
183		GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
184#if SDL_VERSION_ATLEAST(2, 0, 0)
185		GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(context->joystick));
186#else
187		GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(SDL_JoystickIndex(context->joystick)));
188#endif
189	}
190}
191
192void GBASDLPlayerChangeJoystick(struct GBASDLEvents* events, struct GBASDLPlayer* player, size_t index) {
193	if (player->playerId > MAX_PLAYERS || index >= events->nJoysticks) {
194		return;
195	}
196	events->joysticksClaimed[player->playerId] = index;
197	player->joystickIndex = index;
198	player->joystick = events->joysticks[index];
199}
200
201static void _pauseAfterFrame(struct GBAThread* context) {
202	context->frameCallback = 0;
203	GBAThreadPauseFromThread(context);
204}
205
206static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
207	enum GBAKey key = GBA_KEY_NONE;
208	if (!event->keysym.mod) {
209#if !defined(BUILD_PANDORA) && SDL_VERSION_ATLEAST(2, 0, 0)
210		key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.scancode);
211#else
212		key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.sym);
213#endif
214	}
215	if (key != GBA_KEY_NONE) {
216		if (event->type == SDL_KEYDOWN) {
217			context->activeKeys |= 1 << key;
218		} else {
219			context->activeKeys &= ~(1 << key);
220		}
221		return;
222	}
223	if (event->keysym.sym == SDLK_TAB) {
224		context->sync.audioWait = event->type != SDL_KEYDOWN;
225		return;
226	}
227	if (event->type == SDL_KEYDOWN) {
228		switch (event->keysym.sym) {
229		case SDLK_F11:
230			if (context->debugger) {
231				ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL, 0);
232			}
233			return;
234#ifdef USE_PNG
235		case SDLK_F12:
236			GBAThreadInterrupt(context);
237			GBAThreadTakeScreenshot(context);
238			GBAThreadContinue(context);
239			return;
240#endif
241		case SDLK_BACKSLASH:
242			GBAThreadPause(context);
243			context->frameCallback = _pauseAfterFrame;
244			GBAThreadUnpause(context);
245			return;
246		case SDLK_BACKQUOTE:
247			GBAThreadInterrupt(context);
248			GBARewind(context, 10);
249			GBAThreadContinue(context);
250			return;
251#ifdef BUILD_PANDORA
252		case SDLK_ESCAPE:
253			GBAThreadEnd(context);
254			return;
255#endif
256		default:
257			if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) {
258				switch (event->keysym.sym) {
259#if SDL_VERSION_ATLEAST(2, 0, 0)
260				case SDLK_f:
261					SDL_SetWindowFullscreen(sdlContext->window, sdlContext->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
262					sdlContext->fullscreen = !sdlContext->fullscreen;
263					sdlContext->windowUpdated = 1;
264					break;
265#endif
266				case SDLK_p:
267					GBAThreadTogglePause(context);
268					break;
269				case SDLK_n:
270					GBAThreadPause(context);
271					context->frameCallback = _pauseAfterFrame;
272					GBAThreadUnpause(context);
273					break;
274				case SDLK_r:
275					GBAThreadReset(context);
276					break;
277				default:
278					break;
279				}
280			}
281			if (event->keysym.mod & KMOD_SHIFT) {
282				switch (event->keysym.sym) {
283				case SDLK_F1:
284				case SDLK_F2:
285				case SDLK_F3:
286				case SDLK_F4:
287				case SDLK_F5:
288				case SDLK_F6:
289				case SDLK_F7:
290				case SDLK_F8:
291				case SDLK_F9:
292					GBAThreadInterrupt(context);
293					GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
294					GBAThreadContinue(context);
295					break;
296				default:
297					break;
298				}
299			} else {
300				switch (event->keysym.sym) {
301				case SDLK_F1:
302				case SDLK_F2:
303				case SDLK_F3:
304				case SDLK_F4:
305				case SDLK_F5:
306				case SDLK_F6:
307				case SDLK_F7:
308				case SDLK_F8:
309				case SDLK_F9:
310					GBAThreadInterrupt(context);
311					GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1);
312					GBAThreadContinue(context);
313					break;
314				default:
315					break;
316				}
317			}
318			return;
319		}
320	}
321}
322
323static void _GBASDLHandleJoyButton(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_JoyButtonEvent* event) {
324	enum GBAKey key = 0;
325	key = GBAInputMapKey(sdlContext->bindings, SDL_BINDING_BUTTON, event->button);
326	if (key == GBA_KEY_NONE) {
327		return;
328	}
329
330	if (event->type == SDL_JOYBUTTONDOWN) {
331		context->activeKeys |= 1 << key;
332	} else {
333		context->activeKeys &= ~(1 << key);
334	}
335}
336
337static void _GBASDLHandleJoyHat(struct GBAThread* context, const struct SDL_JoyHatEvent* event) {
338	enum GBAKey key = 0;
339
340	if (event->value & SDL_HAT_UP) {
341		key |= 1 << GBA_KEY_UP;
342	}
343	if (event->value & SDL_HAT_LEFT) {
344		key |= 1 << GBA_KEY_LEFT;
345	}
346	if (event->value & SDL_HAT_DOWN) {
347		key |= 1 << GBA_KEY_DOWN;
348	}
349	if (event->value & SDL_HAT_RIGHT) {
350		key |= 1 << GBA_KEY_RIGHT;
351	}
352
353	context->activeKeys &= ~((1 << GBA_KEY_UP) | (1 << GBA_KEY_LEFT) | (1 << GBA_KEY_DOWN) | (1 << GBA_KEY_RIGHT));
354	context->activeKeys |= key;
355}
356
357static void _GBASDLHandleJoyAxis(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_JoyAxisEvent* event) {
358	int keys = context->activeKeys;
359
360	keys = GBAInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, keys);
361	enum GBAKey key = GBAInputMapAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, event->value);
362	if (key != GBA_KEY_NONE) {
363		keys |= 1 << key;
364	}
365
366	context->activeKeys = keys;
367}
368
369#if SDL_VERSION_ATLEAST(2, 0, 0)
370static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const struct SDL_WindowEvent* event) {
371	UNUSED(context);
372	switch (event->event) {
373	case SDL_WINDOWEVENT_SIZE_CHANGED:
374		sdlContext->windowUpdated = 1;
375		break;
376	}
377}
378#endif
379
380void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContext, const union SDL_Event* event) {
381	switch (event->type) {
382	case SDL_QUIT:
383		GBAThreadEnd(context);
384		break;
385#if SDL_VERSION_ATLEAST(2, 0, 0)
386	case SDL_WINDOWEVENT:
387		_GBASDLHandleWindowEvent(context, sdlContext, &event->window);
388		break;
389#endif
390	case SDL_KEYDOWN:
391	case SDL_KEYUP:
392		_GBASDLHandleKeypress(context, sdlContext, &event->key);
393		break;
394	case SDL_JOYBUTTONDOWN:
395	case SDL_JOYBUTTONUP:
396		_GBASDLHandleJoyButton(context, sdlContext, &event->jbutton);
397		break;
398	case SDL_JOYHATMOTION:
399		_GBASDLHandleJoyHat(context, &event->jhat);
400		break;
401	case SDL_JOYAXISMOTION:
402		_GBASDLHandleJoyAxis(context, sdlContext, &event->jaxis);
403		break;
404	}
405}