src/sdl/main.c (view raw)
1#include "debugger.h"
2#include "gba-thread.h"
3#include "gba.h"
4#include "renderers/video-software.h"
5
6#include <SDL.h>
7#ifdef __APPLE__
8#include <OpenGL/gl.h>
9#else
10#include <GL/gl.h>
11#endif
12
13#include <fcntl.h>
14#include <errno.h>
15#include <signal.h>
16#include <sys/time.h>
17#include <unistd.h>
18
19struct GLSoftwareRenderer {
20 struct GBAVideoSoftwareRenderer d;
21
22 GLuint tex;
23};
24
25static int _GBASDLInit(struct GLSoftwareRenderer* renderer);
26static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer);
27static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer);
28static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event);
29
30static const GLint _glVertices[] = {
31 0, 0,
32 256, 0,
33 256, 256,
34 0, 256
35};
36
37static const GLint _glTexCoords[] = {
38 0, 0,
39 1, 0,
40 1, 1,
41 0, 1
42};
43
44int main(int argc, char** argv) {
45 const char* fname = "test.rom";
46 if (argc > 1) {
47 fname = argv[1];
48 }
49 int fd = open(fname, O_RDONLY);
50 if (fd < 0) {
51 return 1;
52 }
53
54 sigset_t signals;
55 sigaddset(&signals, SIGINT);
56 sigaddset(&signals, SIGTRAP);
57 pthread_sigmask(SIG_BLOCK, &signals, 0);
58
59 struct GBAThread context;
60 struct GLSoftwareRenderer renderer;
61 GBAVideoSoftwareRendererCreate(&renderer.d);
62
63 if (!_GBASDLInit(&renderer)) {
64 return 1;
65 }
66
67 context.fd = fd;
68 context.renderer = &renderer.d.d;
69 GBAThreadStart(&context);
70
71 _GBASDLRunloop(&context, &renderer);
72
73 GBAThreadJoin(&context);
74 close(fd);
75
76 _GBASDLDeinit(&renderer);
77
78 return 0;
79}
80
81static int _GBASDLInit(struct GLSoftwareRenderer* renderer) {
82 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
83 return 0;
84 }
85
86 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
87 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
88 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
89 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
90 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
91 SDL_SetVideoMode(240, 160, 32, SDL_OPENGL);
92
93 renderer->d.outputBuffer = malloc(256 * 256 * 4);
94 renderer->d.outputBufferStride = 256;
95 glGenTextures(1, &renderer->tex);
96 glBindTexture(GL_TEXTURE_2D, renderer->tex);
97 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
98 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
99 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
100 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
101 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
102
103 glViewport(0, 0, 240, 160);
104
105 return 1;
106}
107
108static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer) {
109 SDL_Event event;
110
111 int err;
112 glEnable(GL_TEXTURE_2D);
113 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
114 glEnableClientState(GL_VERTEX_ARRAY);
115 glVertexPointer(2, GL_INT, 0, _glVertices);
116 glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
117 glMatrixMode (GL_PROJECTION);
118 glLoadIdentity();
119 glOrtho(0, 240, 160, 0, 0, 1);
120 while (context->started) {
121 pthread_mutex_lock(&renderer->d.mutex);
122 if (renderer->d.d.framesPending) {
123 renderer->d.d.framesPending = 0;
124 pthread_mutex_unlock(&renderer->d.mutex);
125 glBindTexture(GL_TEXTURE_2D, renderer->tex);
126 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer);
127 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
128
129 SDL_GL_SwapBuffers();
130
131 while (SDL_PollEvent(&event)) {
132 switch (event.type) {
133 case SDL_QUIT:
134 // FIXME: this isn't thread-safe
135 context->debugger->state = DEBUGGER_EXITING;
136 break;
137 case SDL_KEYDOWN:
138 case SDL_KEYUP:
139 _GBASDLHandleKeypress(context, &event.key);
140 break;
141 }
142 }
143 pthread_mutex_lock(&renderer->d.mutex);
144 pthread_cond_broadcast(&renderer->d.downCond);
145 pthread_mutex_unlock(&renderer->d.mutex);
146 } else {
147 while (!renderer->d.d.framesPending) {
148 struct timeval tv;
149 struct timespec ts;
150 gettimeofday(&tv, 0);
151 ts.tv_sec = tv.tv_sec;
152 ts.tv_nsec = tv.tv_usec * 1000 + 800000;
153 err = pthread_cond_timedwait(&renderer->d.upCond, &renderer->d.mutex, &ts);
154 if (err == ETIMEDOUT) {
155 break;
156 }
157 }
158 pthread_mutex_unlock(&renderer->d.mutex);
159 }
160 }
161}
162
163static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) {
164 free(renderer->d.outputBuffer);
165
166 SDL_Quit();
167}
168
169static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event) {
170 enum GBAKey key = 0;
171 switch (event->keysym.sym) {
172 case SDLK_z:
173 key = GBA_KEY_A;
174 break;
175 case SDLK_x:
176 key = GBA_KEY_B;
177 break;
178 case SDLK_a:
179 key = GBA_KEY_L;
180 break;
181 case SDLK_s:
182 key = GBA_KEY_R;
183 break;
184 case SDLK_RETURN:
185 key = GBA_KEY_START;
186 break;
187 case SDLK_BACKSPACE:
188 key = GBA_KEY_SELECT;
189 break;
190 case SDLK_UP:
191 key = GBA_KEY_UP;
192 break;
193 case SDLK_DOWN:
194 key = GBA_KEY_DOWN;
195 break;
196 case SDLK_LEFT:
197 key = GBA_KEY_LEFT;
198 break;
199 case SDLK_RIGHT:
200 key = GBA_KEY_RIGHT;
201 break;
202 case SDLK_TAB:
203 context->renderer->turbo = !context->renderer->turbo;
204 return;
205 default:
206 return;
207 }
208
209 if (event->type == SDL_KEYDOWN) {
210 context->activeKeys |= 1 << key;
211 } else {
212 context->activeKeys &= ~(1 << key);
213 }
214}