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