1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "../../config.h"
19 #endif
20
21 #ifdef HAVE_X11
22 #include <X11/Xlib.h>
23 #endif
24
25 #include "../../configuration.h"
26 #include "../../verbosity.h"
27 #include "../common/gl_common.h"
28
29 #include "SDL.h"
30
31 #ifdef HAVE_SDL2
32 #include "../common/sdl2_common.h"
33 #endif
34
35 typedef struct gfx_ctx_sdl_data
36 {
37 int width;
38 int height;
39 int new_width;
40 int new_height;
41
42 bool full;
43 bool resized;
44
45 #ifdef HAVE_SDL2
46 SDL_Window *win;
47 SDL_GLContext ctx;
48 #else
49 SDL_Surface *win;
50 #endif
51 } gfx_ctx_sdl_data_t;
52
53 /* TODO/FIXME - static global */
54 static enum gfx_ctx_api sdl_api = GFX_CTX_OPENGL_API;
55
sdl_ctx_destroy_resources(gfx_ctx_sdl_data_t * sdl)56 static void sdl_ctx_destroy_resources(gfx_ctx_sdl_data_t *sdl)
57 {
58 if (!sdl)
59 return;
60
61 #ifdef HAVE_SDL2
62 if (sdl->ctx)
63 SDL_GL_DeleteContext(sdl->ctx);
64
65 if (sdl->win)
66 SDL_DestroyWindow(sdl->win);
67
68 sdl->ctx = NULL;
69 #else
70 if (sdl->win)
71 SDL_FreeSurface(sdl->win);
72 #endif
73 sdl->win = NULL;
74 }
75
sdl_ctx_init(void * video_driver)76 static void *sdl_ctx_init(void *video_driver)
77 {
78 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)
79 calloc(1, sizeof(gfx_ctx_sdl_data_t));
80 uint32_t sdl_subsystem_flags = SDL_WasInit(0);
81
82 if (!sdl)
83 return NULL;
84
85 #ifdef HAVE_X11
86 XInitThreads();
87 #endif
88
89 /* Initialise graphics subsystem, if required */
90 if (sdl_subsystem_flags == 0)
91 {
92 if (SDL_Init(SDL_INIT_VIDEO) < 0)
93 goto error;
94 }
95 else if ((sdl_subsystem_flags & SDL_INIT_VIDEO) == 0)
96 {
97 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
98 goto error;
99 }
100
101 RARCH_LOG("[SDL_GL] SDL %i.%i.%i gfx context driver initialized.\n",
102 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
103
104 return sdl;
105
106 error:
107 RARCH_WARN("[SDL_GL]: Failed to initialize SDL gfx context driver: %s\n",
108 SDL_GetError());
109
110 sdl_ctx_destroy_resources(sdl);
111
112 if (sdl)
113 free(sdl);
114
115 return NULL;
116 }
117
sdl_ctx_destroy(void * data)118 static void sdl_ctx_destroy(void *data)
119 {
120 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)data;
121
122 if (!sdl)
123 return;
124
125 sdl_ctx_destroy_resources(sdl);
126 free(sdl);
127 }
128
sdl_ctx_get_api(void * data)129 static enum gfx_ctx_api sdl_ctx_get_api(void *data) { return sdl_api; }
130
sdl_ctx_bind_api(void * data,enum gfx_ctx_api api,unsigned major,unsigned minor)131 static bool sdl_ctx_bind_api(void *data,
132 enum gfx_ctx_api api, unsigned major,
133 unsigned minor)
134 {
135 #ifdef HAVE_SDL2
136 unsigned profile;
137
138 if (api != GFX_CTX_OPENGL_API && api != GFX_CTX_OPENGL_ES_API)
139 return false;
140
141 profile = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
142
143 if (api == GFX_CTX_OPENGL_ES_API)
144 profile = SDL_GL_CONTEXT_PROFILE_ES;
145
146 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile);
147
148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
149 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
150 #endif
151
152 sdl_api = api;
153
154 #ifndef HAVE_SDL2
155 if (api != GFX_CTX_OPENGL_API)
156 return false;
157 #endif
158 return true;
159 }
160
sdl_ctx_swap_interval(void * data,int interval)161 static void sdl_ctx_swap_interval(void *data, int interval)
162 {
163 #ifdef HAVE_SDL2
164 SDL_GL_SetSwapInterval(interval);
165 #else
166 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, interval);
167 #endif
168 }
169
sdl_ctx_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)170 static bool sdl_ctx_set_video_mode(void *data,
171 unsigned width, unsigned height,
172 bool fullscreen)
173 {
174 unsigned fsflag = 0;
175 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)data;
176 settings_t *settings = config_get_ptr();
177 bool windowed_fullscreen = settings->bools.video_windowed_fullscreen;
178 unsigned video_monitor_index = settings->uints.video_monitor_index;
179
180 sdl->new_width = width;
181 sdl->new_height = height;
182
183 #ifdef HAVE_SDL2
184
185 if (fullscreen)
186 {
187 if (windowed_fullscreen)
188 fsflag = SDL_WINDOW_FULLSCREEN_DESKTOP;
189 else
190 fsflag = SDL_WINDOW_FULLSCREEN;
191 }
192
193 if (sdl->win)
194 {
195 SDL_SetWindowSize(sdl->win, width, height);
196
197 if (fullscreen)
198 SDL_SetWindowFullscreen(sdl->win, fsflag);
199 }
200 else
201 {
202 unsigned display = video_monitor_index;
203
204 sdl->win = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED_DISPLAY(display),
205 SDL_WINDOWPOS_UNDEFINED_DISPLAY(display),
206 width, height, SDL_WINDOW_OPENGL | fsflag);
207 }
208 #else
209 if (fullscreen)
210 fsflag = SDL_FULLSCREEN;
211
212 sdl->win = SDL_SetVideoMode(width, height, 0, SDL_OPENGL | fsflag);
213 #endif
214
215 if (!sdl->win)
216 goto error;
217
218 #ifdef HAVE_SDL2
219 #if defined(_WIN32)
220 sdl2_set_handles(sdl->win, RARCH_DISPLAY_WIN32);
221 #elif defined(HAVE_X11)
222 sdl2_set_handles(sdl->win, RARCH_DISPLAY_X11);
223 #elif defined(HAVE_COCOA)
224 sdl2_set_handles(sdl->win, RARCH_DISPLAY_OSX);
225 #endif
226
227 if (sdl->ctx)
228 video_driver_set_video_cache_context_ack();
229 else
230 {
231 sdl->ctx = SDL_GL_CreateContext(sdl->win);
232
233 if (!sdl->ctx)
234 goto error;
235 }
236 #endif
237
238 sdl->full = fullscreen;
239 sdl->width = width;
240 sdl->height = height;
241
242 return true;
243
244 error:
245 RARCH_WARN("[SDL_GL]: Failed to set video mode: %s\n", SDL_GetError());
246 return false;
247 }
248
sdl_ctx_get_video_size(void * data,unsigned * width,unsigned * height)249 static void sdl_ctx_get_video_size(void *data,
250 unsigned *width, unsigned *height)
251 {
252 settings_t *settings = config_get_ptr();
253 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)data;
254
255 if (!sdl)
256 return;
257
258 *width = sdl->width;
259 *height = sdl->height;
260
261 if (!sdl->win)
262 {
263 #ifdef HAVE_SDL2
264 SDL_DisplayMode mode = {0};
265 int i = settings->uints.video_monitor_index;
266
267 if (SDL_GetCurrentDisplayMode(i, &mode) < 0)
268 RARCH_WARN("[SDL_GL]: Failed to get display #%i mode: %s\n", i,
269 SDL_GetError());
270 #else
271 SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
272 SDL_Rect mode = {0};
273
274 if (!modes)
275 RARCH_WARN("[SDL_GL]: Failed to detect available video modes: %s\n",
276 SDL_GetError());
277 else if (*modes)
278 mode = **modes;
279 #endif
280
281 *width = mode.w;
282 *height = mode.h;
283 }
284 }
285
sdl_ctx_update_title(void * data)286 static void sdl_ctx_update_title(void *data)
287 {
288 char title[128];
289 title[0] = '\0';
290
291 video_driver_get_window_title(title, sizeof(title));
292
293 if (title[0])
294 {
295 #ifdef HAVE_SDL2
296 SDL_SetWindowTitle((SDL_Window*)
297 video_driver_display_userdata_get(), title);
298 #else
299 SDL_WM_SetCaption(title, NULL);
300 #endif
301 }
302 }
303
sdl_ctx_check_window(void * data,bool * quit,bool * resize,unsigned * width,unsigned * height)304 static void sdl_ctx_check_window(void *data, bool *quit,
305 bool *resize,unsigned *width,
306 unsigned *height)
307 {
308 SDL_Event event;
309 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)data;
310
311 SDL_PumpEvents();
312
313 #ifdef HAVE_SDL2
314 while (SDL_PeepEvents(&event, 1,
315 SDL_GETEVENT, SDL_QUIT, SDL_WINDOWEVENT) > 0)
316 #else
317 while (SDL_PeepEvents(&event, 1,
318 SDL_GETEVENT, SDL_QUITMASK|SDL_VIDEORESIZEMASK) > 0)
319 #endif
320 {
321 switch (event.type)
322 {
323 case SDL_QUIT:
324 #ifdef HAVE_SDL2
325 case SDL_APP_TERMINATING:
326 #endif
327 *quit = true;
328 break;
329 #ifdef HAVE_SDL2
330 case SDL_WINDOWEVENT:
331 if (event.window.event == SDL_WINDOWEVENT_RESIZED)
332 {
333 sdl->resized = true;
334 sdl->new_width = event.window.data1;
335 sdl->new_height = event.window.data2;
336 }
337 #else
338 case SDL_VIDEORESIZE:
339 sdl->resized = true;
340 sdl->new_width = event.resize.w;
341 sdl->new_height = event.resize.h;
342 #endif
343 break;
344 default:
345 break;
346 }
347 }
348
349 if (sdl->resized)
350 {
351 *width = sdl->new_width;
352 *height = sdl->new_height;
353 *resize = true;
354 sdl->resized = false;
355 }
356 }
357
sdl_ctx_has_focus(void * data)358 static bool sdl_ctx_has_focus(void *data)
359 {
360 unsigned flags;
361
362 #ifdef HAVE_SDL2
363 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)data;
364 flags = (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS);
365 return (SDL_GetWindowFlags(sdl->win) & flags) == flags;
366 #else
367 flags = (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
368 return (SDL_GetAppState() & flags) == flags;
369 #endif
370 }
371
sdl_ctx_swap_buffers(void * data)372 static void sdl_ctx_swap_buffers(void *data)
373 {
374 #ifdef HAVE_SDL2
375 gfx_ctx_sdl_data_t *sdl = (gfx_ctx_sdl_data_t*)data;
376 if (sdl)
377 SDL_GL_SwapWindow(sdl->win);
378 #else
379 SDL_GL_SwapBuffers();
380 #endif
381 }
382
sdl_ctx_input_driver(void * data,const char * name,input_driver_t ** input,void ** input_data)383 static void sdl_ctx_input_driver(void *data,
384 const char *name,
385 input_driver_t **input, void **input_data)
386 {
387 *input = NULL;
388 *input_data = NULL;
389 }
390
sdl_ctx_get_proc_address(const char * name)391 static gfx_ctx_proc_t sdl_ctx_get_proc_address(const char *name)
392 {
393 return (gfx_ctx_proc_t)SDL_GL_GetProcAddress(name);
394 }
395
sdl_ctx_show_mouse(void * data,bool state)396 static void sdl_ctx_show_mouse(void *data, bool state) { SDL_ShowCursor(state); }
397
sdl_ctx_get_flags(void * data)398 static uint32_t sdl_ctx_get_flags(void *data)
399 {
400 uint32_t flags = 0;
401
402 BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
403
404 return flags;
405 }
406
sdl_ctx_suppress_screensaver(void * data,bool enable)407 static bool sdl_ctx_suppress_screensaver(void *data, bool enable) { return false; }
sdl_ctx_set_flags(void * data,uint32_t flags)408 static void sdl_ctx_set_flags(void *data, uint32_t flags) { }
409
410 const gfx_ctx_driver_t gfx_ctx_sdl_gl =
411 {
412 sdl_ctx_init,
413 sdl_ctx_destroy,
414 sdl_ctx_get_api,
415 sdl_ctx_bind_api,
416 sdl_ctx_swap_interval,
417 sdl_ctx_set_video_mode,
418 sdl_ctx_get_video_size,
419 NULL, /* get_refresh_rate */
420 NULL, /* get_video_output_size */
421 NULL, /* get_video_output_prev */
422 NULL, /* get_video_output_next */
423 NULL, /* get_metrics */
424 NULL, /* translate_aspect */
425 sdl_ctx_update_title,
426 sdl_ctx_check_window,
427 NULL, /* set_resize */
428 sdl_ctx_has_focus,
429 sdl_ctx_suppress_screensaver,
430 true, /* has_windowed */
431 sdl_ctx_swap_buffers,
432 sdl_ctx_input_driver,
433 sdl_ctx_get_proc_address,
434 NULL,
435 NULL,
436 sdl_ctx_show_mouse,
437 "gl_sdl",
438 sdl_ctx_get_flags,
439 sdl_ctx_set_flags,
440 NULL, /* bind_hw_render */
441 NULL,
442 NULL
443 };
444