d310791fe4cbe7fa1122316a5e1de333853e7f1b
[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                 goto error;
129         } else if (have.format != want.format) {
130                 fprintf(stderr, "Failed to open audio with desired audio format\n");
131                 SDL_CloseAudio();
132                 rc = -EIO;
133                 goto error;
134         } else if (have.freq != want.freq) {
135                 fprintf(stderr, "Failed to open audio with desired sample rate\n");
136                 SDL_CloseAudio();
137                 rc = -EIO;
138                 goto error;
139         } else {
140                 SDL_PauseAudio(0);
141                 audio_initialized = 1;
142         }
143
144         return 0;
145
146 error:
147         exit_sdl();
148         return rc;
149 }
150
151 static int key_ctrl = 0, fullscreen = 0;
152
153 int event_sdl(void)
154 {
155         int quit = 0;
156         SDL_Event event;
157
158         while (SDL_PollEvent(&event)) {
159                 if (event.type == SDL_QUIT)
160                         quit = 1;
161                 if (event.type == SDL_WINDOWEVENT) {
162                         if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
163                                 resize_opengl(event.window.data1, event.window.data2);
164                 }
165                 if (event.type == SDL_KEYDOWN) {
166                         switch (event.key.keysym.sym) {
167                         case SDLK_f:
168                                 if (key_ctrl) {
169                                         if (fullscreen) {
170                                                 fullscreen = 0;
171                                                 SDL_SetWindowFullscreen(gl_window, 0);
172                                         } else {
173                                                 SDL_SetWindowFullscreen(gl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
174                                                 fullscreen = 1;
175                                         }
176                                 }
177                                 break;
178                         case SDLK_LCTRL:
179                         case SDLK_RCTRL:
180                                 key_ctrl = 1;
181                                 break;
182                         }
183                         keyboard_sdl(1, event.key.keysym.sym);
184                 }
185                 if (event.type == SDL_KEYUP) {
186                         switch (event.key.keysym.sym) {
187                         case SDLK_LCTRL:
188                         case SDLK_RCTRL:
189                                 key_ctrl = 0;
190                                 break;
191                         }
192                         keyboard_sdl(0, event.key.keysym.sym);
193                 }
194         }
195
196         return quit;
197 }
198
199 void swap_sdl(void)
200 {
201         SDL_GL_SwapWindow(gl_window);
202 }
203
204 void exit_sdl(void)
205 {
206         /* close window */
207         if (gl_window) {
208                 SDL_DestroyWindow(gl_window);
209                 gl_window = NULL;
210         }
211
212         /* clear OpenGL context */
213         if (gl_context) {
214                 SDL_GL_DeleteContext(gl_context);
215                 gl_context = NULL;
216         }
217
218         /* exit SDL library */
219         if (audio_initialized) {
220                 SDL_PauseAudio(1);
221                 SDL_CloseAudio();
222                 audio_initialized = 0;
223         }
224         if (sdl_initialized) {
225                 SDL_Quit();
226                 sdl_initialized = 0;
227         }
228 }
229