1 | //======================================================================== |
2 | // GLFW 3.4 EGL - www.glfw.org |
3 | //------------------------------------------------------------------------ |
4 | // Copyright (c) 2002-2006 Marcus Geelnard |
5 | // Copyright (c) 2006-2019 Camilla Löwy <[email protected]> |
6 | // |
7 | // This software is provided 'as-is', without any express or implied |
8 | // warranty. In no event will the authors be held liable for any damages |
9 | // arising from the use of this software. |
10 | // |
11 | // Permission is granted to anyone to use this software for any purpose, |
12 | // including commercial applications, and to alter it and redistribute it |
13 | // freely, subject to the following restrictions: |
14 | // |
15 | // 1. The origin of this software must not be misrepresented; you must not |
16 | // claim that you wrote the original software. If you use this software |
17 | // in a product, an acknowledgment in the product documentation would |
18 | // be appreciated but is not required. |
19 | // |
20 | // 2. Altered source versions must be plainly marked as such, and must not |
21 | // be misrepresented as being the original software. |
22 | // |
23 | // 3. This notice may not be removed or altered from any source |
24 | // distribution. |
25 | // |
26 | //======================================================================== |
27 | // Please use C89 style variable declarations in this file because VS 2010 |
28 | //======================================================================== |
29 | |
30 | #include "internal.h" |
31 | |
32 | #include <stdio.h> |
33 | #include <string.h> |
34 | #include <stdlib.h> |
35 | #include <assert.h> |
36 | |
37 | |
38 | // Return a description of the specified EGL error |
39 | // |
40 | static const char* getEGLErrorString(EGLint error) |
41 | { |
42 | switch (error) |
43 | { |
44 | case EGL_SUCCESS: |
45 | return "Success" ; |
46 | case EGL_NOT_INITIALIZED: |
47 | return "EGL is not or could not be initialized" ; |
48 | case EGL_BAD_ACCESS: |
49 | return "EGL cannot access a requested resource" ; |
50 | case EGL_BAD_ALLOC: |
51 | return "EGL failed to allocate resources for the requested operation" ; |
52 | case EGL_BAD_ATTRIBUTE: |
53 | return "An unrecognized attribute or attribute value was passed in the attribute list" ; |
54 | case EGL_BAD_CONTEXT: |
55 | return "An EGLContext argument does not name a valid EGL rendering context" ; |
56 | case EGL_BAD_CONFIG: |
57 | return "An EGLConfig argument does not name a valid EGL frame buffer configuration" ; |
58 | case EGL_BAD_CURRENT_SURFACE: |
59 | return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid" ; |
60 | case EGL_BAD_DISPLAY: |
61 | return "An EGLDisplay argument does not name a valid EGL display connection" ; |
62 | case EGL_BAD_SURFACE: |
63 | return "An EGLSurface argument does not name a valid surface configured for GL rendering" ; |
64 | case EGL_BAD_MATCH: |
65 | return "Arguments are inconsistent" ; |
66 | case EGL_BAD_PARAMETER: |
67 | return "One or more argument values are invalid" ; |
68 | case EGL_BAD_NATIVE_PIXMAP: |
69 | return "A NativePixmapType argument does not refer to a valid native pixmap" ; |
70 | case EGL_BAD_NATIVE_WINDOW: |
71 | return "A NativeWindowType argument does not refer to a valid native window" ; |
72 | case EGL_CONTEXT_LOST: |
73 | return "The application must destroy all contexts and reinitialise" ; |
74 | default: |
75 | return "ERROR: UNKNOWN EGL ERROR" ; |
76 | } |
77 | } |
78 | |
79 | // Returns the specified attribute of the specified EGLConfig |
80 | // |
81 | static int getEGLConfigAttrib(EGLConfig config, int attrib) |
82 | { |
83 | int value; |
84 | eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); |
85 | return value; |
86 | } |
87 | |
88 | // Return the EGLConfig most closely matching the specified hints |
89 | // |
90 | static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, |
91 | const _GLFWfbconfig* desired, |
92 | EGLConfig* result) |
93 | { |
94 | EGLConfig* nativeConfigs; |
95 | _GLFWfbconfig* usableConfigs; |
96 | const _GLFWfbconfig* closest; |
97 | int i, nativeCount, usableCount; |
98 | |
99 | eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); |
100 | if (!nativeCount) |
101 | { |
102 | _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned" ); |
103 | return GLFW_FALSE; |
104 | } |
105 | |
106 | nativeConfigs = _glfw_calloc(nativeCount, sizeof(EGLConfig)); |
107 | eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); |
108 | |
109 | usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); |
110 | usableCount = 0; |
111 | |
112 | for (i = 0; i < nativeCount; i++) |
113 | { |
114 | const EGLConfig n = nativeConfigs[i]; |
115 | _GLFWfbconfig* u = usableConfigs + usableCount; |
116 | |
117 | // Only consider RGB(A) EGLConfigs |
118 | if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) |
119 | continue; |
120 | |
121 | // Only consider window EGLConfigs |
122 | if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) |
123 | continue; |
124 | |
125 | #if defined(_GLFW_X11) |
126 | if (_glfw.platform.platformID == GLFW_PLATFORM_X11) |
127 | { |
128 | XVisualInfo vi = {0}; |
129 | |
130 | // Only consider EGLConfigs with associated Visuals |
131 | vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); |
132 | if (!vi.visualid) |
133 | continue; |
134 | |
135 | if (desired->transparent) |
136 | { |
137 | int count; |
138 | XVisualInfo* vis = |
139 | XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count); |
140 | if (vis) |
141 | { |
142 | u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); |
143 | XFree(vis); |
144 | } |
145 | } |
146 | } |
147 | #endif // _GLFW_X11 |
148 | |
149 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
150 | { |
151 | if (ctxconfig->major == 1) |
152 | { |
153 | if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) |
154 | continue; |
155 | } |
156 | else |
157 | { |
158 | if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) |
159 | continue; |
160 | } |
161 | } |
162 | else if (ctxconfig->client == GLFW_OPENGL_API) |
163 | { |
164 | if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) |
165 | continue; |
166 | } |
167 | |
168 | u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); |
169 | u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); |
170 | u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); |
171 | |
172 | u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); |
173 | u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); |
174 | u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); |
175 | |
176 | u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); |
177 | u->doublebuffer = desired->doublebuffer; |
178 | |
179 | u->handle = (uintptr_t) n; |
180 | usableCount++; |
181 | } |
182 | |
183 | closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); |
184 | if (closest) |
185 | *result = (EGLConfig) closest->handle; |
186 | |
187 | _glfw_free(nativeConfigs); |
188 | _glfw_free(usableConfigs); |
189 | |
190 | return closest != NULL; |
191 | } |
192 | |
193 | static void makeContextCurrentEGL(_GLFWwindow* window) |
194 | { |
195 | if (window) |
196 | { |
197 | if (!eglMakeCurrent(_glfw.egl.display, |
198 | window->context.egl.surface, |
199 | window->context.egl.surface, |
200 | window->context.egl.handle)) |
201 | { |
202 | _glfwInputError(GLFW_PLATFORM_ERROR, |
203 | "EGL: Failed to make context current: %s" , |
204 | getEGLErrorString(eglGetError())); |
205 | return; |
206 | } |
207 | } |
208 | else |
209 | { |
210 | if (!eglMakeCurrent(_glfw.egl.display, |
211 | EGL_NO_SURFACE, |
212 | EGL_NO_SURFACE, |
213 | EGL_NO_CONTEXT)) |
214 | { |
215 | _glfwInputError(GLFW_PLATFORM_ERROR, |
216 | "EGL: Failed to clear current context: %s" , |
217 | getEGLErrorString(eglGetError())); |
218 | return; |
219 | } |
220 | } |
221 | |
222 | _glfwPlatformSetTls(&_glfw.contextSlot, window); |
223 | } |
224 | |
225 | static void swapBuffersEGL(_GLFWwindow* window) |
226 | { |
227 | if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) |
228 | { |
229 | _glfwInputError(GLFW_PLATFORM_ERROR, |
230 | "EGL: The context must be current on the calling thread when swapping buffers" ); |
231 | return; |
232 | } |
233 | |
234 | eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); |
235 | } |
236 | |
237 | static void swapIntervalEGL(int interval) |
238 | { |
239 | eglSwapInterval(_glfw.egl.display, interval); |
240 | } |
241 | |
242 | static int extensionSupportedEGL(const char* extension) |
243 | { |
244 | const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); |
245 | if (extensions) |
246 | { |
247 | if (_glfwStringInExtensionString(extension, extensions)) |
248 | return GLFW_TRUE; |
249 | } |
250 | |
251 | return GLFW_FALSE; |
252 | } |
253 | |
254 | static GLFWglproc getProcAddressEGL(const char* procname) |
255 | { |
256 | _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); |
257 | |
258 | if (window->context.egl.client) |
259 | { |
260 | GLFWglproc proc = (GLFWglproc) |
261 | _glfwPlatformGetModuleSymbol(window->context.egl.client, procname); |
262 | if (proc) |
263 | return proc; |
264 | } |
265 | |
266 | return eglGetProcAddress(procname); |
267 | } |
268 | |
269 | static void destroyContextEGL(_GLFWwindow* window) |
270 | { |
271 | // NOTE: Do not unload libGL.so.1 while the X11 display is still open, |
272 | // as it will make XCloseDisplay segfault |
273 | if (_glfw.platform.platformID != GLFW_PLATFORM_X11 || |
274 | window->context.client != GLFW_OPENGL_API) |
275 | { |
276 | if (window->context.egl.client) |
277 | { |
278 | _glfwPlatformFreeModule(window->context.egl.client); |
279 | window->context.egl.client = NULL; |
280 | } |
281 | } |
282 | |
283 | if (window->context.egl.surface) |
284 | { |
285 | eglDestroySurface(_glfw.egl.display, window->context.egl.surface); |
286 | window->context.egl.surface = EGL_NO_SURFACE; |
287 | } |
288 | |
289 | if (window->context.egl.handle) |
290 | { |
291 | eglDestroyContext(_glfw.egl.display, window->context.egl.handle); |
292 | window->context.egl.handle = EGL_NO_CONTEXT; |
293 | } |
294 | } |
295 | |
296 | |
297 | ////////////////////////////////////////////////////////////////////////// |
298 | ////// GLFW internal API ////// |
299 | ////////////////////////////////////////////////////////////////////////// |
300 | |
301 | // Initialize EGL |
302 | // |
303 | GLFWbool _glfwInitEGL(void) |
304 | { |
305 | int i; |
306 | EGLint* attribs = NULL; |
307 | const char* extensions; |
308 | const char* sonames[] = |
309 | { |
310 | #if defined(_GLFW_EGL_LIBRARY) |
311 | _GLFW_EGL_LIBRARY, |
312 | #elif defined(_GLFW_WIN32) |
313 | "libEGL.dll" , |
314 | "EGL.dll" , |
315 | #elif defined(_GLFW_COCOA) |
316 | "libEGL.dylib" , |
317 | #elif defined(__CYGWIN__) |
318 | "libEGL-1.so" , |
319 | #else |
320 | "libEGL.so.1" , |
321 | #endif |
322 | NULL |
323 | }; |
324 | |
325 | if (_glfw.egl.handle) |
326 | return GLFW_TRUE; |
327 | |
328 | for (i = 0; sonames[i]; i++) |
329 | { |
330 | _glfw.egl.handle = _glfwPlatformLoadModule(sonames[i]); |
331 | if (_glfw.egl.handle) |
332 | break; |
333 | } |
334 | |
335 | if (!_glfw.egl.handle) |
336 | { |
337 | _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found" ); |
338 | return GLFW_FALSE; |
339 | } |
340 | |
341 | _glfw.egl.prefix = (strncmp(sonames[i], "lib" , 3) == 0); |
342 | |
343 | _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) |
344 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetConfigAttrib" ); |
345 | _glfw.egl.GetConfigs = (PFN_eglGetConfigs) |
346 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetConfigs" ); |
347 | _glfw.egl.GetDisplay = (PFN_eglGetDisplay) |
348 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetDisplay" ); |
349 | _glfw.egl.GetError = (PFN_eglGetError) |
350 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetError" ); |
351 | _glfw.egl.Initialize = (PFN_eglInitialize) |
352 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglInitialize" ); |
353 | _glfw.egl.Terminate = (PFN_eglTerminate) |
354 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglTerminate" ); |
355 | _glfw.egl.BindAPI = (PFN_eglBindAPI) |
356 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglBindAPI" ); |
357 | _glfw.egl.CreateContext = (PFN_eglCreateContext) |
358 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglCreateContext" ); |
359 | _glfw.egl.DestroySurface = (PFN_eglDestroySurface) |
360 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglDestroySurface" ); |
361 | _glfw.egl.DestroyContext = (PFN_eglDestroyContext) |
362 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglDestroyContext" ); |
363 | _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) |
364 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglCreateWindowSurface" ); |
365 | _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) |
366 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglMakeCurrent" ); |
367 | _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) |
368 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglSwapBuffers" ); |
369 | _glfw.egl.SwapInterval = (PFN_eglSwapInterval) |
370 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglSwapInterval" ); |
371 | _glfw.egl.QueryString = (PFN_eglQueryString) |
372 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglQueryString" ); |
373 | _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) |
374 | _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetProcAddress" ); |
375 | |
376 | if (!_glfw.egl.GetConfigAttrib || |
377 | !_glfw.egl.GetConfigs || |
378 | !_glfw.egl.GetDisplay || |
379 | !_glfw.egl.GetError || |
380 | !_glfw.egl.Initialize || |
381 | !_glfw.egl.Terminate || |
382 | !_glfw.egl.BindAPI || |
383 | !_glfw.egl.CreateContext || |
384 | !_glfw.egl.DestroySurface || |
385 | !_glfw.egl.DestroyContext || |
386 | !_glfw.egl.CreateWindowSurface || |
387 | !_glfw.egl.MakeCurrent || |
388 | !_glfw.egl.SwapBuffers || |
389 | !_glfw.egl.SwapInterval || |
390 | !_glfw.egl.QueryString || |
391 | !_glfw.egl.GetProcAddress) |
392 | { |
393 | _glfwInputError(GLFW_PLATFORM_ERROR, |
394 | "EGL: Failed to load required entry points" ); |
395 | |
396 | _glfwTerminateEGL(); |
397 | return GLFW_FALSE; |
398 | } |
399 | |
400 | extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); |
401 | if (extensions && eglGetError() == EGL_SUCCESS) |
402 | _glfw.egl.EXT_client_extensions = GLFW_TRUE; |
403 | |
404 | if (_glfw.egl.EXT_client_extensions) |
405 | { |
406 | _glfw.egl.EXT_platform_base = |
407 | _glfwStringInExtensionString("EGL_EXT_platform_base" , extensions); |
408 | _glfw.egl.EXT_platform_x11 = |
409 | _glfwStringInExtensionString("EGL_EXT_platform_x11" , extensions); |
410 | _glfw.egl.EXT_platform_wayland = |
411 | _glfwStringInExtensionString("EGL_EXT_platform_wayland" , extensions); |
412 | _glfw.egl.ANGLE_platform_angle = |
413 | _glfwStringInExtensionString("EGL_ANGLE_platform_angle" , extensions); |
414 | _glfw.egl.ANGLE_platform_angle_opengl = |
415 | _glfwStringInExtensionString("EGL_ANGLE_platform_angle_opengl" , extensions); |
416 | _glfw.egl.ANGLE_platform_angle_d3d = |
417 | _glfwStringInExtensionString("EGL_ANGLE_platform_angle_d3d" , extensions); |
418 | _glfw.egl.ANGLE_platform_angle_vulkan = |
419 | _glfwStringInExtensionString("EGL_ANGLE_platform_angle_vulkan" , extensions); |
420 | _glfw.egl.ANGLE_platform_angle_metal = |
421 | _glfwStringInExtensionString("EGL_ANGLE_platform_angle_metal" , extensions); |
422 | } |
423 | |
424 | if (_glfw.egl.EXT_platform_base) |
425 | { |
426 | _glfw.egl.GetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC) |
427 | eglGetProcAddress("eglGetPlatformDisplayEXT" ); |
428 | _glfw.egl.CreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) |
429 | eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT" ); |
430 | } |
431 | |
432 | _glfw.egl.platform = _glfw.platform.getEGLPlatform(&attribs); |
433 | if (_glfw.egl.platform) |
434 | { |
435 | _glfw.egl.display = |
436 | eglGetPlatformDisplayEXT(_glfw.egl.platform, |
437 | _glfw.platform.getEGLNativeDisplay(), |
438 | attribs); |
439 | } |
440 | else |
441 | _glfw.egl.display = eglGetDisplay(_glfw.platform.getEGLNativeDisplay()); |
442 | |
443 | _glfw_free(attribs); |
444 | |
445 | if (_glfw.egl.display == EGL_NO_DISPLAY) |
446 | { |
447 | _glfwInputError(GLFW_API_UNAVAILABLE, |
448 | "EGL: Failed to get EGL display: %s" , |
449 | getEGLErrorString(eglGetError())); |
450 | |
451 | _glfwTerminateEGL(); |
452 | return GLFW_FALSE; |
453 | } |
454 | |
455 | if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) |
456 | { |
457 | _glfwInputError(GLFW_API_UNAVAILABLE, |
458 | "EGL: Failed to initialize EGL: %s" , |
459 | getEGLErrorString(eglGetError())); |
460 | |
461 | _glfwTerminateEGL(); |
462 | return GLFW_FALSE; |
463 | } |
464 | |
465 | _glfw.egl.KHR_create_context = |
466 | extensionSupportedEGL("EGL_KHR_create_context" ); |
467 | _glfw.egl.KHR_create_context_no_error = |
468 | extensionSupportedEGL("EGL_KHR_create_context_no_error" ); |
469 | _glfw.egl.KHR_gl_colorspace = |
470 | extensionSupportedEGL("EGL_KHR_gl_colorspace" ); |
471 | _glfw.egl.KHR_get_all_proc_addresses = |
472 | extensionSupportedEGL("EGL_KHR_get_all_proc_addresses" ); |
473 | _glfw.egl.KHR_context_flush_control = |
474 | extensionSupportedEGL("EGL_KHR_context_flush_control" ); |
475 | |
476 | return GLFW_TRUE; |
477 | } |
478 | |
479 | // Terminate EGL |
480 | // |
481 | void _glfwTerminateEGL(void) |
482 | { |
483 | if (_glfw.egl.display) |
484 | { |
485 | eglTerminate(_glfw.egl.display); |
486 | _glfw.egl.display = EGL_NO_DISPLAY; |
487 | } |
488 | |
489 | if (_glfw.egl.handle) |
490 | { |
491 | _glfwPlatformFreeModule(_glfw.egl.handle); |
492 | _glfw.egl.handle = NULL; |
493 | } |
494 | } |
495 | |
496 | #define setAttrib(a, v) \ |
497 | { \ |
498 | assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ |
499 | attribs[index++] = a; \ |
500 | attribs[index++] = v; \ |
501 | } |
502 | |
503 | // Create the OpenGL or OpenGL ES context |
504 | // |
505 | GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, |
506 | const _GLFWctxconfig* ctxconfig, |
507 | const _GLFWfbconfig* fbconfig) |
508 | { |
509 | EGLint attribs[40]; |
510 | EGLConfig config; |
511 | EGLContext share = NULL; |
512 | EGLNativeWindowType native; |
513 | int index = 0; |
514 | |
515 | if (!_glfw.egl.display) |
516 | { |
517 | _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available" ); |
518 | return GLFW_FALSE; |
519 | } |
520 | |
521 | if (ctxconfig->share) |
522 | share = ctxconfig->share->context.egl.handle; |
523 | |
524 | if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) |
525 | { |
526 | _glfwInputError(GLFW_FORMAT_UNAVAILABLE, |
527 | "EGL: Failed to find a suitable EGLConfig" ); |
528 | return GLFW_FALSE; |
529 | } |
530 | |
531 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
532 | { |
533 | if (!eglBindAPI(EGL_OPENGL_ES_API)) |
534 | { |
535 | _glfwInputError(GLFW_API_UNAVAILABLE, |
536 | "EGL: Failed to bind OpenGL ES: %s" , |
537 | getEGLErrorString(eglGetError())); |
538 | return GLFW_FALSE; |
539 | } |
540 | } |
541 | else |
542 | { |
543 | if (!eglBindAPI(EGL_OPENGL_API)) |
544 | { |
545 | _glfwInputError(GLFW_API_UNAVAILABLE, |
546 | "EGL: Failed to bind OpenGL: %s" , |
547 | getEGLErrorString(eglGetError())); |
548 | return GLFW_FALSE; |
549 | } |
550 | } |
551 | |
552 | if (_glfw.egl.KHR_create_context) |
553 | { |
554 | int mask = 0, flags = 0; |
555 | |
556 | if (ctxconfig->client == GLFW_OPENGL_API) |
557 | { |
558 | if (ctxconfig->forward) |
559 | flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; |
560 | |
561 | if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) |
562 | mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; |
563 | else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) |
564 | mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; |
565 | } |
566 | |
567 | if (ctxconfig->debug) |
568 | flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; |
569 | |
570 | if (ctxconfig->robustness) |
571 | { |
572 | if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) |
573 | { |
574 | setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, |
575 | EGL_NO_RESET_NOTIFICATION_KHR); |
576 | } |
577 | else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) |
578 | { |
579 | setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, |
580 | EGL_LOSE_CONTEXT_ON_RESET_KHR); |
581 | } |
582 | |
583 | flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; |
584 | } |
585 | |
586 | if (ctxconfig->noerror) |
587 | { |
588 | if (_glfw.egl.KHR_create_context_no_error) |
589 | setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); |
590 | } |
591 | |
592 | if (ctxconfig->major != 1 || ctxconfig->minor != 0) |
593 | { |
594 | setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); |
595 | setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); |
596 | } |
597 | |
598 | if (mask) |
599 | setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); |
600 | |
601 | if (flags) |
602 | setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); |
603 | } |
604 | else |
605 | { |
606 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
607 | setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); |
608 | } |
609 | |
610 | if (_glfw.egl.KHR_context_flush_control) |
611 | { |
612 | if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) |
613 | { |
614 | setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, |
615 | EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); |
616 | } |
617 | else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) |
618 | { |
619 | setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, |
620 | EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); |
621 | } |
622 | } |
623 | |
624 | setAttrib(EGL_NONE, EGL_NONE); |
625 | |
626 | window->context.egl.handle = eglCreateContext(_glfw.egl.display, |
627 | config, share, attribs); |
628 | |
629 | if (window->context.egl.handle == EGL_NO_CONTEXT) |
630 | { |
631 | _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
632 | "EGL: Failed to create context: %s" , |
633 | getEGLErrorString(eglGetError())); |
634 | return GLFW_FALSE; |
635 | } |
636 | |
637 | // Set up attributes for surface creation |
638 | index = 0; |
639 | |
640 | if (fbconfig->sRGB) |
641 | { |
642 | if (_glfw.egl.KHR_gl_colorspace) |
643 | setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); |
644 | } |
645 | |
646 | if (!fbconfig->doublebuffer) |
647 | setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); |
648 | |
649 | setAttrib(EGL_NONE, EGL_NONE); |
650 | |
651 | native = _glfw.platform.getEGLNativeWindow(window); |
652 | // HACK: ANGLE does not implement eglCreatePlatformWindowSurfaceEXT |
653 | // despite reporting EGL_EXT_platform_base |
654 | if (_glfw.egl.platform && _glfw.egl.platform != EGL_PLATFORM_ANGLE_ANGLE) |
655 | { |
656 | window->context.egl.surface = |
657 | eglCreatePlatformWindowSurfaceEXT(_glfw.egl.display, config, native, attribs); |
658 | } |
659 | else |
660 | { |
661 | window->context.egl.surface = |
662 | eglCreateWindowSurface(_glfw.egl.display, config, native, attribs); |
663 | } |
664 | |
665 | if (window->context.egl.surface == EGL_NO_SURFACE) |
666 | { |
667 | _glfwInputError(GLFW_PLATFORM_ERROR, |
668 | "EGL: Failed to create window surface: %s" , |
669 | getEGLErrorString(eglGetError())); |
670 | return GLFW_FALSE; |
671 | } |
672 | |
673 | window->context.egl.config = config; |
674 | |
675 | // Load the appropriate client library |
676 | if (!_glfw.egl.KHR_get_all_proc_addresses) |
677 | { |
678 | int i; |
679 | const char** sonames; |
680 | const char* es1sonames[] = |
681 | { |
682 | #if defined(_GLFW_GLESV1_LIBRARY) |
683 | _GLFW_GLESV1_LIBRARY, |
684 | #elif defined(_GLFW_WIN32) |
685 | "GLESv1_CM.dll" , |
686 | "libGLES_CM.dll" , |
687 | #elif defined(_GLFW_COCOA) |
688 | "libGLESv1_CM.dylib" , |
689 | #else |
690 | "libGLESv1_CM.so.1" , |
691 | "libGLES_CM.so.1" , |
692 | #endif |
693 | NULL |
694 | }; |
695 | const char* es2sonames[] = |
696 | { |
697 | #if defined(_GLFW_GLESV2_LIBRARY) |
698 | _GLFW_GLESV2_LIBRARY, |
699 | #elif defined(_GLFW_WIN32) |
700 | "GLESv2.dll" , |
701 | "libGLESv2.dll" , |
702 | #elif defined(_GLFW_COCOA) |
703 | "libGLESv2.dylib" , |
704 | #elif defined(__CYGWIN__) |
705 | "libGLESv2-2.so" , |
706 | #else |
707 | "libGLESv2.so.2" , |
708 | #endif |
709 | NULL |
710 | }; |
711 | const char* glsonames[] = |
712 | { |
713 | #if defined(_GLFW_OPENGL_LIBRARY) |
714 | _GLFW_OPENGL_LIBRARY, |
715 | #elif defined(_GLFW_WIN32) |
716 | #elif defined(_GLFW_COCOA) |
717 | #else |
718 | "libGL.so.1" , |
719 | #endif |
720 | NULL |
721 | }; |
722 | |
723 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
724 | { |
725 | if (ctxconfig->major == 1) |
726 | sonames = es1sonames; |
727 | else |
728 | sonames = es2sonames; |
729 | } |
730 | else |
731 | sonames = glsonames; |
732 | |
733 | for (i = 0; sonames[i]; i++) |
734 | { |
735 | // HACK: Match presence of lib prefix to increase chance of finding |
736 | // a matching pair in the jungle that is Win32 EGL/GLES |
737 | if (_glfw.egl.prefix != (strncmp(sonames[i], "lib" , 3) == 0)) |
738 | continue; |
739 | |
740 | window->context.egl.client = _glfwPlatformLoadModule(sonames[i]); |
741 | if (window->context.egl.client) |
742 | break; |
743 | } |
744 | |
745 | if (!window->context.egl.client) |
746 | { |
747 | _glfwInputError(GLFW_API_UNAVAILABLE, |
748 | "EGL: Failed to load client library" ); |
749 | return GLFW_FALSE; |
750 | } |
751 | } |
752 | |
753 | window->context.makeCurrent = makeContextCurrentEGL; |
754 | window->context.swapBuffers = swapBuffersEGL; |
755 | window->context.swapInterval = swapIntervalEGL; |
756 | window->context.extensionSupported = extensionSupportedEGL; |
757 | window->context.getProcAddress = getProcAddressEGL; |
758 | window->context.destroy = destroyContextEGL; |
759 | |
760 | return GLFW_TRUE; |
761 | } |
762 | |
763 | #undef setAttrib |
764 | |
765 | // Returns the Visual and depth of the chosen EGLConfig |
766 | // |
767 | #if defined(_GLFW_X11) |
768 | GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, |
769 | const _GLFWctxconfig* ctxconfig, |
770 | const _GLFWfbconfig* fbconfig, |
771 | Visual** visual, int* depth) |
772 | { |
773 | XVisualInfo* result; |
774 | XVisualInfo desired; |
775 | EGLConfig native; |
776 | EGLint visualID = 0, count = 0; |
777 | const long vimask = VisualScreenMask | VisualIDMask; |
778 | |
779 | if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) |
780 | { |
781 | _glfwInputError(GLFW_FORMAT_UNAVAILABLE, |
782 | "EGL: Failed to find a suitable EGLConfig" ); |
783 | return GLFW_FALSE; |
784 | } |
785 | |
786 | eglGetConfigAttrib(_glfw.egl.display, native, |
787 | EGL_NATIVE_VISUAL_ID, &visualID); |
788 | |
789 | desired.screen = _glfw.x11.screen; |
790 | desired.visualid = visualID; |
791 | |
792 | result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count); |
793 | if (!result) |
794 | { |
795 | _glfwInputError(GLFW_PLATFORM_ERROR, |
796 | "EGL: Failed to retrieve Visual for EGLConfig" ); |
797 | return GLFW_FALSE; |
798 | } |
799 | |
800 | *visual = result->visual; |
801 | *depth = result->depth; |
802 | |
803 | XFree(result); |
804 | return GLFW_TRUE; |
805 | } |
806 | #endif // _GLFW_X11 |
807 | |
808 | |
809 | ////////////////////////////////////////////////////////////////////////// |
810 | ////// GLFW native API ////// |
811 | ////////////////////////////////////////////////////////////////////////// |
812 | |
813 | GLFWAPI EGLDisplay glfwGetEGLDisplay(void) |
814 | { |
815 | _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); |
816 | return _glfw.egl.display; |
817 | } |
818 | |
819 | GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) |
820 | { |
821 | _GLFWwindow* window = (_GLFWwindow*) handle; |
822 | _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); |
823 | |
824 | if (window->context.source != GLFW_EGL_CONTEXT_API) |
825 | { |
826 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
827 | return EGL_NO_CONTEXT; |
828 | } |
829 | |
830 | return window->context.egl.handle; |
831 | } |
832 | |
833 | GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) |
834 | { |
835 | _GLFWwindow* window = (_GLFWwindow*) handle; |
836 | _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); |
837 | |
838 | if (window->context.source != GLFW_EGL_CONTEXT_API) |
839 | { |
840 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
841 | return EGL_NO_SURFACE; |
842 | } |
843 | |
844 | return window->context.egl.surface; |
845 | } |
846 | |
847 | |