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 /* X/EGL context. Mostly used for testing GLES code paths. */
18
19 #include <stdint.h>
20 #include <stdlib.h>
21
22 #include <string/stdstring.h>
23
24 #ifdef HAVE_CONFIG_H
25 #include "../../config.h"
26 #endif
27
28 #include "../../frontend/frontend_driver.h"
29 #include "../../configuration.h"
30 #include "../../input/input_driver.h"
31 #include "../../verbosity.h"
32
33 #include "../common/egl_common.h"
34 #include "../common/gl_common.h"
35 #include "../common/x11_common.h"
36
37 #ifdef HAVE_XINERAMA
38 #include "../common/xinerama_common.h"
39 #endif
40
41 #ifndef EGL_OPENGL_ES3_BIT_KHR
42 #define EGL_OPENGL_ES3_BIT_KHR 0x0040
43 #endif
44
45 #ifndef EGL_PLATFORM_X11_KHR
46 #define EGL_PLATFORM_X11_KHR 0x31D5
47 #endif
48
49 typedef struct
50 {
51 #ifdef HAVE_EGL
52 egl_ctx_data_t egl;
53 #endif
54 bool should_reset_mode;
55 } xegl_ctx_data_t;
56
57 /* TODO/FIXME - static globals */
58 static enum gfx_ctx_api xegl_api = GFX_CTX_NONE;
59
xegl_nul_handler(Display * dpy,XErrorEvent * event)60 static int xegl_nul_handler(Display *dpy, XErrorEvent *event) { return 0; }
61
gfx_ctx_xegl_destroy(void * data)62 static void gfx_ctx_xegl_destroy(void *data)
63 {
64 xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data;
65
66 x11_input_ctx_destroy();
67 #ifdef HAVE_EGL
68 egl_destroy(&xegl->egl);
69 #endif
70
71 if (g_x11_win)
72 {
73 #ifdef HAVE_XINERAMA
74 /* Save last used monitor for later. */
75 xinerama_save_last_used_monitor(RootWindow(
76 g_x11_dpy, DefaultScreen(g_x11_dpy)));
77 #endif
78 x11_window_destroy(false);
79 }
80
81 x11_colormap_destroy();
82
83 if (xegl->should_reset_mode)
84 {
85 x11_exit_fullscreen(g_x11_dpy);
86 xegl->should_reset_mode = false;
87 }
88
89 free(data);
90
91 /* Do not close g_x11_dpy. We'll keep one for the entire application
92 * lifecycle to work-around nVidia EGL limitations.
93 */
94 }
95
96 #define XEGL_ATTRIBS_BASE \
97 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, \
98 EGL_RED_SIZE, 1, \
99 EGL_GREEN_SIZE, 1, \
100 EGL_BLUE_SIZE, 1, \
101 EGL_ALPHA_SIZE, 0, \
102 EGL_DEPTH_SIZE, 0
103
gfx_ctx_xegl_init(void * video_driver)104 static void *gfx_ctx_xegl_init(void *video_driver)
105 {
106 #ifdef HAVE_EGL
107 static const EGLint egl_attribs_gl[] = {
108 XEGL_ATTRIBS_BASE,
109 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
110 EGL_NONE,
111 };
112
113 static const EGLint egl_attribs_gles[] = {
114 XEGL_ATTRIBS_BASE,
115 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
116 EGL_NONE,
117 };
118
119 #ifdef EGL_KHR_create_context
120 static const EGLint egl_attribs_gles3[] = {
121 XEGL_ATTRIBS_BASE,
122 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
123 EGL_NONE,
124 };
125 #endif
126 #ifdef HAVE_VG
127 static const EGLint egl_attribs_vg[] = {
128 XEGL_ATTRIBS_BASE,
129 EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
130 EGL_NONE,
131 };
132 #endif
133 const EGLint *attrib_ptr = NULL;
134 EGLint major, minor;
135 EGLint n;
136 #endif
137 xegl_ctx_data_t *xegl;
138
139 if (g_egl_inited)
140 return NULL;
141
142 XInitThreads();
143
144 xegl = (xegl_ctx_data_t*)calloc(1, sizeof(xegl_ctx_data_t));
145 if (!xegl)
146 return NULL;
147
148 switch (xegl_api)
149 {
150 case GFX_CTX_OPENGL_API:
151 attrib_ptr = egl_attribs_gl;
152 break;
153 case GFX_CTX_OPENGL_ES_API:
154 #ifdef EGL_KHR_create_context
155 if (xegl->egl.major >= 3)
156 attrib_ptr = egl_attribs_gles3;
157 else
158 #endif
159 attrib_ptr = egl_attribs_gles;
160 break;
161 case GFX_CTX_OPENVG_API:
162 #ifdef HAVE_VG
163 attrib_ptr = egl_attribs_vg;
164 #endif
165 break;
166 default:
167 break;
168 }
169
170 if (!x11_connect())
171 goto error;
172
173 #ifdef HAVE_EGL
174 if (!egl_init_context(&xegl->egl, EGL_PLATFORM_X11_KHR,
175 (EGLNativeDisplayType)g_x11_dpy, &major, &minor, &n, attrib_ptr, egl_default_accept_config_cb))
176 {
177 egl_report_error();
178 goto error;
179 }
180
181 if (n == 0 || !egl_has_config(&xegl->egl))
182 goto error;
183 #endif
184
185 return xegl;
186
187 error:
188 gfx_ctx_xegl_destroy(xegl);
189 return NULL;
190 }
191
xegl_fill_attribs(xegl_ctx_data_t * xegl,EGLint * attr)192 static EGLint *xegl_fill_attribs(xegl_ctx_data_t *xegl, EGLint *attr)
193 {
194 switch (xegl_api)
195 {
196 #ifdef EGL_KHR_create_context
197 case GFX_CTX_OPENGL_API:
198 {
199 unsigned version = xegl->egl.major * 1000 + xegl->egl.minor;
200 bool core = version >= 3001;
201 #ifdef GL_DEBUG
202 bool debug = true;
203 #else
204 struct retro_hw_render_callback *hwr = video_driver_get_hw_context();
205 bool debug = hwr->debug_context;
206 #endif
207
208 if (core)
209 {
210 *attr++ = EGL_CONTEXT_MAJOR_VERSION_KHR;
211 *attr++ = xegl->egl.major;
212 *attr++ = EGL_CONTEXT_MINOR_VERSION_KHR;
213 *attr++ = xegl->egl.minor;
214
215 /* Technically, we don't have core/compat until 3.2.
216 * Version 3.1 is either compat or not depending
217 * on GL_ARB_compatibility.
218 */
219 if (version >= 3002)
220 {
221 *attr++ = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
222 *attr++ = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
223 }
224 }
225
226 if (debug)
227 {
228 *attr++ = EGL_CONTEXT_FLAGS_KHR;
229 *attr++ = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
230 }
231
232 break;
233 }
234 #endif
235
236 case GFX_CTX_OPENGL_ES_API:
237 /* Same as EGL_CONTEXT_MAJOR_VERSION. */
238 *attr++ = EGL_CONTEXT_CLIENT_VERSION;
239 *attr++ = xegl->egl.major ? (EGLint)xegl->egl.major : 2;
240 #ifdef EGL_KHR_create_context
241 if (xegl->egl.minor > 0)
242 {
243 *attr++ = EGL_CONTEXT_MINOR_VERSION_KHR;
244 *attr++ = xegl->egl.minor;
245 }
246 #endif
247 break;
248
249 default:
250 break;
251 }
252
253 *attr = EGL_NONE;
254 return attr;
255 }
256
257 /* forward declaration */
258 static void gfx_ctx_xegl_set_swap_interval(void *data, int swap_interval);
259
gfx_ctx_xegl_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)260 static bool gfx_ctx_xegl_set_video_mode(void *data,
261 unsigned width, unsigned height,
262 bool fullscreen)
263 {
264 XEvent event;
265 EGLint egl_attribs[16];
266 EGLint vid, num_visuals;
267 EGLint *attr = NULL;
268 bool true_full = false;
269 int x_off = 0;
270 int y_off = 0;
271 XVisualInfo temp = {0};
272 XSetWindowAttributes swa = {0};
273 XVisualInfo *vi = NULL;
274 char *wm_name = NULL;
275 xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data;
276 settings_t *settings = config_get_ptr();
277 bool video_disable_composition = settings->bools.video_disable_composition;
278 bool windowed_fullscreen = settings->bools.video_windowed_fullscreen;
279 unsigned video_monitor_index = settings->uints.video_monitor_index;
280
281 int (*old_handler)(Display*, XErrorEvent*) = NULL;
282
283 frontend_driver_install_signal_handler();
284
285 attr = egl_attribs;
286 attr = xegl_fill_attribs(xegl, attr);
287
288 #ifdef HAVE_EGL
289 if (!egl_get_native_visual_id(&xegl->egl, &vid))
290 goto error;
291 #endif
292
293 temp.visualid = vid;
294
295 vi = XGetVisualInfo(g_x11_dpy, VisualIDMask, &temp, &num_visuals);
296 if (!vi)
297 goto error;
298
299 swa.colormap = g_x11_cmap = XCreateColormap(
300 g_x11_dpy, RootWindow(g_x11_dpy, vi->screen),
301 vi->visual, AllocNone);
302 swa.event_mask = StructureNotifyMask | KeyPressMask |
303 ButtonPressMask | ButtonReleaseMask | KeyReleaseMask |
304 EnterWindowMask | LeaveWindowMask;
305 swa.override_redirect = False;
306
307 if (fullscreen && !windowed_fullscreen)
308 {
309 if (x11_enter_fullscreen(g_x11_dpy, width, height))
310 {
311 xegl->should_reset_mode = true;
312 true_full = true;
313 }
314 else
315 RARCH_ERR("[X/EGL]: Entering true fullscreen failed. Will attempt windowed mode.\n");
316 }
317
318 wm_name = x11_get_wm_name(g_x11_dpy);
319 if (wm_name)
320 {
321 RARCH_LOG("[X/EGL]: Window manager is %s.\n", wm_name);
322
323 if (true_full && strcasestr(wm_name, "xfwm"))
324 {
325 RARCH_LOG("[X/EGL]: Using override-redirect workaround.\n");
326 swa.override_redirect = True;
327 }
328 free(wm_name);
329 }
330 if (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full)
331 swa.override_redirect = True;
332
333 if (video_monitor_index)
334 g_x11_screen = video_monitor_index - 1;
335
336 #ifdef HAVE_XINERAMA
337 if (fullscreen || g_x11_screen != 0)
338 {
339 unsigned new_width = width;
340 unsigned new_height = height;
341
342 if (xinerama_get_coord(g_x11_dpy, g_x11_screen,
343 &x_off, &y_off, &new_width, &new_height))
344 RARCH_LOG("[X/EGL]: Using Xinerama on screen #%u.\n", g_x11_screen);
345 else
346 RARCH_LOG("[X/EGL]: Xinerama is not active on screen.\n");
347
348 if (fullscreen)
349 {
350 width = new_width;
351 height = new_height;
352 }
353 }
354 #endif
355
356 RARCH_LOG("[X/EGL]: X = %d, Y = %d, W = %u, H = %u.\n",
357 x_off, y_off, width, height);
358
359 g_x11_win = XCreateWindow(g_x11_dpy, RootWindow(g_x11_dpy, vi->screen),
360 x_off, y_off, width, height, 0,
361 vi->depth, InputOutput, vi->visual,
362 CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect,
363 &swa);
364 XSetWindowBackground(g_x11_dpy, g_x11_win, 0);
365
366 if (fullscreen && video_disable_composition)
367 {
368 uint32_t value = 1;
369 Atom cardinal = XInternAtom(g_x11_dpy, "CARDINAL", False);
370 Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy, "_NET_WM_BYPASS_COMPOSITOR", False);
371
372 RARCH_LOG("[X/EGL]: Requesting compositor bypass.\n");
373 XChangeProperty(g_x11_dpy, g_x11_win, net_wm_bypass_compositor, cardinal, 32, PropModeReplace, (const unsigned char*)&value, 1);
374 }
375
376 if (!egl_create_context(&xegl->egl, (attr != egl_attribs) ? egl_attribs : NULL))
377 {
378 egl_report_error();
379 goto error;
380 }
381
382 if (!egl_create_surface(&xegl->egl, (void*)g_x11_win))
383 goto error;
384
385 x11_set_window_attr(g_x11_dpy, g_x11_win);
386 x11_update_title(NULL);
387
388 if (fullscreen)
389 x11_show_mouse(g_x11_dpy, g_x11_win, false);
390
391 if (true_full)
392 {
393 RARCH_LOG("[X/EGL]: Using true fullscreen.\n");
394 XMapRaised(g_x11_dpy, g_x11_win);
395 x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win);
396 }
397 else if (fullscreen)
398 {
399 /* We attempted true fullscreen, but failed.
400 * Attempt using windowed fullscreen. */
401 XMapRaised(g_x11_dpy, g_x11_win);
402 RARCH_LOG("[X/EGL]: Using windowed fullscreen.\n");
403
404 /* We have to move the window to the screen we
405 * want to go fullscreen on first.
406 * x_off and y_off usually get ignored in XCreateWindow().
407 */
408 x11_move_window(g_x11_dpy, g_x11_win, x_off, y_off, width, height);
409 x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win);
410 }
411 else
412 {
413 XMapWindow(g_x11_dpy, g_x11_win);
414
415 /* If we want to map the window on a different screen,
416 * we'll have to do it by force.
417 *
418 * Otherwise, we should try to let the window manager sort it out.
419 * x_off and y_off usually get ignored in XCreateWindow().
420 */
421 if (g_x11_screen)
422 x11_move_window(g_x11_dpy, g_x11_win, x_off, y_off, width, height);
423 }
424
425 x11_event_queue_check(&event);
426 x11_install_quit_atom();
427
428 #ifdef HAVE_EGL
429 gfx_ctx_xegl_set_swap_interval(&xegl->egl, xegl->egl.interval);
430 #endif
431
432 /* This can blow up on some drivers. It's not fatal,
433 * so override errors for this call.
434 */
435 old_handler = XSetErrorHandler(xegl_nul_handler);
436 XSetInputFocus(g_x11_dpy, g_x11_win, RevertToNone, CurrentTime);
437 XSync(g_x11_dpy, False);
438 XSetErrorHandler(old_handler);
439
440 XFree(vi);
441 g_egl_inited = true;
442
443 if (!x11_input_ctx_new(true_full))
444 goto error;
445
446 return true;
447
448 error:
449 if (vi)
450 XFree(vi);
451
452 gfx_ctx_xegl_destroy(data);
453 return false;
454 }
455
gfx_ctx_xegl_input_driver(void * data,const char * joypad_name,input_driver_t ** input,void ** input_data)456 static void gfx_ctx_xegl_input_driver(void *data,
457 const char *joypad_name,
458 input_driver_t **input, void **input_data)
459 {
460 void *xinput = input_driver_init_wrap(&input_x, joypad_name);
461
462 *input = xinput ? &input_x : NULL;
463 *input_data = xinput;
464 }
465
gfx_ctx_xegl_suppress_screensaver(void * data,bool enable)466 static bool gfx_ctx_xegl_suppress_screensaver(void *data, bool enable)
467 {
468 if (video_driver_display_type_get() != RARCH_DISPLAY_X11)
469 return false;
470
471 x11_suspend_screensaver(video_driver_window_get(), enable);
472
473 return true;
474 }
475
gfx_ctx_xegl_get_api(void * data)476 static enum gfx_ctx_api gfx_ctx_xegl_get_api(void *data) { return xegl_api; }
477
gfx_ctx_xegl_bind_api(void * video_driver,enum gfx_ctx_api api,unsigned major,unsigned minor)478 static bool gfx_ctx_xegl_bind_api(void *video_driver,
479 enum gfx_ctx_api api, unsigned major, unsigned minor)
480 {
481 g_egl_major = major;
482 g_egl_minor = minor;
483 xegl_api = api;
484
485 switch (api)
486 {
487 case GFX_CTX_OPENGL_API:
488 #ifndef EGL_KHR_create_context
489 if ((major * 1000 + minor) >= 3001)
490 break;
491 #endif
492 return egl_bind_api(EGL_OPENGL_API);
493 case GFX_CTX_OPENGL_ES_API:
494 #ifndef EGL_KHR_create_context
495 if (major >= 3)
496 break;
497 #endif
498 return egl_bind_api(EGL_OPENGL_ES_API);
499 case GFX_CTX_OPENVG_API:
500 #ifdef HAVE_VG
501 return egl_bind_api(EGL_OPENVG_API);
502 #endif
503 default:
504 break;
505 }
506
507 return false;
508 }
509
gfx_ctx_xegl_show_mouse(void * data,bool state)510 static void gfx_ctx_xegl_show_mouse(void *data, bool state)
511 {
512 x11_show_mouse(g_x11_dpy, g_x11_win, state);
513 }
514
gfx_ctx_xegl_swap_buffers(void * data)515 static void gfx_ctx_xegl_swap_buffers(void *data)
516 {
517 xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data;
518
519 #ifdef HAVE_EGL
520 egl_swap_buffers(&xegl->egl);
521 #endif
522 }
523
gfx_ctx_xegl_bind_hw_render(void * data,bool enable)524 static void gfx_ctx_xegl_bind_hw_render(void *data, bool enable)
525 {
526 xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data;
527
528 #ifdef HAVE_EGL
529 egl_bind_hw_render(&xegl->egl, enable);
530 #endif
531 }
532
gfx_ctx_xegl_set_swap_interval(void * data,int swap_interval)533 static void gfx_ctx_xegl_set_swap_interval(void *data, int swap_interval)
534 {
535 xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data;
536
537 #ifdef HAVE_EGL
538 egl_set_swap_interval(&xegl->egl, swap_interval);
539 #endif
540 }
541
gfx_ctx_xegl_get_proc_address(const char * symbol)542 static gfx_ctx_proc_t gfx_ctx_xegl_get_proc_address(const char *symbol)
543 {
544 switch (xegl_api)
545 {
546 case GFX_CTX_OPENGL_ES_API:
547 case GFX_CTX_OPENVG_API:
548 #ifdef HAVE_EGL
549 return egl_get_proc_address(symbol);
550 #else
551 break;
552 #endif
553 case GFX_CTX_OPENGL_API:
554 break;
555 case GFX_CTX_NONE:
556 default:
557 break;
558 }
559
560 return NULL;
561 }
562
gfx_ctx_xegl_get_flags(void * data)563 static uint32_t gfx_ctx_xegl_get_flags(void *data)
564 {
565 uint32_t flags = 0;
566
567 if (string_is_equal(video_driver_get_ident(), "glcore"))
568 {
569 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
570 BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
571 #endif
572 }
573 else
574 {
575 BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
576 }
577
578 return flags;
579 }
580
gfx_ctx_xegl_set_flags(void * data,uint32_t flags)581 static void gfx_ctx_xegl_set_flags(void *data, uint32_t flags) { }
582
583 const gfx_ctx_driver_t gfx_ctx_x_egl =
584 {
585 gfx_ctx_xegl_init,
586 gfx_ctx_xegl_destroy,
587 gfx_ctx_xegl_get_api,
588 gfx_ctx_xegl_bind_api,
589 gfx_ctx_xegl_set_swap_interval,
590 gfx_ctx_xegl_set_video_mode,
591 x11_get_video_size,
592 x11_get_refresh_rate,
593 NULL, /* get_video_output_size */
594 NULL, /* get_video_output_prev */
595 NULL, /* get_video_output_next */
596 x11_get_metrics,
597 NULL,
598 x11_update_title,
599 x11_check_window,
600 NULL, /* set_resize */
601 x11_has_focus,
602 gfx_ctx_xegl_suppress_screensaver,
603 true, /* has_windowed */
604 gfx_ctx_xegl_swap_buffers,
605 gfx_ctx_xegl_input_driver,
606 gfx_ctx_xegl_get_proc_address,
607 NULL,
608 NULL,
609 gfx_ctx_xegl_show_mouse,
610 "egl_x",
611 gfx_ctx_xegl_get_flags,
612 gfx_ctx_xegl_set_flags,
613 gfx_ctx_xegl_bind_hw_render,
614 NULL,
615 NULL
616 };
617