src/platform/sdl/sdl-events.c (view raw)
1#include "sdl-events.h"
2
3#include "debugger/debugger.h"
4#include "gba-io.h"
5#include "gba-rr.h"
6#include "gba-serialize.h"
7#include "gba-video.h"
8#include "renderers/video-software.h"
9#include "util/vfs.h"
10
11#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__)
12#define GUI_MOD KMOD_GUI
13#else
14#define GUI_MOD KMOD_CTRL
15#endif
16
17#define SDL_BINDING_KEY 0x53444C4B
18#define SDL_BINDING_BUTTON 0x53444C42
19
20bool GBASDLInitEvents(struct GBASDLEvents* context) {
21 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
22 return false;
23 }
24 SDL_JoystickEventState(SDL_ENABLE);
25 context->joystick = SDL_JoystickOpen(0);
26#if !SDL_VERSION_ATLEAST(2, 0, 0)
27 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
28#endif
29
30 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_z, GBA_KEY_A);
31 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_x, GBA_KEY_B);
32 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_a, GBA_KEY_L);
33 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_s, GBA_KEY_R);
34 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_RETURN, GBA_KEY_START);
35 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_BACKSPACE, GBA_KEY_SELECT);
36 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_UP, GBA_KEY_UP);
37 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_DOWN, GBA_KEY_DOWN);
38 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT);
39 GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT);
40
41 GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 2, GBA_KEY_A);
42 GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 1, GBA_KEY_B);
43 GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 6, GBA_KEY_L);
44 GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 7, GBA_KEY_R);
45 GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 8, GBA_KEY_START);
46 GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 9, GBA_KEY_SELECT);
47 return true;
48}
49
50void GBASDLDeinitEvents(struct GBASDLEvents* context) {
51 SDL_JoystickClose(context->joystick);
52 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
53}
54
55static void _pauseAfterFrame(struct GBAThread* context) {
56 context->frameCallback = 0;
57 GBAThreadPauseFromThread(context);
58}
59
60static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) {
61 enum GBAKey key = GBA_KEY_NONE;
62 if (!event->keysym.mod) {
63 key = GBAInputMapKey(&context->inputMap, SDL_BINDING_KEY, event->keysym.sym);
64 }
65 if (key != GBA_KEY_NONE) {
66 if (event->type == SDL_KEYDOWN) {
67 context->activeKeys |= 1 << key;
68 } else {
69 context->activeKeys &= ~(1 << key);
70 }
71 return;
72 }
73 switch (event->keysym.sym) {
74 case SDLK_F11:
75 if (event->type == SDL_KEYDOWN && context->debugger) {
76 ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL);
77 }
78 return;
79 case SDLK_F12:
80 if (event->type == SDL_KEYDOWN) {
81 GBAThreadInterrupt(context);
82 GBAThreadTakeScreenshot(context);
83 GBAThreadContinue(context);
84 }
85 return;
86 case SDLK_TAB:
87 context->sync.audioWait = event->type != SDL_KEYDOWN;
88 return;
89 case SDLK_BACKSLASH:
90 if (event->type == SDL_KEYDOWN) {
91 GBAThreadPause(context);
92 context->frameCallback = _pauseAfterFrame;
93 GBAThreadUnpause(context);
94 }
95 return;
96 case SDLK_LEFTBRACKET:
97 GBAThreadInterrupt(context);
98 GBARewind(context, 10);
99 GBAThreadContinue(context);
100 return;
101 case SDLK_ESCAPE:
102 GBAThreadInterrupt(context);
103 if (context->gba->rr) {
104 GBARRStopPlaying(context->gba->rr);
105 GBARRStopRecording(context->gba->rr);
106 }
107 GBAThreadContinue(context);
108 return;
109 default:
110 if (event->type == SDL_KEYDOWN) {
111 if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) {
112 switch (event->keysym.sym) {
113#if SDL_VERSION_ATLEAST(2, 0, 0)
114 case SDLK_f:
115 SDL_SetWindowFullscreen(sdlContext->window, sdlContext->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
116 sdlContext->fullscreen = !sdlContext->fullscreen;
117 sdlContext->windowUpdated = 1;
118 break;
119#endif
120 case SDLK_p:
121 GBAThreadTogglePause(context);
122 break;
123 case SDLK_n:
124 GBAThreadPause(context);
125 context->frameCallback = _pauseAfterFrame;
126 GBAThreadUnpause(context);
127 break;
128 case SDLK_r:
129 GBAThreadReset(context);
130 break;
131 case SDLK_t:
132 if (context->stateDir) {
133 GBAThreadInterrupt(context);
134 GBARRContextCreate(context->gba);
135 if (!GBARRIsRecording(context->gba->rr)) {
136 GBARRStopPlaying(context->gba->rr);
137 GBARRInitStream(context->gba->rr, context->stateDir);
138 GBARRReinitStream(context->gba->rr, INIT_EX_NIHILO);
139 GBARRStartRecording(context->gba->rr);
140 GBARRSaveState(context->gba);
141 }
142 GBAThreadContinue(context);
143 }
144 break;
145 case SDLK_y:
146 if (context->stateDir) {
147 GBAThreadInterrupt(context);
148 GBARRContextCreate(context->gba);
149 GBARRStopRecording(context->gba->rr);
150 GBARRInitStream(context->gba->rr, context->stateDir);
151 GBARRStartPlaying(context->gba->rr, false);
152 GBARRLoadState(context->gba);
153 GBAThreadContinue(context);
154 }
155 break;
156 default:
157 break;
158 }
159 }
160 if (event->keysym.mod & KMOD_SHIFT) {
161 switch (event->keysym.sym) {
162 case SDLK_F1:
163 case SDLK_F2:
164 case SDLK_F3:
165 case SDLK_F4:
166 case SDLK_F5:
167 case SDLK_F6:
168 case SDLK_F7:
169 case SDLK_F8:
170 case SDLK_F9:
171 case SDLK_F10:
172 GBAThreadInterrupt(context);
173 GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, true);
174 GBAThreadContinue(context);
175 break;
176 default:
177 break;
178 }
179 } else {
180 switch (event->keysym.sym) {
181 case SDLK_F1:
182 case SDLK_F2:
183 case SDLK_F3:
184 case SDLK_F4:
185 case SDLK_F5:
186 case SDLK_F6:
187 case SDLK_F7:
188 case SDLK_F8:
189 case SDLK_F9:
190 case SDLK_F10:
191 GBAThreadInterrupt(context);
192 GBALoadState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1);
193 GBAThreadContinue(context);
194 break;
195 default:
196 break;
197 }
198 }
199 }
200 return;
201 }
202}
203
204static void _GBASDLHandleJoyButton(struct GBAThread* context, const struct SDL_JoyButtonEvent* event) {
205 enum GBAKey key = 0;
206 key = GBAInputMapKey(&context->inputMap, SDL_BINDING_BUTTON, event->button);
207 if (key == GBA_KEY_NONE) {
208 return;
209 }
210
211 if (event->type == SDL_JOYBUTTONDOWN) {
212 context->activeKeys |= 1 << key;
213 } else {
214 context->activeKeys &= ~(1 << key);
215 }
216}
217
218static void _GBASDLHandleJoyHat(struct GBAThread* context, const struct SDL_JoyHatEvent* event) {
219 enum GBAKey key = 0;
220
221 if (event->value & SDL_HAT_UP) {
222 key |= 1 << GBA_KEY_UP;
223 }
224 if (event->value & SDL_HAT_LEFT) {
225 key |= 1 << GBA_KEY_LEFT;
226 }
227 if (event->value & SDL_HAT_DOWN) {
228 key |= 1 << GBA_KEY_DOWN;
229 }
230 if (event->value & SDL_HAT_RIGHT) {
231 key |= 1 << GBA_KEY_RIGHT;
232 }
233
234 context->activeKeys &= ~((1 << GBA_KEY_UP) | (1 << GBA_KEY_LEFT) | (1 << GBA_KEY_DOWN) | (1 << GBA_KEY_RIGHT));
235 context->activeKeys |= key;
236}
237
238#if SDL_VERSION_ATLEAST(2, 0, 0)
239static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_WindowEvent* event) {
240 UNUSED(context);
241 switch (event->event) {
242 case SDL_WINDOWEVENT_SIZE_CHANGED:
243 sdlContext->windowUpdated = 1;
244 break;
245 }
246}
247#endif
248
249void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event) {
250 switch (event->type) {
251 case SDL_QUIT:
252 GBAThreadEnd(context);
253 break;
254#if SDL_VERSION_ATLEAST(2, 0, 0)
255 case SDL_WINDOWEVENT:
256 _GBASDLHandleWindowEvent(context, sdlContext, &event->window);
257 break;
258#endif
259 case SDL_KEYDOWN:
260 case SDL_KEYUP:
261 _GBASDLHandleKeypress(context, sdlContext, &event->key);
262 break;
263 case SDL_JOYBUTTONDOWN:
264 case SDL_JOYBUTTONUP:
265 _GBASDLHandleJoyButton(context, &event->jbutton);
266 break;
267 case SDL_JOYHATMOTION:
268 _GBASDLHandleJoyHat(context, &event->jhat);
269 }
270}