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 #include <stdint.h>
17 
18 #include <sys/system_properties.h>
19 
20 #include <formats/image.h>
21 #include <string/stdstring.h>
22 #include <compat/strl.h>
23 
24 #ifdef HAVE_CONFIG_H
25 #include "../../config.h"
26 #endif
27 
28 #ifdef HAVE_EGL
29 #include "../common/egl_common.h"
30 #endif
31 
32 #ifdef HAVE_OPENGLES
33 #include "../common/gl_common.h"
34 #endif
35 
36 #include "../../frontend/frontend_driver.h"
37 #include "../../frontend/drivers/platform_unix.h"
38 #include "../../verbosity.h"
39 #include "../../configuration.h"
40 
41 #ifdef HAVE_OPENGLES
42 #ifndef EGL_OPENGL_ES3_BIT_KHR
43 #define EGL_OPENGL_ES3_BIT_KHR                  0x0040
44 #endif
45 #endif
46 
47 typedef struct
48 {
49 #ifdef HAVE_EGL
50    egl_ctx_data_t egl;
51 #endif
52 } android_ctx_data_t;
53 
54 /* TODO/FIXME - static globals */
55 static enum gfx_ctx_api android_api           = GFX_CTX_NONE;
56 #ifdef HAVE_OPENGLES
57 static bool g_es3                             = false;
58 #endif
59 
android_gfx_ctx_destroy(void * data)60 static void android_gfx_ctx_destroy(void *data)
61 {
62    android_ctx_data_t *and         = (android_ctx_data_t*)data;
63 
64    if (!and)
65       return;
66 
67 #ifdef HAVE_EGL
68    egl_destroy(&and->egl);
69 #endif
70 
71    free(data);
72 }
73 
android_gfx_ctx_init(void * video_driver)74 static void *android_gfx_ctx_init(void *video_driver)
75 {
76 #ifdef HAVE_OPENGLES
77    EGLint n, major, minor;
78    EGLint format;
79 #if 0
80    struct retro_hw_render_callback *hwr = video_driver_get_hw_context();
81    bool debug = hwr->debug_context;
82 #endif
83    EGLint attribs[] = {
84       EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
85       EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
86       EGL_BLUE_SIZE, 8,
87       EGL_GREEN_SIZE, 8,
88       EGL_RED_SIZE, 8,
89       EGL_ALPHA_SIZE, 8,
90       EGL_DEPTH_SIZE, 16,
91       EGL_NONE
92    };
93 #endif
94    struct android_app *android_app = (struct android_app*)g_android;
95    android_ctx_data_t        *and  = (android_ctx_data_t*)
96       calloc(1, sizeof(*and));
97 
98    if (!android_app || !and)
99       return false;
100 
101 #ifdef HAVE_OPENGLES
102    if (g_es3)
103       attribs[1] = EGL_OPENGL_ES3_BIT_KHR;
104 #endif
105 
106 #ifdef HAVE_EGL
107    RARCH_LOG("Android EGL: GLES version = %d.\n", g_es3 ? 3 : 2);
108 
109    if (!egl_init_context(&and->egl, EGL_NONE, EGL_DEFAULT_DISPLAY,
110             &major, &minor, &n, attribs, NULL))
111    {
112       egl_report_error();
113       goto error;
114    }
115 
116    if (!egl_get_native_visual_id(&and->egl, &format))
117       goto error;
118 #endif
119 
120    slock_lock(android_app->mutex);
121    if (!android_app->window)
122    {
123       slock_unlock(android_app->mutex);
124       android_gfx_ctx_destroy(and);
125       return NULL;
126    }
127 
128    ANativeWindow_setBuffersGeometry(android_app->window, 0, 0, format);
129 
130    slock_unlock(android_app->mutex);
131    return and;
132 
133 error:
134    android_gfx_ctx_destroy(and);
135 
136    return NULL;
137 }
138 
android_gfx_ctx_get_video_size(void * data,unsigned * width,unsigned * height)139 static void android_gfx_ctx_get_video_size(void *data,
140       unsigned *width, unsigned *height)
141 {
142    android_ctx_data_t *and  = (android_ctx_data_t*)data;
143 
144 #ifdef HAVE_EGL
145    egl_get_video_size(&and->egl, width, height);
146 #endif
147 }
148 
android_gfx_ctx_check_window(void * data,bool * quit,bool * resize,unsigned * width,unsigned * height)149 static void android_gfx_ctx_check_window(void *data, bool *quit,
150       bool *resize, unsigned *width, unsigned *height)
151 {
152    unsigned new_width       = 0;
153    unsigned new_height      = 0;
154    android_ctx_data_t *and  = (android_ctx_data_t*)data;
155 
156    *quit                    = false;
157 
158 #ifdef HAVE_EGL
159    egl_get_video_size(&and->egl, &new_width, &new_height);
160 #endif
161 
162    if (new_width != *width || new_height != *height)
163    {
164       RARCH_LOG("[Android]: Resizing (%u x %u) -> (%u x %u).\n",
165               *width, *height, new_width, new_height);
166 
167       *width  = new_width;
168       *height = new_height;
169       *resize = true;
170    }
171 }
172 
android_gfx_ctx_set_resize(void * data,unsigned width,unsigned height)173 static bool android_gfx_ctx_set_resize(void *data,
174       unsigned width, unsigned height) { return false; }
175 
android_gfx_ctx_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)176 static bool android_gfx_ctx_set_video_mode(void *data,
177       unsigned width, unsigned height,
178       bool fullscreen)
179 {
180 #if defined(HAVE_OPENGLES)
181    struct android_app *android_app = (struct android_app*)g_android;
182    android_ctx_data_t         *and = (android_ctx_data_t*)data;
183 #if defined(HAVE_EGL)
184    EGLint     context_attributes[] = {
185       EGL_CONTEXT_CLIENT_VERSION, g_es3 ? 3 : 2,
186 #if 0
187       EGL_CONTEXT_FLAGS_KHR, debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0,
188 #endif
189       EGL_NONE
190    };
191 #endif
192 #endif
193 
194    switch (android_api)
195    {
196       case GFX_CTX_OPENGL_API:
197          break;
198       case GFX_CTX_OPENGL_ES_API:
199 #if defined(HAVE_OPENGLES) && defined(HAVE_EGL)
200          if (!egl_create_context(&and->egl, context_attributes))
201          {
202             egl_report_error();
203             return false;
204          }
205 
206          if (!egl_create_surface(&and->egl, android_app->window))
207             return false;
208 #endif
209          break;
210 
211       case GFX_CTX_NONE:
212       default:
213          break;
214    }
215 
216    return true;
217 }
218 
android_gfx_ctx_input_driver(void * data,const char * joypad_name,input_driver_t ** input,void ** input_data)219 static void android_gfx_ctx_input_driver(void *data,
220       const char *joypad_name,
221       input_driver_t **input, void **input_data)
222 {
223    void *androidinput   = input_driver_init_wrap(&input_android, joypad_name);
224 
225    *input               = androidinput ? &input_android : NULL;
226    *input_data          = androidinput;
227 }
228 
android_gfx_ctx_get_api(void * data)229 static enum gfx_ctx_api android_gfx_ctx_get_api(void *data)
230 {
231    return android_api;
232 }
233 
android_gfx_ctx_bind_api(void * data,enum gfx_ctx_api api,unsigned major,unsigned minor)234 static bool android_gfx_ctx_bind_api(void *data,
235       enum gfx_ctx_api api, unsigned major, unsigned minor)
236 {
237    unsigned version;
238    android_api = api;
239 
240 #ifdef HAVE_OPENGLES
241    version     = major * 100 + minor;
242    if (version >= 300)
243       g_es3 = true;
244    if (api == GFX_CTX_OPENGL_ES_API)
245       return true;
246 #endif
247 
248    return false;
249 }
250 
android_gfx_ctx_has_focus(void * data)251 static bool android_gfx_ctx_has_focus(void *data)
252 {
253    bool                    focused = false;
254    struct android_app *android_app = (struct android_app*)g_android;
255    if (!android_app)
256       return true;
257 
258    slock_lock(android_app->mutex);
259    focused = !android_app->unfocused;
260    slock_unlock(android_app->mutex);
261 
262    return focused;
263 }
264 
android_gfx_ctx_suppress_screensaver(void * data,bool enable)265 static bool android_gfx_ctx_suppress_screensaver(void *data, bool enable) { return false; }
266 
android_gfx_ctx_get_metrics(void * data,enum display_metric_types type,float * value)267 static bool android_gfx_ctx_get_metrics(void *data,
268 	enum display_metric_types type, float *value)
269 {
270    static int dpi = -1;
271 
272    switch (type)
273    {
274       case DISPLAY_METRIC_MM_WIDTH:
275       case DISPLAY_METRIC_MM_HEIGHT:
276          return false;
277       case DISPLAY_METRIC_DPI:
278          if (dpi == -1)
279          {
280             char density[PROP_VALUE_MAX];
281             density[0] = '\0';
282 
283             android_dpi_get_density(density, sizeof(density));
284             if (string_is_empty(density))
285                goto dpi_fallback;
286             dpi    = atoi(density);
287 
288             if (dpi <= 0)
289                goto dpi_fallback;
290          }
291          *value = (float)dpi;
292          break;
293       case DISPLAY_METRIC_NONE:
294       default:
295          *value = 0;
296          return false;
297    }
298 
299    return true;
300 
301 dpi_fallback:
302    /* add a fallback in case the device doesn't report DPI.
303     * Hopefully fixes issues with the moto G2. */
304    dpi    = 90;
305    *value = (float)dpi;
306    return true;
307 }
308 
android_gfx_ctx_swap_buffers(void * data)309 static void android_gfx_ctx_swap_buffers(void *data)
310 {
311    android_ctx_data_t *and  = (android_ctx_data_t*)data;
312 
313 #ifdef HAVE_EGL
314    egl_swap_buffers(&and->egl);
315 #endif
316 }
317 
android_gfx_ctx_set_swap_interval(void * data,int swap_interval)318 static void android_gfx_ctx_set_swap_interval(void *data, int swap_interval)
319 {
320    android_ctx_data_t *and  = (android_ctx_data_t*)data;
321 
322 #ifdef HAVE_EGL
323    egl_set_swap_interval(&and->egl, swap_interval);
324 #endif
325 }
326 
android_gfx_ctx_bind_hw_render(void * data,bool enable)327 static void android_gfx_ctx_bind_hw_render(void *data, bool enable)
328 {
329    android_ctx_data_t *and  = (android_ctx_data_t*)data;
330 #ifdef HAVE_EGL
331    egl_bind_hw_render(&and->egl, enable);
332 #endif
333 }
334 
android_gfx_ctx_get_flags(void * data)335 static uint32_t android_gfx_ctx_get_flags(void *data)
336 {
337    uint32_t flags = 0;
338 
339 #ifdef HAVE_GLSL
340    BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
341 #endif
342 
343    return flags;
344 }
345 
android_gfx_ctx_set_flags(void * data,uint32_t flags)346 static void android_gfx_ctx_set_flags(void *data, uint32_t flags) { }
347 
348 const gfx_ctx_driver_t gfx_ctx_android = {
349    android_gfx_ctx_init,
350    android_gfx_ctx_destroy,
351    android_gfx_ctx_get_api,
352    android_gfx_ctx_bind_api,
353    android_gfx_ctx_set_swap_interval,
354    android_gfx_ctx_set_video_mode,
355    android_gfx_ctx_get_video_size,
356    NULL, /* get_refresh_rate */
357    NULL, /* get_video_output_size */
358    NULL, /* get_video_output_prev */
359    NULL, /* get_video_output_next */
360    android_gfx_ctx_get_metrics,
361    NULL,
362    NULL, /* update_title */
363    android_gfx_ctx_check_window,
364    android_gfx_ctx_set_resize,
365    android_gfx_ctx_has_focus,
366    android_gfx_ctx_suppress_screensaver,
367    false, /* has_windowed */
368    android_gfx_ctx_swap_buffers,
369    android_gfx_ctx_input_driver,
370 #ifdef HAVE_EGL
371    egl_get_proc_address,
372 #else
373    NULL,
374 #endif
375    NULL,
376    NULL,
377    NULL,
378    "egl_android",
379    android_gfx_ctx_get_flags,
380    android_gfx_ctx_set_flags,
381    android_gfx_ctx_bind_hw_render,
382    NULL,
383    NULL
384 };
385