1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *  Copyright (C) 2016-2019 - Brad Parker
5  *
6  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
7  *  of the GNU General Public License as published by the Free Software Found-
8  *  ation, either version 3 of the License, or (at your option) any later version.
9  *
10  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12  *  PURPOSE.  See the GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along with RetroArch.
15  *  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <signal.h>
21 
22 #ifdef HAVE_CONFIG_H
23 #include "../../config.h"
24 #endif
25 
26 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
27 #include <GL/glx.h>
28 
29 #ifndef GLX_SAMPLE_BUFFERS
30 #define GLX_SAMPLE_BUFFERS 100000
31 #endif
32 
33 #ifndef GLX_SAMPLES
34 #define GLX_SAMPLES 100001
35 #endif
36 
37 #endif
38 
39 #include <string/stdstring.h>
40 #include <compat/strcasestr.h>
41 #include <X11/Xatom.h>
42 
43 #include "../../configuration.h"
44 #include "../../frontend/frontend_driver.h"
45 #include "../../input/input_driver.h"
46 #include "../../verbosity.h"
47 #include "../common/gl_common.h"
48 #include "../common/x11_common.h"
49 
50 #ifdef HAVE_XINERAMA
51 #include "../common/xinerama_common.h"
52 #endif
53 
54 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
55 static int      (*g_pglSwapInterval)(int);
56 static int      (*g_pglSwapIntervalSGI)(int);
57 static void     (*g_pglSwapIntervalEXT)(Display*, GLXDrawable, int);
58 #endif
59 
60 typedef struct gfx_ctx_x_data
61 {
62    bool use_hw_ctx;
63    bool core_es;
64    bool core_es_core;
65    bool debug;
66    bool should_reset_mode;
67    bool is_fullscreen;
68    bool is_double;
69    bool core_hw_context_enable;
70    bool adaptive_vsync;
71    bool msaa_enable;
72 
73 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
74    GLXWindow glx_win;
75    GLXContext ctx, hw_ctx;
76    GLXFBConfig fbc;
77    unsigned swap_mode;
78 #endif
79 
80    int interval;
81 } gfx_ctx_x_data_t;
82 
83 /* TODO/FIXME - static globals */
84 static unsigned g_major                       = 0;
85 static unsigned g_minor                       = 0;
86 static enum gfx_ctx_api x_api                 = GFX_CTX_NONE;
87 static gfx_ctx_x_data_t *current_context_data = NULL;
88 
89 typedef struct Hints
90 {
91    unsigned long flags;
92    unsigned long functions;
93    unsigned long decorations;
94    long          inputMode;
95    unsigned long status;
96 } Hints;
97 
98 /* We use long because X11 wants 32-bit pixels for 32-bit systems and 64 for 64... */
99 /* ARGB*/
100 static const unsigned long retroarch_icon_data[] = {
101    16, 16,
102 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
103 0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000,
104 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
105 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
106 0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
107 0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
108 0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
109 0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
110 0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
111 0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
112 0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
113 0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
114 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
115 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
116 0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000,
117 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
118 };
119 
120 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
121 static PFNGLXCREATECONTEXTATTRIBSARBPROC glx_create_context_attribs;
122 
GLXExtensionSupported(Display * dpy,const char * extension)123 static int GLXExtensionSupported(Display *dpy, const char *extension)
124 {
125    const char *extensionsString  = glXQueryExtensionsString(dpy, DefaultScreen(dpy));
126    const char *client_extensions = glXGetClientString(dpy, GLX_EXTENSIONS);
127    const char *pos               = strstr(extensionsString, extension);
128    size_t pos_ext_len            = strlen(extension);
129 
130    if (  pos &&
131          (pos == extensionsString || pos[-1] == ' ') &&
132          (pos[pos_ext_len] == ' ' || pos[pos_ext_len] == '\0')
133       )
134       return 1;
135 
136    pos                           = strstr(client_extensions, extension);
137    pos_ext_len                   = strlen(extension);
138 
139    if (
140          pos &&
141          (pos == extensionsString || pos[-1] == ' ') &&
142          (pos[pos_ext_len] == ' ' || pos[pos_ext_len] == '\0')
143       )
144       return 1;
145 
146    return 0;
147 }
148 #endif
149 
x_log_error_handler(Display * dpy,XErrorEvent * event)150 static int x_log_error_handler(Display *dpy, XErrorEvent *event)
151 {
152    char buf[1024];
153    XGetErrorText(dpy, event->error_code, buf, sizeof buf);
154    RARCH_WARN("[GLX]: X error message: %s, request code: %d, minor code: %d\n",
155          buf, event->request_code, event->minor_code);
156    return 0;
157 }
158 
x_nul_handler(Display * dpy,XErrorEvent * event)159 static int x_nul_handler(Display *dpy, XErrorEvent *event) { return 0; }
160 
gfx_ctx_x_destroy_resources(gfx_ctx_x_data_t * x)161 static void gfx_ctx_x_destroy_resources(gfx_ctx_x_data_t *x)
162 {
163    x11_input_ctx_destroy();
164 
165    if (g_x11_dpy)
166    {
167       switch (x_api)
168       {
169          case GFX_CTX_OPENGL_API:
170          case GFX_CTX_OPENGL_ES_API:
171 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
172             if (x->ctx)
173             {
174                glXSwapBuffers(g_x11_dpy, x->glx_win);
175                glFinish();
176                glXMakeContextCurrent(g_x11_dpy, None, None, NULL);
177 
178                if (!video_driver_is_video_cache_context())
179                {
180                   if (x->hw_ctx)
181                      glXDestroyContext(g_x11_dpy, x->hw_ctx);
182                   if (x->ctx)
183                      glXDestroyContext(g_x11_dpy, x->ctx);
184 
185                   x->ctx    = NULL;
186                   x->hw_ctx = NULL;
187                }
188             }
189 
190             if (g_x11_win)
191             {
192                if (x->glx_win)
193                   glXDestroyWindow(g_x11_dpy, x->glx_win);
194                x->glx_win = 0;
195             }
196 #endif
197             break;
198          case GFX_CTX_NONE:
199          default:
200             break;
201       }
202    }
203 
204    if (g_x11_win && g_x11_dpy)
205    {
206 #ifdef HAVE_XINERAMA
207       /* Save last used monitor for later. */
208       xinerama_save_last_used_monitor(DefaultRootWindow(g_x11_dpy));
209 #endif
210       x11_window_destroy(false);
211    }
212 
213    x11_colormap_destroy();
214 
215    if (g_x11_dpy)
216    {
217       if (x->should_reset_mode)
218       {
219          x11_exit_fullscreen(g_x11_dpy);
220          x->should_reset_mode = false;
221       }
222    }
223 
224 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
225    g_pglSwapInterval    = NULL;
226    g_pglSwapIntervalSGI = NULL;
227    g_pglSwapIntervalEXT = NULL;
228 #endif
229    g_major              = 0;
230    g_minor              = 0;
231    x->core_es           = false;
232 }
233 
gfx_ctx_x_destroy(void * data)234 static void gfx_ctx_x_destroy(void *data)
235 {
236    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
237    if (!x)
238       return;
239 
240    gfx_ctx_x_destroy_resources(x);
241 
242    free(data);
243 }
244 
gfx_ctx_x_swap_interval(void * data,int interval)245 static void gfx_ctx_x_swap_interval(void *data, int interval)
246 {
247    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
248 
249 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
250    x->interval = interval;
251 
252    if (x->swap_mode)
253    {
254       if (g_pglSwapInterval)
255       {
256          if (g_pglSwapInterval(x->interval) != 0)
257             RARCH_WARN("[GLX]: glXSwapInterval(%i) failed.\n", x->interval);
258       }
259       else if (g_pglSwapIntervalEXT)
260          g_pglSwapIntervalEXT(g_x11_dpy, x->glx_win, x->interval);
261       else if (g_pglSwapIntervalSGI)
262       {
263          if (g_pglSwapIntervalSGI(x->interval) != 0)
264             RARCH_WARN("[GLX]: glXSwapIntervalSGI(%i) failed.\n", x->interval);
265       }
266    }
267    else
268    {
269       if (g_pglSwapIntervalEXT)
270          g_pglSwapIntervalEXT(g_x11_dpy, x->glx_win, x->interval);
271       else if (g_pglSwapInterval)
272       {
273          if (g_pglSwapInterval(x->interval) != 0)
274             RARCH_WARN("[GLX]: glXSwapInterval(%i) failed.\n", x->interval);
275       }
276       else if (g_pglSwapIntervalSGI)
277       {
278          if (g_pglSwapIntervalSGI(x->interval) != 0)
279             RARCH_WARN("[GLX]: glXSwapIntervalSGI(%i) failed.\n", x->interval);
280       }
281    }
282 #endif
283 }
284 
gfx_ctx_x_swap_buffers(void * data)285 static void gfx_ctx_x_swap_buffers(void *data)
286 {
287    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
288 
289 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
290    if (x->is_double)
291       glXSwapBuffers(g_x11_dpy, x->glx_win);
292 #endif
293 }
294 
gfx_ctx_x_set_resize(void * data,unsigned width,unsigned height)295 static bool gfx_ctx_x_set_resize(void *data,
296       unsigned width, unsigned height)
297 {
298    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
299 
300    if (!x)
301       return false;
302 
303    /*
304     * X11 loses focus on monitor/resolution swap and exits fullscreen.
305     * Set window on top again to maintain both fullscreen and resolution.
306     */
307    if (x->is_fullscreen) {
308       XMapRaised(g_x11_dpy, g_x11_win);
309       RARCH_LOG("[GLX]: Resized fullscreen resolution to %dx%d.\n", width, height);
310    }
311 
312    return true;
313 }
314 
gfx_ctx_x_init(void * data)315 static void *gfx_ctx_x_init(void *data)
316 {
317    int nelements           = 0;
318    int major               = 0;
319    int minor               = 0;
320 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
321    static const int visual_attribs[] = {
322       GLX_X_RENDERABLE     , True,
323       GLX_DRAWABLE_TYPE    , GLX_WINDOW_BIT,
324       GLX_RENDER_TYPE      , GLX_RGBA_BIT,
325       GLX_DOUBLEBUFFER     , True,
326       GLX_RED_SIZE         , 8,
327       GLX_GREEN_SIZE       , 8,
328       GLX_BLUE_SIZE        , 8,
329       GLX_ALPHA_SIZE       , 8,
330       GLX_DEPTH_SIZE       , 0,
331       GLX_STENCIL_SIZE     , 0,
332       GLX_SAMPLE_BUFFERS   , 0,
333       GLX_SAMPLES          , 0,
334       None
335    };
336    GLXFBConfig *fbcs       = NULL;
337 #endif
338    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)
339       calloc(1, sizeof(gfx_ctx_x_data_t));
340 #ifndef GL_DEBUG
341    struct retro_hw_render_callback *hwr =
342       video_driver_get_hw_context();
343 #endif
344 
345    if (!x)
346       return NULL;
347 
348    current_context_data = x;
349 
350    XInitThreads();
351 
352    if (!x11_connect())
353       goto error;
354 
355    switch (x_api)
356    {
357       case GFX_CTX_OPENGL_API:
358       case GFX_CTX_OPENGL_ES_API:
359 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGLES) || defined(HAVE_OPENGL_CORE)
360          glXQueryVersion(g_x11_dpy, &major, &minor);
361 
362          /* GLX 1.3+ minimum required. */
363          if ((major * 1000 + minor) < 1003)
364             goto error;
365 
366          glx_create_context_attribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
367             glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
368 
369 #ifdef GL_DEBUG
370          x->debug          = true;
371 #else
372          x->debug          = hwr->debug_context;
373 #endif
374 
375          /* Have to use ContextAttribs */
376 #ifdef HAVE_OPENGLES2
377          x->core_es        = true;
378          x->core_es_core   = true;
379 #else
380          x->core_es        = (g_major * 1000 + g_minor) >= 3001;
381          x->core_es_core   = (g_major * 1000 + g_minor) >= 3002;
382 #endif
383 
384          if ((x->core_es || x->debug) && !glx_create_context_attribs)
385             goto error;
386 
387          fbcs = glXChooseFBConfig(g_x11_dpy, DefaultScreen(g_x11_dpy),
388                visual_attribs, &nelements);
389 
390          if (!fbcs)
391             goto error;
392 
393          if (!nelements)
394          {
395             XFree(fbcs);
396             goto error;
397          }
398 
399          x->fbc = fbcs[0];
400          XFree(fbcs);
401 #endif
402          break;
403       case GFX_CTX_NONE:
404       default:
405          break;
406    }
407 
408    switch (x_api)
409    {
410       case GFX_CTX_OPENGL_API:
411 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
412          if (GLXExtensionSupported(g_x11_dpy, "GLX_EXT_swap_control_tear"))
413          {
414             RARCH_LOG("[GLX]: GLX_EXT_swap_control_tear supported.\n");
415             x->adaptive_vsync = true;
416          }
417 
418          if (GLXExtensionSupported(g_x11_dpy, "GLX_OML_sync_control") &&
419                GLXExtensionSupported(g_x11_dpy, "GLX_MESA_swap_control")
420             )
421             x->swap_mode         = 1;
422 #endif
423          break;
424       default:
425          break;
426    }
427 
428    return x;
429 
430 error:
431    if (x)
432    {
433       gfx_ctx_x_destroy_resources(x);
434       free(x);
435    }
436    g_x11_screen = 0;
437 
438    return NULL;
439 }
440 
gfx_ctx_x_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)441 static bool gfx_ctx_x_set_video_mode(void *data,
442       unsigned width, unsigned height,
443       bool fullscreen)
444 {
445    XEvent event;
446    bool true_full            = false;
447    int val                   = 0;
448    int x_off                 = 0;
449    int y_off                 = 0;
450    XVisualInfo *vi           = NULL;
451    XSetWindowAttributes swa  = {0};
452    char *wm_name             = NULL;
453    int (*old_handler)(Display*, XErrorEvent*) = NULL;
454    gfx_ctx_x_data_t *x       = (gfx_ctx_x_data_t*)data;
455    Atom net_wm_icon          = XInternAtom(g_x11_dpy, "_NET_WM_ICON", False);
456    Atom cardinal             = XInternAtom(g_x11_dpy, "CARDINAL", False);
457    settings_t *settings      = config_get_ptr();
458    unsigned opacity          = settings->uints.video_window_opacity
459       * ((unsigned)-1 / 100.0);
460    bool disable_composition  = settings->bools.video_disable_composition;
461    bool show_decorations     = settings->bools.video_window_show_decorations;
462    bool windowed_full        = settings->bools.video_windowed_fullscreen;
463    unsigned video_monitor_index = settings->uints.video_monitor_index;
464 
465    frontend_driver_install_signal_handler();
466 
467    if (!x)
468       return false;
469 
470    switch (x_api)
471    {
472       case GFX_CTX_OPENGL_API:
473       case GFX_CTX_OPENGL_ES_API:
474 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
475          vi = glXGetVisualFromFBConfig(g_x11_dpy, x->fbc);
476          if (!vi)
477             goto error;
478 #endif
479          break;
480 
481       case GFX_CTX_NONE:
482       default:
483       {
484          XVisualInfo vi_template;
485          /* For default case, just try to obtain a visual from template. */
486          int nvisuals = 0;
487 
488          memset(&vi_template, 0, sizeof(vi_template));
489          vi_template.screen = DefaultScreen(g_x11_dpy);
490          vi = XGetVisualInfo(g_x11_dpy, VisualScreenMask, &vi_template, &nvisuals);
491          if (!vi || nvisuals < 1)
492             goto error;
493       }
494       break;
495    }
496 
497    swa.colormap = g_x11_cmap = XCreateColormap(g_x11_dpy,
498          RootWindow(g_x11_dpy, vi->screen), vi->visual, AllocNone);
499    swa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
500       LeaveWindowMask | EnterWindowMask |
501       ButtonReleaseMask | ButtonPressMask;
502    swa.override_redirect = False;
503 
504    x->is_fullscreen = fullscreen;
505 
506    if (fullscreen && !windowed_full)
507    {
508       if (x11_enter_fullscreen(g_x11_dpy, width, height))
509       {
510          x->should_reset_mode = true;
511          true_full = true;
512       }
513       else
514          RARCH_ERR("[GLX]: Entering true fullscreen failed. Will attempt windowed mode.\n");
515    }
516 
517    wm_name = x11_get_wm_name(g_x11_dpy);
518    if (wm_name)
519    {
520       RARCH_LOG("[GLX]: Window manager is %s.\n", wm_name);
521 
522       if (true_full && strcasestr(wm_name, "xfwm"))
523       {
524          RARCH_LOG("[GLX]: Using override-redirect workaround.\n");
525          swa.override_redirect = True;
526       }
527       free(wm_name);
528    }
529    if (!x11_has_net_wm_fullscreen(g_x11_dpy) && true_full)
530       swa.override_redirect = True;
531 
532    if (video_monitor_index)
533       g_x11_screen = video_monitor_index - 1;
534 
535 #ifdef HAVE_XINERAMA
536    if (fullscreen || g_x11_screen != 0)
537    {
538       unsigned new_width  = width;
539       unsigned new_height = height;
540 
541       if (xinerama_get_coord(g_x11_dpy, g_x11_screen,
542                &x_off, &y_off, &new_width, &new_height))
543          RARCH_LOG("[GLX]: Using Xinerama on screen #%u.\n", g_x11_screen);
544       else
545          RARCH_LOG("[GLX]: Xinerama is not active on screen.\n");
546 
547       if (fullscreen)
548       {
549          width  = new_width;
550          height = new_height;
551       }
552    }
553 #endif
554 
555    RARCH_LOG("[GLX]: X = %d, Y = %d, W = %u, H = %u.\n",
556          x_off, y_off, width, height);
557 
558    g_x11_win = XCreateWindow(g_x11_dpy, RootWindow(g_x11_dpy, vi->screen),
559          x_off, y_off, width, height, 0,
560          vi->depth, InputOutput, vi->visual,
561          CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect,
562          &swa);
563    XSetWindowBackground(g_x11_dpy, g_x11_win, 0);
564 
565    XChangeProperty(g_x11_dpy, g_x11_win, net_wm_icon, cardinal, 32, PropModeReplace, (const unsigned char*)retroarch_icon_data, sizeof(retroarch_icon_data) / sizeof(*retroarch_icon_data));
566 
567    if (fullscreen && disable_composition)
568    {
569       uint32_t                value = 1;
570       Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy, "_NET_WM_BYPASS_COMPOSITOR", False);
571 
572       RARCH_LOG("[GLX]: Requesting compositor bypass.\n");
573       XChangeProperty(g_x11_dpy, g_x11_win, net_wm_bypass_compositor, cardinal, 32, PropModeReplace, (const unsigned char*)&value, 1);
574    }
575 
576    if (opacity < (unsigned)-1)
577    {
578       Atom net_wm_opacity = XInternAtom(g_x11_dpy, "_NET_WM_WINDOW_OPACITY", False);
579       XChangeProperty(g_x11_dpy, g_x11_win, net_wm_opacity, cardinal, 32, PropModeReplace, (const unsigned char*)&opacity, 1);
580    }
581 
582    if (!show_decorations)
583    {
584       /* We could have just set _NET_WM_WINDOW_TYPE_DOCK instead,
585        * but that removes the window from any taskbar/panel,
586        * so we are forced to use the old motif hints method. */
587       Hints hints;
588       Atom property     = XInternAtom(g_x11_dpy, "_MOTIF_WM_HINTS", False);
589 
590       hints.flags       = 2;
591       hints.decorations = 0;
592 
593       XChangeProperty(g_x11_dpy, g_x11_win, property, property, 32, PropModeReplace, (const unsigned char*)&hints, 5);
594    }
595 
596    switch (x_api)
597    {
598       case GFX_CTX_OPENGL_API:
599       case GFX_CTX_OPENGL_ES_API:
600 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
601          x->glx_win = glXCreateWindow(g_x11_dpy, x->fbc, g_x11_win, 0);
602 #endif
603          break;
604 
605       case GFX_CTX_NONE:
606       default:
607          break;
608    }
609 
610    x11_set_window_attr(g_x11_dpy, g_x11_win);
611    x11_update_title(NULL);
612 
613    if (fullscreen)
614       x11_show_mouse(g_x11_dpy, g_x11_win, false);
615 
616    if (true_full)
617    {
618       RARCH_LOG("[GLX]: Using true fullscreen.\n");
619       XMapRaised(g_x11_dpy, g_x11_win);
620       x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win);
621    }
622    else if (fullscreen)
623    {
624       /* We attempted true fullscreen, but failed.
625        * Attempt using windowed fullscreen. */
626 
627       XMapRaised(g_x11_dpy, g_x11_win);
628       RARCH_LOG("[GLX]: Using windowed fullscreen.\n");
629 
630       /* We have to move the window to the screen we want
631        * to go fullscreen on first.
632        * x_off and y_off usually get ignored in XCreateWindow().
633        */
634       x11_move_window(g_x11_dpy, g_x11_win, x_off, y_off, width, height);
635       x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win);
636    }
637    else
638    {
639       XMapWindow(g_x11_dpy, g_x11_win);
640       /* If we want to map the window on a different screen,
641        * we'll have to do it by force.
642        * Otherwise, we should try to let the window manager sort it out.
643        * x_off and y_off usually get ignored in XCreateWindow(). */
644       if (g_x11_screen)
645          x11_move_window(g_x11_dpy, g_x11_win, x_off, y_off, width, height);
646    }
647 
648    x11_event_queue_check(&event);
649 
650    switch (x_api)
651    {
652       case GFX_CTX_OPENGL_API:
653       case GFX_CTX_OPENGL_ES_API:
654 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
655          if (!x->ctx)
656          {
657             if (x->core_es || x->debug)
658             {
659                int attribs[16] = {0};
660                int *aptr = attribs;
661 
662                if (x->core_es)
663                {
664                   *aptr++ = GLX_CONTEXT_MAJOR_VERSION_ARB;
665                   *aptr++ = g_major;
666                   *aptr++ = GLX_CONTEXT_MINOR_VERSION_ARB;
667                   *aptr++ = g_minor;
668 
669                   if (x->core_es_core)
670                   {
671                      /* Technically, we don't have core/compat until 3.2.
672                       * Version 3.1 is either compat or not depending on
673                       * GL_ARB_compatibility.
674                       */
675                      *aptr++ = GLX_CONTEXT_PROFILE_MASK_ARB;
676 #ifdef HAVE_OPENGLES2
677                      *aptr++ = GLX_CONTEXT_ES_PROFILE_BIT_EXT;
678 #else
679                      *aptr++ = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
680 #endif
681                   }
682                }
683 
684                if (x->debug)
685                {
686                   *aptr++ = GLX_CONTEXT_FLAGS_ARB;
687                   *aptr++ = GLX_CONTEXT_DEBUG_BIT_ARB;
688                }
689 
690                *aptr = None;
691 
692                old_handler = XSetErrorHandler(x_log_error_handler);
693 
694                /* In order to support the core info "required_hw_api" field correctly, we should try to init the highest available
695                 * version GL context possible. This means trying successively lower versions until it works, because GL has
696                 * no facility for determining the highest possible supported version.
697                 */
698                {
699                   int i;
700                   int gl_versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, {3, 1}, {3, 0}};
701 #ifdef HAVE_OPENGLES3
702                   int gles_versions[][2] = {{3, 2}, {3, 1}, {3, 0}, {2, 0}, {1, 1}, {1, 0}};
703 #else
704                   int gles_versions[][2] = {{2, 0}, {1, 1}, {1, 0}};
705 #endif
706                   int gl_version_rows = ARRAY_SIZE(gl_versions);
707                   int gles_version_rows = ARRAY_SIZE(gles_versions);
708                   int (*versions)[2];
709                   int version_rows = 0;
710 
711                   if (x_api == GFX_CTX_OPENGL_API)
712                   {
713                      versions = gl_versions;
714                      version_rows = gl_version_rows;
715                   }
716                   else
717                   {
718                      versions = gles_versions;
719                      version_rows = gles_version_rows;
720                   }
721 
722                   /* Mesa/X currently crashes when an unsupported version is
723                    * requested. Since Mesa always seems to return a context
724                    * of the highest compatible version, we start with the
725                    * requested version first.
726                    * The following code can hopefully be removed in the future:
727                    */
728                   RARCH_LOG("[GLX]: Creating context for requested version %u.%u.\n", g_major, g_minor);
729                   x->ctx = glx_create_context_attribs(g_x11_dpy,
730                         x->fbc, NULL, True, attribs);
731 
732                   if (x->ctx)
733                   {
734                      const char *version;
735 
736                      if (x->use_hw_ctx)
737                      {
738                         RARCH_LOG("[GLX]: Creating shared HW context.\n");
739                         x->hw_ctx = glx_create_context_attribs(g_x11_dpy,
740                               x->fbc, x->ctx, True, attribs);
741 
742                         if (!x->hw_ctx)
743                            RARCH_ERR("[GLX]: Failed to create new shared context.\n");
744                      }
745 
746                      glXMakeContextCurrent(g_x11_dpy,
747                            x->glx_win, x->glx_win, x->ctx);
748 
749                      version = (const char*)glGetString(GL_VERSION);
750                      if (strstr(version, " Mesa ") || !x->core_es)
751                      {
752                         /* we are done, break switch case */
753                         XSetErrorHandler(old_handler);
754                         break;
755                      }
756 
757                      glXMakeContextCurrent(g_x11_dpy, None, None, NULL);
758                      glXDestroyContext(g_x11_dpy, x->ctx);
759 
760                      RARCH_LOG("[GLX]: Not running Mesa, trying higher versions...\n");
761                   }
762                   else
763                   {
764                      RARCH_ERR("[GLX]: Failed to create new context.\n");
765                      goto error;
766                   }
767                   /* end of Mesa workaround / code to be removed */
768 
769                   /* only try higher versions when x->core_es is true */
770                   if (!x->core_es)
771                      version_rows = 1;
772 
773                   /* try versions from highest down to requested version */
774                   for (i = 0; i < version_rows; i++)
775                   {
776                      if (x->core_es)
777                      {
778                         attribs[1] = versions[i][0];
779                         attribs[3] = versions[i][1];
780                         RARCH_LOG("[GLX]: Creating context for version %d.%d.\n", versions[i][0], versions[i][1]);
781                      }
782                      else
783                         RARCH_LOG("[GLX]: Creating context for version %u.%u.\n", g_major, g_minor);
784 
785                      x->ctx = glx_create_context_attribs(g_x11_dpy,
786                            x->fbc, NULL, True, attribs);
787 
788                      if (x->ctx)
789                      {
790                         if (x->use_hw_ctx)
791                         {
792                            RARCH_LOG("[GLX]: Creating shared HW context.\n");
793                            x->hw_ctx = glx_create_context_attribs(g_x11_dpy,
794                                  x->fbc, x->ctx, True, attribs);
795 
796                            if (!x->hw_ctx)
797                               RARCH_ERR("[GLX]: Failed to create new shared context.\n");
798                         }
799 
800                         break;
801                      }
802                      else if (versions[i][0] == g_major && versions[i][1] == g_minor)
803                      {
804                         /* The requested version was tried and is not supported, go ahead and fail since everything else will be lower than that. */
805                         break;
806                      }
807                   }
808                }
809 
810                XSetErrorHandler(old_handler);
811             }
812             else
813             {
814                x->ctx = glXCreateNewContext(g_x11_dpy, x->fbc,
815                      GLX_RGBA_TYPE, 0, True);
816 
817                if (x->use_hw_ctx)
818                {
819                   RARCH_LOG("[GLX]: Creating shared HW context.\n");
820                   x->hw_ctx = glXCreateNewContext(g_x11_dpy, x->fbc,
821                         GLX_RGBA_TYPE, x->ctx, True);
822 
823                   if (!x->hw_ctx)
824                      RARCH_ERR("[GLX]: Failed to create new shared context.\n");
825                }
826             }
827 
828             if (!x->ctx)
829             {
830                RARCH_ERR("[GLX]: Failed to create new context.\n");
831                goto error;
832             }
833          }
834          else
835          {
836             video_driver_set_video_cache_context_ack();
837             RARCH_LOG("[GLX]: Using cached GL context.\n");
838          }
839 
840          glXMakeContextCurrent(g_x11_dpy,
841                x->glx_win, x->glx_win, x->ctx);
842 #endif
843          break;
844       case GFX_CTX_NONE:
845       default:
846          break;
847    }
848 
849    XSync(g_x11_dpy, False);
850 
851    x11_install_quit_atom();
852 
853    switch (x_api)
854    {
855       case GFX_CTX_OPENGL_API:
856       case GFX_CTX_OPENGL_ES_API:
857 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
858          glXGetConfig(g_x11_dpy, vi, GLX_DOUBLEBUFFER, &val);
859          x->is_double = val;
860 
861          if (x->is_double)
862          {
863             const char *swap_func = NULL;
864 
865             g_pglSwapIntervalEXT = (void (*)(Display*, GLXDrawable, int))
866                glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
867             g_pglSwapIntervalSGI = (int (*)(int))
868                glXGetProcAddress((const GLubyte*)"glXSwapIntervalSGI");
869             g_pglSwapInterval    = (int (*)(int))
870                glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA");
871 
872             if (g_pglSwapIntervalEXT)
873                swap_func = "glXSwapIntervalEXT";
874             else if (g_pglSwapInterval)
875                swap_func = "glXSwapIntervalMESA";
876             else if (g_pglSwapIntervalSGI)
877                swap_func = "glXSwapIntervalSGI";
878 
879             if (!g_pglSwapInterval && !g_pglSwapIntervalEXT && !g_pglSwapIntervalSGI)
880                RARCH_WARN("[GLX]: Cannot find swap interval call.\n");
881             else
882                RARCH_LOG("[GLX]: Found swap function: %s.\n", swap_func);
883          }
884          else
885             RARCH_WARN("[GLX]: Context is not double buffered!.\n");
886 #endif
887          break;
888 
889       case GFX_CTX_NONE:
890       default:
891          break;
892    }
893 
894    gfx_ctx_x_swap_interval(data, x->interval);
895 
896    /* This can blow up on some drivers.
897     * It's not fatal, so override errors for this call. */
898    old_handler = XSetErrorHandler(x_nul_handler);
899    XSetInputFocus(g_x11_dpy, g_x11_win, RevertToNone, CurrentTime);
900    XSync(g_x11_dpy, False);
901    XSetErrorHandler(old_handler);
902 
903    XFree(vi);
904    vi = NULL;
905 
906    if (!x11_input_ctx_new(true_full))
907       goto error;
908 
909    return true;
910 
911 error:
912    if (vi)
913       XFree(vi);
914 
915    gfx_ctx_x_destroy_resources(x);
916 
917    if (x)
918       free(x);
919    g_x11_screen = 0;
920 
921    return false;
922 }
923 
gfx_ctx_x_input_driver(void * data,const char * joypad_name,input_driver_t ** input,void ** input_data)924 static void gfx_ctx_x_input_driver(void *data,
925       const char *joypad_name,
926       input_driver_t **input, void **input_data)
927 {
928    void *x_input            = NULL;
929 #ifdef HAVE_UDEV
930    settings_t *settings     = config_get_ptr();
931    const char *input_driver = settings->arrays.input_driver;
932 
933    if (string_is_equal(input_driver, "udev"))
934    {
935       *input_data = input_driver_init_wrap(&input_udev, joypad_name);
936       if (*input_data)
937       {
938          *input = &input_udev;
939          return;
940       }
941    }
942 #endif
943 
944    x_input      = input_driver_init_wrap(&input_x, joypad_name);
945    *input       = x_input ? &input_x : NULL;
946    *input_data  = x_input;
947 }
948 
gfx_ctx_x_suppress_screensaver(void * data,bool enable)949 static bool gfx_ctx_x_suppress_screensaver(void *data, bool enable)
950 {
951    (void)data;
952 
953    if (video_driver_display_type_get() != RARCH_DISPLAY_X11)
954       return false;
955 
956    x11_suspend_screensaver(video_driver_window_get(), enable);
957 
958    return true;
959 }
960 
gfx_ctx_x_get_proc_address(const char * symbol)961 static gfx_ctx_proc_t gfx_ctx_x_get_proc_address(const char *symbol)
962 {
963    switch (x_api)
964    {
965       case GFX_CTX_OPENGL_API:
966       case GFX_CTX_OPENGL_ES_API:
967 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
968          return glXGetProcAddress((const GLubyte*)symbol);
969 #else
970          break;
971 #endif
972       case GFX_CTX_NONE:
973       default:
974          break;
975    }
976 
977    return NULL;
978 }
979 
gfx_ctx_x_get_api(void * data)980 static enum gfx_ctx_api gfx_ctx_x_get_api(void *data)
981 {
982    return x_api;
983 }
984 
gfx_ctx_x_bind_api(void * data,enum gfx_ctx_api api,unsigned major,unsigned minor)985 static bool gfx_ctx_x_bind_api(void *data, enum gfx_ctx_api api,
986       unsigned major, unsigned minor)
987 {
988    g_major = major;
989    g_minor = minor;
990    x_api   = api;
991 
992    switch (api)
993    {
994       case GFX_CTX_OPENGL_API:
995 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
996          return true;
997 #else
998          break;
999 #endif
1000       case GFX_CTX_OPENGL_ES_API:
1001 #ifdef HAVE_OPENGLES2
1002          {
1003             Display     *dpy = XOpenDisplay(NULL);
1004             const char *exts = glXQueryExtensionsString(dpy, DefaultScreen(dpy));
1005             bool ret         = exts && strstr(exts,
1006                   "GLX_EXT_create_context_es2_profile");
1007             XCloseDisplay(dpy);
1008             if (ret && g_major < 3)
1009             {
1010                g_major = 2; /* ES 2.0. */
1011                g_minor = 0;
1012             }
1013             return ret;
1014          }
1015 #else
1016          break;
1017 #endif
1018       case GFX_CTX_NONE:
1019       default:
1020          break;
1021    }
1022 
1023    return false;
1024 }
1025 
gfx_ctx_x_show_mouse(void * data,bool state)1026 static void gfx_ctx_x_show_mouse(void *data, bool state)
1027 {
1028    x11_show_mouse(g_x11_dpy, g_x11_win, state);
1029 }
1030 
gfx_ctx_x_bind_hw_render(void * data,bool enable)1031 static void gfx_ctx_x_bind_hw_render(void *data, bool enable)
1032 {
1033    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
1034 
1035    if (!x)
1036       return;
1037 
1038    switch (x_api)
1039    {
1040       case GFX_CTX_OPENGL_API:
1041       case GFX_CTX_OPENGL_ES_API:
1042 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
1043          x->use_hw_ctx = enable;
1044          if (!g_x11_dpy || !x->glx_win)
1045             return;
1046          glXMakeContextCurrent(g_x11_dpy, x->glx_win,
1047                x->glx_win, enable ? x->hw_ctx : x->ctx);
1048 #endif
1049          break;
1050 
1051       case GFX_CTX_NONE:
1052       default:
1053          break;
1054    }
1055 }
1056 
gfx_ctx_x_get_flags(void * data)1057 static uint32_t gfx_ctx_x_get_flags(void *data)
1058 {
1059    uint32_t      flags = 0;
1060    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
1061 
1062    switch (x_api)
1063    {
1064       case GFX_CTX_OPENGL_API:
1065       case GFX_CTX_OPENGL_ES_API:
1066          if (x->adaptive_vsync)
1067             BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
1068 
1069          if (x->core_hw_context_enable || x->core_es)
1070             BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
1071 
1072          if (x->msaa_enable)
1073             BIT32_SET(flags, GFX_CTX_FLAGS_MULTISAMPLING);
1074 
1075          if (string_is_equal(video_driver_get_ident(), "gl1")) { }
1076          else if (string_is_equal(video_driver_get_ident(), "glcore"))
1077          {
1078 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
1079             BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
1080 #endif
1081          }
1082          else
1083          {
1084 #ifdef HAVE_CG
1085             if (!(x->core_hw_context_enable || x->core_es))
1086                BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_CG);
1087 #endif
1088 #ifdef HAVE_GLSL
1089             BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
1090 #endif
1091          }
1092          break;
1093       case GFX_CTX_NONE:
1094       default:
1095          break;
1096    }
1097 
1098    return flags;
1099 }
1100 
gfx_ctx_x_set_flags(void * data,uint32_t flags)1101 static void gfx_ctx_x_set_flags(void *data, uint32_t flags)
1102 {
1103    gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
1104 
1105    switch (x_api)
1106    {
1107       case GFX_CTX_OPENGL_API:
1108       case GFX_CTX_OPENGL_ES_API:
1109          if (BIT32_GET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC))
1110                x->adaptive_vsync = true;
1111          if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
1112             x->core_hw_context_enable = true;
1113          if (BIT32_GET(flags, GFX_CTX_FLAGS_MULTISAMPLING))
1114             x->msaa_enable = true;
1115          break;
1116       case GFX_CTX_NONE:
1117       default:
1118          break;
1119    }
1120 }
1121 
gfx_ctx_x_make_current(bool release)1122 static void gfx_ctx_x_make_current(bool release)
1123 {
1124    if (!current_context_data)
1125       return;
1126 
1127    switch (x_api)
1128    {
1129       case GFX_CTX_OPENGL_API:
1130       case GFX_CTX_OPENGL_ES_API:
1131 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)
1132          if (release)
1133             glXMakeContextCurrent(g_x11_dpy, None, None, NULL);
1134          else
1135             glXMakeContextCurrent(g_x11_dpy,
1136                   current_context_data->glx_win,
1137                   current_context_data->glx_win, current_context_data->ctx);
1138 #endif
1139          break;
1140 
1141       case GFX_CTX_NONE:
1142       default:
1143          break;
1144    }
1145 }
1146 
1147 const gfx_ctx_driver_t gfx_ctx_x = {
1148    gfx_ctx_x_init,
1149    gfx_ctx_x_destroy,
1150    gfx_ctx_x_get_api,
1151    gfx_ctx_x_bind_api,
1152    gfx_ctx_x_swap_interval,
1153    gfx_ctx_x_set_video_mode,
1154    x11_get_video_size,
1155    x11_get_refresh_rate,
1156    NULL, /* get_video_output_size */
1157    NULL, /* get_video_output_prev */
1158    NULL, /* get_video_output_next */
1159    x11_get_metrics,
1160    NULL,
1161    x11_update_title,
1162    x11_check_window,
1163    gfx_ctx_x_set_resize,
1164    x11_has_focus,
1165    gfx_ctx_x_suppress_screensaver,
1166    true, /* has_windowed */
1167    gfx_ctx_x_swap_buffers,
1168    gfx_ctx_x_input_driver,
1169    gfx_ctx_x_get_proc_address,
1170    NULL,
1171    NULL,
1172    gfx_ctx_x_show_mouse,
1173    "x",
1174    gfx_ctx_x_get_flags,
1175    gfx_ctx_x_set_flags,
1176 
1177    gfx_ctx_x_bind_hw_render,
1178    NULL,
1179    gfx_ctx_x_make_current
1180 };
1181