Ignore OpenGL VBLANK setting, if fail
[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 #include <SDL2/SDL.h>
28 #define GL3_PROTOTYPES 1
29 #include <GL/glew.h>
30
31 static int sdl_initialized = 0;
32 static int audio_initialized = 0;
33 static SDL_Window *gl_window = NULL;
34 static SDL_GLContext gl_context = NULL;
35 static void (*keyboard_sdl)(int down, SDL_Keycode sym) = NULL;
36 static void (*audio_sdl)(float *data, int len) = NULL;
37
38 static void audio_cb(void __attribute__((unused)) *userdata, Uint8 *stream, int len)
39 {
40         float audio_data[len / sizeof(float)];
41
42         SDL_memset(stream, 0, len);
43         audio_sdl(audio_data, len / sizeof(float) / 2);
44         SDL_MixAudio(stream, (Uint8 *)audio_data, len, SDL_MIX_MAXVOLUME);
45 }
46
47 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)
48 {
49         int rc;
50
51         keyboard_sdl = keyboard;
52         audio_sdl = audio;
53
54         /* init SDL library */
55         rc = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
56         if (rc < 0) {
57                 print_error("Failed to init SDL\n");
58                 goto error;
59         }
60         sdl_initialized = 1;
61
62         if (multisampling) {
63                 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
64                 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
65                 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
66                 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
67                 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
68                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
69                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisampling);
70         }
71
72         /* open window */
73         gl_window = SDL_CreateWindow(progname, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
74         if (!gl_window) {
75                 print_error("Failed to open SDL window: %s\n", SDL_GetError());
76                 rc = EIO;
77                 goto error;
78         }
79
80         /* create GL context */
81         gl_context = SDL_GL_CreateContext(gl_window);
82         if (!gl_context) {
83                 print_error("Failed to create SDL's OpenGL context: %s\n", SDL_GetError());
84                 rc = EIO;
85                 goto error;
86         }
87
88         rc = SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
89         if (rc < 0) {
90                 print_error("Failed to set SDL's OpenGL context profile\n");
91                 goto error;
92         }
93
94         rc = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
95         if (rc < 0) {
96                 print_error("Failed to set SDL's OpenGL major version\n");
97                 goto error;
98         }
99         rc = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
100         if (rc < 0) {
101                 print_error("Failed to set SDL's OpenGL minor version\n");
102                 goto error;
103         }
104
105         rc = SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
106         if (rc < 0) {
107                 print_error("Failed to set SDL's OpenGL doublebuffer\n");
108                 goto error;
109         }
110
111         rc = SDL_GL_SetSwapInterval(1);
112         if (rc < 0) {
113                 print_error("Failed to set SDL's OpenGL swap interval to VBLANK\n");
114                 // continue anyway
115         }
116
117 #ifndef __APPLE__
118         glewExperimental = GL_TRUE;
119         if (glewInit() != GLEW_OK) {
120                 print_error("Failed to init GLEW\n");
121                 goto error;
122         }
123 #endif
124
125         /* just in case */
126         glDisable(GL_DEPTH_TEST);
127         glDisable(GL_CULL_FACE);
128         if (multisampling)
129                 glEnable(GL_MULTISAMPLE);
130
131         /* open audio */
132         SDL_AudioSpec want, have;
133         SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
134         want.freq = sound_samplerate;
135         want.format = AUDIO_F32; /* we always use float in this project */
136         want.channels = 2;
137         want.samples = sound_chunk; /* must be a power of two */
138         want.callback = audio_cb;
139         rc = SDL_OpenAudio(&want, &have);
140         if (rc < 0) {
141                 print_error("Failed to open audio! (No speaker connected?)\n");
142                 goto error;
143         } else if (have.format != want.format) {
144                 print_error("Failed to open audio with desired audio format\n");
145                 SDL_CloseAudio();
146                 rc = -EIO;
147                 goto error;
148         } else if (have.freq != want.freq) {
149                 print_error("Failed to open audio with desired sample rate\n");
150                 SDL_CloseAudio();
151                 rc = -EIO;
152                 goto error;
153         } else if (have.channels != want.channels) {
154                 print_error("Failed to open audio with desired channels (got %d)\n", have.channels);
155                 SDL_CloseAudio();
156                 rc = -EIO;
157                 goto error;
158         } else {
159                 SDL_PauseAudio(0);
160                 audio_initialized = 1;
161         }
162
163         return 0;
164
165 error:
166         exit_sdl();
167         return rc;
168 }
169
170 static int key_ctrl = 0, fullscreen = 0;
171
172 int event_sdl(void)
173 {
174         int quit = 0;
175         SDL_Event event;
176
177         while (SDL_PollEvent(&event)) {
178                 if (event.type == SDL_QUIT)
179                         quit = 1;
180                 if (event.type == SDL_WINDOWEVENT) {
181                         if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
182                                 resize_opengl(event.window.data1, event.window.data2);
183                 }
184                 if (event.type == SDL_KEYDOWN) {
185                         switch (event.key.keysym.sym) {
186                         case SDLK_f:
187                                 if (key_ctrl) {
188                                         if (fullscreen) {
189                                                 fullscreen = 0;
190                                                 SDL_SetWindowFullscreen(gl_window, 0);
191                                                 SDL_ShowCursor(SDL_ENABLE);
192                                         } else {
193                                                 SDL_SetWindowFullscreen(gl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
194                                                 SDL_ShowCursor(SDL_DISABLE);
195                                                 fullscreen = 1;
196                                         }
197                                 }
198                                 break;
199                         case SDLK_LCTRL:
200                         case SDLK_RCTRL:
201                                 key_ctrl = 1;
202                                 break;
203                         }
204                         keyboard_sdl(1, event.key.keysym.sym);
205                 }
206                 if (event.type == SDL_KEYUP) {
207                         switch (event.key.keysym.sym) {
208                         case SDLK_LCTRL:
209                         case SDLK_RCTRL:
210                                 key_ctrl = 0;
211                                 break;
212                         }
213                         keyboard_sdl(0, event.key.keysym.sym);
214                 }
215         }
216
217         return quit;
218 }
219
220 void swap_sdl(void)
221 {
222         SDL_GL_SwapWindow(gl_window);
223 }
224
225 void exit_sdl(void)
226 {
227         /* close window */
228         if (gl_window) {
229                 SDL_DestroyWindow(gl_window);
230                 gl_window = NULL;
231         }
232
233         /* clear OpenGL context */
234         if (gl_context) {
235                 SDL_GL_DeleteContext(gl_context);
236                 gl_context = NULL;
237         }
238
239         /* exit SDL library */
240         if (audio_initialized) {
241                 SDL_PauseAudio(1);
242                 SDL_CloseAudio();
243                 audio_initialized = 0;
244         }
245         if (sdl_initialized) {
246                 SDL_Quit();
247                 sdl_initialized = 0;
248         }
249 }
250
251 uint32_t ticks_sdl(void)
252 {
253         return SDL_GetTicks();
254 }