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