1 /*************************************************************************/
2 /*  context_gl_x11.cpp                                                   */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 
31 #include "context_gl_x11.h"
32 
33 #ifdef X11_ENABLED
34 #if defined(OPENGL_ENABLED)
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 
39 #define GLX_GLXEXT_PROTOTYPES
40 #include <GL/glx.h>
41 #include <GL/glxext.h>
42 
43 #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
44 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
45 
46 typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
47 
48 struct ContextGL_X11_Private {
49 
50 	::GLXContext glx_context;
51 };
52 
release_current()53 void ContextGL_X11::release_current() {
54 
55 	glXMakeCurrent(x11_display, None, NULL);
56 }
57 
make_current()58 void ContextGL_X11::make_current() {
59 
60 	glXMakeCurrent(x11_display, x11_window, p->glx_context);
61 }
62 
swap_buffers()63 void ContextGL_X11::swap_buffers() {
64 
65 	glXSwapBuffers(x11_display, x11_window);
66 }
67 
68 static bool ctxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * ev)69 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
70 	ctxErrorOccurred = true;
71 	return 0;
72 }
73 
set_class_hint(Display * p_display,Window p_window)74 static void set_class_hint(Display *p_display, Window p_window) {
75 	XClassHint *classHint;
76 
77 	/* set the name and class hints for the window manager to use */
78 	classHint = XAllocClassHint();
79 	if (classHint) {
80 		classHint->res_name = (char *)"Godot_Engine";
81 		classHint->res_class = (char *)"Godot";
82 	}
83 	XSetClassHint(p_display, p_window, classHint);
84 	XFree(classHint);
85 }
86 
initialize()87 Error ContextGL_X11::initialize() {
88 
89 	//const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
90 
91 	GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
92 
93 	ERR_FAIL_COND_V(!glXCreateContextAttribsARB, ERR_UNCONFIGURED);
94 
95 	static int visual_attribs[] = {
96 		GLX_RENDER_TYPE, GLX_RGBA_BIT,
97 		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
98 		GLX_DOUBLEBUFFER, true,
99 		GLX_RED_SIZE, 1,
100 		GLX_GREEN_SIZE, 1,
101 		GLX_BLUE_SIZE, 1,
102 		GLX_DEPTH_SIZE, 24,
103 		None
104 	};
105 
106 	static int visual_attribs_layered[] = {
107 		GLX_RENDER_TYPE, GLX_RGBA_BIT,
108 		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
109 		GLX_DOUBLEBUFFER, true,
110 		GLX_RED_SIZE, 8,
111 		GLX_GREEN_SIZE, 8,
112 		GLX_BLUE_SIZE, 8,
113 		GLX_ALPHA_SIZE, 8,
114 		GLX_DEPTH_SIZE, 24,
115 		None
116 	};
117 
118 	int fbcount;
119 	GLXFBConfig fbconfig = 0;
120 	XVisualInfo *vi = NULL;
121 
122 	XSetWindowAttributes swa;
123 	swa.event_mask = StructureNotifyMask;
124 	swa.border_pixel = 0;
125 	unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
126 
127 	if (OS::get_singleton()->is_layered_allowed()) {
128 		GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount);
129 		ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
130 
131 		for (int i = 0; i < fbcount; i++) {
132 			vi = (XVisualInfo *)glXGetVisualFromFBConfig(x11_display, fbc[i]);
133 			if (!vi)
134 				continue;
135 
136 			XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual);
137 			if (!pict_format) {
138 				XFree(vi);
139 				vi = NULL;
140 				continue;
141 			}
142 
143 			fbconfig = fbc[i];
144 			if (pict_format->direct.alphaMask > 0) {
145 				break;
146 			}
147 		}
148 		XFree(fbc);
149 		ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED);
150 
151 		swa.background_pixmap = None;
152 		swa.background_pixel = 0;
153 		swa.border_pixmap = None;
154 		valuemask |= CWBackPixel;
155 
156 	} else {
157 		GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
158 		ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
159 
160 		vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);
161 
162 		fbconfig = fbc[0];
163 		XFree(fbc);
164 	}
165 
166 	int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&ctxErrorHandler);
167 
168 	switch (context_type) {
169 		case OLDSTYLE: {
170 
171 			p->glx_context = glXCreateContext(x11_display, vi, 0, GL_TRUE);
172 			ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
173 		} break;
174 		case GLES_2_0_COMPATIBLE: {
175 
176 			p->glx_context = glXCreateNewContext(x11_display, fbconfig, GLX_RGBA_TYPE, 0, true);
177 			ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
178 		} break;
179 		case GLES_3_0_COMPATIBLE: {
180 
181 			static int context_attribs[] = {
182 				GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
183 				GLX_CONTEXT_MINOR_VERSION_ARB, 3,
184 				GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
185 				GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*|GLX_CONTEXT_DEBUG_BIT_ARB*/,
186 				None
187 			};
188 
189 			p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
190 			ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED);
191 		} break;
192 	}
193 
194 	swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
195 	x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa);
196 	XStoreName(x11_display, x11_window, "Godot Engine");
197 
198 	ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED);
199 	set_class_hint(x11_display, x11_window);
200 	XMapWindow(x11_display, x11_window);
201 
202 	XSync(x11_display, False);
203 	XSetErrorHandler(oldHandler);
204 
205 	glXMakeCurrent(x11_display, x11_window, p->glx_context);
206 
207 	XFree(vi);
208 
209 	return OK;
210 }
211 
get_window_width()212 int ContextGL_X11::get_window_width() {
213 
214 	XWindowAttributes xwa;
215 	XGetWindowAttributes(x11_display, x11_window, &xwa);
216 
217 	return xwa.width;
218 }
219 
get_window_height()220 int ContextGL_X11::get_window_height() {
221 	XWindowAttributes xwa;
222 	XGetWindowAttributes(x11_display, x11_window, &xwa);
223 
224 	return xwa.height;
225 }
226 
set_use_vsync(bool p_use)227 void ContextGL_X11::set_use_vsync(bool p_use) {
228 	static bool setup = false;
229 	static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL;
230 	static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = NULL;
231 	static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = NULL;
232 
233 	if (!setup) {
234 		setup = true;
235 		String extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
236 		if (extensions.find("GLX_EXT_swap_control") != -1)
237 			glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
238 		if (extensions.find("GLX_MESA_swap_control") != -1)
239 			glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA");
240 		if (extensions.find("GLX_SGI_swap_control") != -1)
241 			glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI");
242 	}
243 	int val = p_use ? 1 : 0;
244 	if (glXSwapIntervalMESA) {
245 		glXSwapIntervalMESA(val);
246 	} else if (glXSwapIntervalSGI) {
247 		glXSwapIntervalSGI(val);
248 	} else if (glXSwapIntervalEXT) {
249 		GLXDrawable drawable = glXGetCurrentDrawable();
250 		glXSwapIntervalEXT(x11_display, drawable, val);
251 	} else
252 		return;
253 	use_vsync = p_use;
254 }
is_using_vsync() const255 bool ContextGL_X11::is_using_vsync() const {
256 
257 	return use_vsync;
258 }
259 
ContextGL_X11(::Display * p_x11_display,::Window & p_x11_window,const OS::VideoMode & p_default_video_mode,ContextType p_context_type)260 ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type) :
261 		x11_window(p_x11_window) {
262 
263 	default_video_mode = p_default_video_mode;
264 	x11_display = p_x11_display;
265 
266 	context_type = p_context_type;
267 
268 	double_buffer = false;
269 	direct_render = false;
270 	glx_minor = glx_major = 0;
271 	p = memnew(ContextGL_X11_Private);
272 	p->glx_context = 0;
273 	use_vsync = false;
274 }
275 
~ContextGL_X11()276 ContextGL_X11::~ContextGL_X11() {
277 	release_current();
278 	glXDestroyContext(x11_display, p->glx_context);
279 	memdelete(p);
280 }
281 
282 #endif
283 #endif
284