Render game graphics using OpenGL
[mercenary-reloaded.git] / src / libsdl / sdl.c
1 /* SDL handling
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <errno.h>
23 #include "print.h"
24 #include "sdl.h"
25 #include "opengl.h"
26
27 #define GL3_PROTOTYPES 1
28 #include <GL/glew.h>
29
30 static int sdl_initialized = 0;
31 static int audio_initialized = 0;
32 static SDL_Window *gl_window = NULL;
33 static SDL_GLContext gl_context = NULL;
34 static void (*keyboard_sdl)(int down, SDL_Keycode sym) = NULL;
35 static void (*audio_sdl)(float *data, int len) = NULL;
36
37 static void audio_cb(void __attribute__((unused)) *userdata, Uint8 *stream, int len)
38 {
39         float audio_data[len / sizeof(float)];
40
41         SDL_memset(stream, 0, len);
42         audio_sdl(audio_data, len / sizeof(float) / 2);
43         SDL_MixAudio(stream, (Uint8 *)audio_data, len, SDL_MIX_MAXVOLUME);
44 }
45
46 int init_sdl(const char *progname, int width, int height, int sound_samplerate, int sound_chunk, void (*keyboard)(int down, SDL_Keycode sym), void (*audio)(float *data, int len), int multisampling)
47 {
48         int rc;
49
50         keyboard_sdl = keyboard;
51         audio_sdl = audio;
52
53         /* init SDL library */
54         rc = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
55         if (rc < 0) {
56                 print_error("Failed to init SDL\n");
57                 goto error;
58         }
59         sdl_initialized = 1;
60
61         if (multisampling) {
62                 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
63                 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
64                 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
65                 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
66                 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
67                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
68                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisampling);
69         }
70
71         /* open window */
72         gl_window = SDL_CreateWindow(progname, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
73         if (!gl_window) {
74                 print_error("Failed to open SDL window: %s\n", SDL_GetError());
75                 rc = EIO;
76                 goto error;
77         }
78
79         /* create GL context */
80         gl_context = SDL_GL_CreateContext(gl_window);
81         if (!gl_context) {
82                 print_error("Failed to create SDL's OpenGL context: %s\n", SDL_GetError());
83                 rc = EIO;
84                 goto error;
85         }
86
87         rc = SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
88         if (rc < 0) {
89                 print_error("Failed to set SDL's OpenGL context profile\n");
90                 goto error;
91         }
92
93         rc = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
94         if (rc < 0) {
95                 print_error("Failed to set SDL's OpenGL major version\n");
96                 goto error;
97         }
98         rc = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
99         if (rc < 0) {
100                 print_error("Failed to set SDL's OpenGL minor version\n");
101                 goto error;
102         }
103
104         rc = SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
105         if (rc < 0) {
106                 print_error("Failed to set SDL's OpenGL doublebuffer\n");
107                 goto error;
108         }
109
110         rc = SDL_GL_SetSwapInterval(1);
111         if (rc < 0) {
112                 print_error("Failed to set SDL's OpenGL swap interval to VBLANK\n");
113                 goto error;
114         }
115
116 #ifndef __APPLE__
117         glewExperimental = GL_TRUE;
118         if (glewInit() != GLEW_OK) {
119                 print_error("Failed to init GLEW\n");
120                 goto error;
121         }
122 #endif
123
124         /* just in case */
125         glDisable(GL_DEPTH_TEST);
126         glDisable(GL_CULL_FACE);
127         if (multisampling)
128                 glEnable(GL_MULTISAMPLE);
129
130         /* open audio */
131         SDL_AudioSpec want, have;
132         SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
133         want.freq = sound_samplerate;
134         want.format = AUDIO_F32; /* we always use float in this project */
135         want.channels = 2;
136         want.samples = sound_chunk; /* must be a power of two */
137         want.callback = audio_cb;
138         rc = SDL_OpenAudio(&want, &have);
139         if (rc < 0) {
140                 print_error("Failed to open audio\n");
141                 goto error;
142         } else if (have.format != want.format) {
143                 print_error("Failed to open audio with desired audio format\n");
144                 SDL_CloseAudio();
145                 rc = -EIO;
146                 goto error;
147         } else if (have.freq != want.freq) {
148                 print_error("Failed to open audio with desired sample rate\n");
149                 SDL_CloseAudio();
150                 rc = -EIO;
151                 goto error;
152         } else if (have.channels != want.channels) {
153                 print_error("Failed to open audio with desired channels (got %d)\n", have.channels);
154                 SDL_CloseAudio();
155                 rc = -EIO;
156                 goto error;
157         } else {
158                 SDL_PauseAudio(0);
159                 audio_initialized = 1;
160         }
161
162         return 0;
163
164 error:
165         exit_sdl();
166         return rc;
167 }
168
169 static int key_ctrl = 0, fullscreen = 0;
170
171 int event_sdl(void)
172 {
173         int quit = 0;
174         SDL_Event event;
175
176         while (SDL_PollEvent(&event)) {
177                 if (event.type == SDL_QUIT)
178                         quit = 1;
179                 if (event.type == SDL_WINDOWEVENT) {
180                         if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
181                                 resize_opengl(event.window.data1, event.window.data2);
182                 }
183                 if (event.type == SDL_KEYDOWN) {
184                         switch (event.key.keysym.sym) {
185                         case SDLK_f:
186                                 if (key_ctrl) {
187                                         if (fullscreen) {
188                                                 fullscreen = 0;
189                                                 SDL_SetWindowFullscreen(gl_window, 0);
190                                                 SDL_ShowCursor(SDL_ENABLE);
191                                         } else {
192                                                 SDL_SetWindowFullscreen(gl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
193                                                 SDL_ShowCursor(SDL_DISABLE);
194                                                 fullscreen = 1;
195                                         }
196                                 }
197                                 break;
198                         case SDLK_LCTRL:
199                         case SDLK_RCTRL:
200                                 key_ctrl = 1;
201                                 break;
202                         }
203                         keyboard_sdl(1, event.key.keysym.sym);
204                 }
205                 if (event.type == SDL_KEYUP) {
206                         switch (event.key.keysym.sym) {
207                         case SDLK_LCTRL:
208                         case SDLK_RCTRL:
209                                 key_ctrl = 0;
210                                 break;
211                         }
212                         keyboard_sdl(0, event.key.keysym.sym);
213                 }
214         }
215
216         return quit;
217 }
218
219 void swap_sdl(void)
220 {
221         SDL_GL_SwapWindow(gl_window);
222 }
223
224 void exit_sdl(void)
225 {
226         /* close window */
227         if (gl_window) {
228                 SDL_DestroyWindow(gl_window);
229                 gl_window = NULL;
230         }
231
232         /* clear OpenGL context */
233         if (gl_context) {
234                 SDL_GL_DeleteContext(gl_context);
235                 gl_context = NULL;
236         }
237
238         /* exit SDL library */
239         if (audio_initialized) {
240                 SDL_PauseAudio(1);
241                 SDL_CloseAudio();
242                 audio_initialized = 0;
243         }
244         if (sdl_initialized) {
245                 SDL_Quit();
246                 sdl_initialized = 0;
247         }
248 }
249