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