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