1 | //======================================================================== |
2 | // GLFW 3.2 EGL - www.glfw.org |
3 | //------------------------------------------------------------------------ |
4 | // Copyright (c) 2002-2006 Marcus Geelnard |
5 | // Copyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org> |
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 | |
28 | #include "internal.h" |
29 | |
30 | #include <stdio.h> |
31 | #include <string.h> |
32 | #include <stdlib.h> |
33 | #include <assert.h> |
34 | |
35 | |
36 | // Return a description of the specified EGL error |
37 | // |
38 | static const char* getEGLErrorString(EGLint error) |
39 | { |
40 | switch (error) |
41 | { |
42 | case EGL_SUCCESS: |
43 | return "Success" ; |
44 | case EGL_NOT_INITIALIZED: |
45 | return "EGL is not or could not be initialized" ; |
46 | case EGL_BAD_ACCESS: |
47 | return "EGL cannot access a requested resource" ; |
48 | case EGL_BAD_ALLOC: |
49 | return "EGL failed to allocate resources for the requested operation" ; |
50 | case EGL_BAD_ATTRIBUTE: |
51 | return "An unrecognized attribute or attribute value was passed in the attribute list" ; |
52 | case EGL_BAD_CONTEXT: |
53 | return "An EGLContext argument does not name a valid EGL rendering context" ; |
54 | case EGL_BAD_CONFIG: |
55 | return "An EGLConfig argument does not name a valid EGL frame buffer configuration" ; |
56 | case EGL_BAD_CURRENT_SURFACE: |
57 | return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid" ; |
58 | case EGL_BAD_DISPLAY: |
59 | return "An EGLDisplay argument does not name a valid EGL display connection" ; |
60 | case EGL_BAD_SURFACE: |
61 | return "An EGLSurface argument does not name a valid surface configured for GL rendering" ; |
62 | case EGL_BAD_MATCH: |
63 | return "Arguments are inconsistent" ; |
64 | case EGL_BAD_PARAMETER: |
65 | return "One or more argument values are invalid" ; |
66 | case EGL_BAD_NATIVE_PIXMAP: |
67 | return "A NativePixmapType argument does not refer to a valid native pixmap" ; |
68 | case EGL_BAD_NATIVE_WINDOW: |
69 | return "A NativeWindowType argument does not refer to a valid native window" ; |
70 | case EGL_CONTEXT_LOST: |
71 | return "The application must destroy all contexts and reinitialise" ; |
72 | default: |
73 | return "ERROR: UNKNOWN EGL ERROR" ; |
74 | } |
75 | } |
76 | |
77 | // Returns the specified attribute of the specified EGLConfig |
78 | // |
79 | static int getEGLConfigAttrib(EGLConfig config, int attrib) |
80 | { |
81 | int value; |
82 | eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); |
83 | return value; |
84 | } |
85 | |
86 | // Return the EGLConfig most closely matching the specified hints |
87 | // |
88 | static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, |
89 | const _GLFWfbconfig* desired, |
90 | EGLConfig* result) |
91 | { |
92 | EGLConfig* nativeConfigs; |
93 | _GLFWfbconfig* usableConfigs; |
94 | const _GLFWfbconfig* closest; |
95 | int i, nativeCount, usableCount; |
96 | |
97 | eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); |
98 | if (!nativeCount) |
99 | { |
100 | _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned" ); |
101 | return GLFW_FALSE; |
102 | } |
103 | |
104 | nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); |
105 | eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); |
106 | |
107 | usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); |
108 | usableCount = 0; |
109 | |
110 | for (i = 0; i < nativeCount; i++) |
111 | { |
112 | const EGLConfig n = nativeConfigs[i]; |
113 | _GLFWfbconfig* u = usableConfigs + usableCount; |
114 | |
115 | // Only consider RGB(A) EGLConfigs |
116 | if (!(getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) & EGL_RGB_BUFFER)) |
117 | continue; |
118 | |
119 | // Only consider window EGLConfigs |
120 | if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) |
121 | continue; |
122 | |
123 | #if defined(_GLFW_X11) |
124 | // Only consider EGLConfigs with associated Visuals |
125 | if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) |
126 | continue; |
127 | #endif // _GLFW_X11 |
128 | |
129 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
130 | { |
131 | if (ctxconfig->major == 1) |
132 | { |
133 | if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) |
134 | continue; |
135 | } |
136 | else |
137 | { |
138 | if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) |
139 | continue; |
140 | } |
141 | } |
142 | else if (ctxconfig->client == GLFW_OPENGL_API) |
143 | { |
144 | if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) |
145 | continue; |
146 | } |
147 | |
148 | u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); |
149 | u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); |
150 | u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); |
151 | |
152 | u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); |
153 | u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); |
154 | u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); |
155 | |
156 | u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); |
157 | u->doublebuffer = GLFW_TRUE; |
158 | |
159 | u->handle = (uintptr_t) n; |
160 | usableCount++; |
161 | } |
162 | |
163 | closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); |
164 | if (closest) |
165 | *result = (EGLConfig) closest->handle; |
166 | |
167 | free(nativeConfigs); |
168 | free(usableConfigs); |
169 | |
170 | return closest != NULL; |
171 | } |
172 | |
173 | static void makeContextCurrentEGL(_GLFWwindow* window) |
174 | { |
175 | if (window) |
176 | { |
177 | if (!eglMakeCurrent(_glfw.egl.display, |
178 | window->context.egl.surface, |
179 | window->context.egl.surface, |
180 | window->context.egl.handle)) |
181 | { |
182 | _glfwInputError(GLFW_PLATFORM_ERROR, |
183 | "EGL: Failed to make context current: %s" , |
184 | getEGLErrorString(eglGetError())); |
185 | return; |
186 | } |
187 | } |
188 | else |
189 | { |
190 | if (!eglMakeCurrent(_glfw.egl.display, |
191 | EGL_NO_SURFACE, |
192 | EGL_NO_SURFACE, |
193 | EGL_NO_CONTEXT)) |
194 | { |
195 | _glfwInputError(GLFW_PLATFORM_ERROR, |
196 | "EGL: Failed to clear current context: %s" , |
197 | getEGLErrorString(eglGetError())); |
198 | return; |
199 | } |
200 | } |
201 | |
202 | _glfwPlatformSetCurrentContext(window); |
203 | } |
204 | |
205 | static void swapBuffersEGL(_GLFWwindow* window) |
206 | { |
207 | if (window != _glfwPlatformGetCurrentContext()) |
208 | { |
209 | _glfwInputError(GLFW_PLATFORM_ERROR, |
210 | "EGL: The context must be current on the calling thread when swapping buffers" ); |
211 | return; |
212 | } |
213 | |
214 | eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); |
215 | } |
216 | |
217 | static void swapIntervalEGL(int interval) |
218 | { |
219 | eglSwapInterval(_glfw.egl.display, interval); |
220 | } |
221 | |
222 | static int extensionSupportedEGL(const char* extension) |
223 | { |
224 | const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); |
225 | if (extensions) |
226 | { |
227 | if (_glfwStringInExtensionString(extension, extensions)) |
228 | return GLFW_TRUE; |
229 | } |
230 | |
231 | return GLFW_FALSE; |
232 | } |
233 | |
234 | static GLFWglproc getProcAddressEGL(const char* procname) |
235 | { |
236 | _GLFWwindow* window = _glfwPlatformGetCurrentContext(); |
237 | |
238 | if (window->context.egl.client) |
239 | { |
240 | GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, |
241 | procname); |
242 | if (proc) |
243 | return proc; |
244 | } |
245 | |
246 | return eglGetProcAddress(procname); |
247 | } |
248 | |
249 | static void destroyContextEGL(_GLFWwindow* window) |
250 | { |
251 | #if defined(_GLFW_X11) |
252 | // NOTE: Do not unload libGL.so.1 while the X11 display is still open, |
253 | // as it will make XCloseDisplay segfault |
254 | if (window->context.client != GLFW_OPENGL_API) |
255 | #endif // _GLFW_X11 |
256 | { |
257 | if (window->context.egl.client) |
258 | { |
259 | _glfw_dlclose(window->context.egl.client); |
260 | window->context.egl.client = NULL; |
261 | } |
262 | } |
263 | |
264 | if (window->context.egl.surface) |
265 | { |
266 | eglDestroySurface(_glfw.egl.display, window->context.egl.surface); |
267 | window->context.egl.surface = EGL_NO_SURFACE; |
268 | } |
269 | |
270 | if (window->context.egl.handle) |
271 | { |
272 | eglDestroyContext(_glfw.egl.display, window->context.egl.handle); |
273 | window->context.egl.handle = EGL_NO_CONTEXT; |
274 | } |
275 | } |
276 | |
277 | |
278 | ////////////////////////////////////////////////////////////////////////// |
279 | ////// GLFW internal API ////// |
280 | ////////////////////////////////////////////////////////////////////////// |
281 | |
282 | // Initialize EGL |
283 | // |
284 | GLFWbool _glfwInitEGL(void) |
285 | { |
286 | int i; |
287 | const char* sonames[] = |
288 | { |
289 | #if defined(_GLFW_WIN32) |
290 | "libEGL.dll" , |
291 | "EGL.dll" , |
292 | #elif defined(_GLFW_COCOA) |
293 | "libEGL.dylib" , |
294 | #else |
295 | "libEGL.so.1" , |
296 | #endif |
297 | NULL |
298 | }; |
299 | |
300 | if (_glfw.egl.handle) |
301 | return GLFW_TRUE; |
302 | |
303 | for (i = 0; sonames[i]; i++) |
304 | { |
305 | _glfw.egl.handle = _glfw_dlopen(sonames[i]); |
306 | if (_glfw.egl.handle) |
307 | break; |
308 | } |
309 | |
310 | if (!_glfw.egl.handle) |
311 | { |
312 | _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found" ); |
313 | return GLFW_FALSE; |
314 | } |
315 | |
316 | _glfw.egl.prefix = (strncmp(sonames[i], "lib" , 3) == 0); |
317 | |
318 | _glfw.egl.GetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) |
319 | _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib" ); |
320 | _glfw.egl.GetConfigs = (PFNEGLGETCONFIGSPROC) |
321 | _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs" ); |
322 | _glfw.egl.GetDisplay = (PFNEGLGETDISPLAYPROC) |
323 | _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay" ); |
324 | _glfw.egl.GetError = (PFNEGLGETERRORPROC) |
325 | _glfw_dlsym(_glfw.egl.handle, "eglGetError" ); |
326 | _glfw.egl.Initialize = (PFNEGLINITIALIZEPROC) |
327 | _glfw_dlsym(_glfw.egl.handle, "eglInitialize" ); |
328 | _glfw.egl.Terminate = (PFNEGLTERMINATEPROC) |
329 | _glfw_dlsym(_glfw.egl.handle, "eglTerminate" ); |
330 | _glfw.egl.BindAPI = (PFNEGLBINDAPIPROC) |
331 | _glfw_dlsym(_glfw.egl.handle, "eglBindAPI" ); |
332 | _glfw.egl.CreateContext = (PFNEGLCREATECONTEXTPROC) |
333 | _glfw_dlsym(_glfw.egl.handle, "eglCreateContext" ); |
334 | _glfw.egl.DestroySurface = (PFNEGLDESTROYSURFACEPROC) |
335 | _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface" ); |
336 | _glfw.egl.DestroyContext = (PFNEGLDESTROYCONTEXTPROC) |
337 | _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext" ); |
338 | _glfw.egl.CreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) |
339 | _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface" ); |
340 | _glfw.egl.MakeCurrent = (PFNEGLMAKECURRENTPROC) |
341 | _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent" ); |
342 | _glfw.egl.SwapBuffers = (PFNEGLSWAPBUFFERSPROC) |
343 | _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers" ); |
344 | _glfw.egl.SwapInterval = (PFNEGLSWAPINTERVALPROC) |
345 | _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval" ); |
346 | _glfw.egl.QueryString = (PFNEGLQUERYSTRINGPROC) |
347 | _glfw_dlsym(_glfw.egl.handle, "eglQueryString" ); |
348 | _glfw.egl.GetProcAddress = (PFNEGLGETPROCADDRESSPROC) |
349 | _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress" ); |
350 | |
351 | if (!_glfw.egl.GetConfigAttrib || |
352 | !_glfw.egl.GetConfigs || |
353 | !_glfw.egl.GetDisplay || |
354 | !_glfw.egl.GetError || |
355 | !_glfw.egl.Initialize || |
356 | !_glfw.egl.Terminate || |
357 | !_glfw.egl.BindAPI || |
358 | !_glfw.egl.CreateContext || |
359 | !_glfw.egl.DestroySurface || |
360 | !_glfw.egl.DestroyContext || |
361 | !_glfw.egl.CreateWindowSurface || |
362 | !_glfw.egl.MakeCurrent || |
363 | !_glfw.egl.SwapBuffers || |
364 | !_glfw.egl.SwapInterval || |
365 | !_glfw.egl.QueryString || |
366 | !_glfw.egl.GetProcAddress) |
367 | { |
368 | _glfwInputError(GLFW_PLATFORM_ERROR, |
369 | "EGL: Failed to load required entry points" ); |
370 | |
371 | _glfwTerminateEGL(); |
372 | return GLFW_FALSE; |
373 | } |
374 | |
375 | _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); |
376 | if (_glfw.egl.display == EGL_NO_DISPLAY) |
377 | { |
378 | _glfwInputError(GLFW_API_UNAVAILABLE, |
379 | "EGL: Failed to get EGL display: %s" , |
380 | getEGLErrorString(eglGetError())); |
381 | |
382 | _glfwTerminateEGL(); |
383 | return GLFW_FALSE; |
384 | } |
385 | |
386 | if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) |
387 | { |
388 | _glfwInputError(GLFW_API_UNAVAILABLE, |
389 | "EGL: Failed to initialize EGL: %s" , |
390 | getEGLErrorString(eglGetError())); |
391 | |
392 | _glfwTerminateEGL(); |
393 | return GLFW_FALSE; |
394 | } |
395 | |
396 | _glfw.egl.KHR_create_context = |
397 | extensionSupportedEGL("EGL_KHR_create_context" ); |
398 | _glfw.egl.KHR_create_context_no_error = |
399 | extensionSupportedEGL("EGL_KHR_create_context_no_error" ); |
400 | _glfw.egl.KHR_gl_colorspace = |
401 | extensionSupportedEGL("EGL_KHR_gl_colorspace" ); |
402 | |
403 | return GLFW_TRUE; |
404 | } |
405 | |
406 | // Terminate EGL |
407 | // |
408 | void _glfwTerminateEGL(void) |
409 | { |
410 | if (_glfw.egl.display) |
411 | { |
412 | eglTerminate(_glfw.egl.display); |
413 | _glfw.egl.display = EGL_NO_DISPLAY; |
414 | } |
415 | |
416 | if (_glfw.egl.handle) |
417 | { |
418 | _glfw_dlclose(_glfw.egl.handle); |
419 | _glfw.egl.handle = NULL; |
420 | } |
421 | } |
422 | |
423 | #define setEGLattrib(attribName, attribValue) \ |
424 | { \ |
425 | attribs[index++] = attribName; \ |
426 | attribs[index++] = attribValue; \ |
427 | assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ |
428 | } |
429 | |
430 | // Create the OpenGL or OpenGL ES context |
431 | // |
432 | GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, |
433 | const _GLFWctxconfig* ctxconfig, |
434 | const _GLFWfbconfig* fbconfig) |
435 | { |
436 | EGLint attribs[40]; |
437 | EGLConfig config; |
438 | EGLContext share = NULL; |
439 | |
440 | if (!_glfw.egl.display) |
441 | { |
442 | _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available" ); |
443 | return GLFW_FALSE; |
444 | } |
445 | |
446 | if (ctxconfig->share) |
447 | share = ctxconfig->share->context.egl.handle; |
448 | |
449 | if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) |
450 | { |
451 | _glfwInputError(GLFW_FORMAT_UNAVAILABLE, |
452 | "EGL: Failed to find a suitable EGLConfig" ); |
453 | return GLFW_FALSE; |
454 | } |
455 | |
456 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
457 | { |
458 | if (!eglBindAPI(EGL_OPENGL_ES_API)) |
459 | { |
460 | _glfwInputError(GLFW_API_UNAVAILABLE, |
461 | "EGL: Failed to bind OpenGL ES: %s" , |
462 | getEGLErrorString(eglGetError())); |
463 | return GLFW_FALSE; |
464 | } |
465 | } |
466 | else |
467 | { |
468 | if (!eglBindAPI(EGL_OPENGL_API)) |
469 | { |
470 | _glfwInputError(GLFW_API_UNAVAILABLE, |
471 | "EGL: Failed to bind OpenGL: %s" , |
472 | getEGLErrorString(eglGetError())); |
473 | return GLFW_FALSE; |
474 | } |
475 | } |
476 | |
477 | if (_glfw.egl.KHR_create_context) |
478 | { |
479 | int index = 0, mask = 0, flags = 0; |
480 | |
481 | if (ctxconfig->client == GLFW_OPENGL_API) |
482 | { |
483 | if (ctxconfig->forward) |
484 | flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; |
485 | |
486 | if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) |
487 | mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; |
488 | else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) |
489 | mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; |
490 | |
491 | if (_glfw.egl.KHR_create_context_no_error) |
492 | { |
493 | if (ctxconfig->noerror) |
494 | flags |= EGL_CONTEXT_OPENGL_NO_ERROR_KHR; |
495 | } |
496 | } |
497 | |
498 | if (ctxconfig->debug) |
499 | flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; |
500 | |
501 | if (ctxconfig->robustness) |
502 | { |
503 | if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) |
504 | { |
505 | setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, |
506 | EGL_NO_RESET_NOTIFICATION_KHR); |
507 | } |
508 | else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) |
509 | { |
510 | setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, |
511 | EGL_LOSE_CONTEXT_ON_RESET_KHR); |
512 | } |
513 | |
514 | flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; |
515 | } |
516 | |
517 | if (ctxconfig->major != 1 || ctxconfig->minor != 0) |
518 | { |
519 | setEGLattrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); |
520 | setEGLattrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); |
521 | } |
522 | |
523 | if (mask) |
524 | setEGLattrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); |
525 | |
526 | if (flags) |
527 | setEGLattrib(EGL_CONTEXT_FLAGS_KHR, flags); |
528 | |
529 | setEGLattrib(EGL_NONE, EGL_NONE); |
530 | } |
531 | else |
532 | { |
533 | int index = 0; |
534 | |
535 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
536 | setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); |
537 | |
538 | setEGLattrib(EGL_NONE, EGL_NONE); |
539 | } |
540 | |
541 | // Context release behaviors (GL_KHR_context_flush_control) are not yet |
542 | // supported on EGL but are not a hard constraint, so ignore and continue |
543 | |
544 | window->context.egl.handle = eglCreateContext(_glfw.egl.display, |
545 | config, share, attribs); |
546 | |
547 | if (window->context.egl.handle == EGL_NO_CONTEXT) |
548 | { |
549 | _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
550 | "EGL: Failed to create context: %s" , |
551 | getEGLErrorString(eglGetError())); |
552 | return GLFW_FALSE; |
553 | } |
554 | |
555 | // Set up attributes for surface creation |
556 | { |
557 | int index = 0; |
558 | |
559 | if (fbconfig->sRGB) |
560 | { |
561 | if (_glfw.egl.KHR_gl_colorspace) |
562 | { |
563 | setEGLattrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); |
564 | } |
565 | } |
566 | |
567 | setEGLattrib(EGL_NONE, EGL_NONE); |
568 | } |
569 | |
570 | window->context.egl.surface = |
571 | eglCreateWindowSurface(_glfw.egl.display, |
572 | config, |
573 | _GLFW_EGL_NATIVE_WINDOW, |
574 | attribs); |
575 | if (window->context.egl.surface == EGL_NO_SURFACE) |
576 | { |
577 | _glfwInputError(GLFW_PLATFORM_ERROR, |
578 | "EGL: Failed to create window surface: %s" , |
579 | getEGLErrorString(eglGetError())); |
580 | return GLFW_FALSE; |
581 | } |
582 | |
583 | window->context.egl.config = config; |
584 | |
585 | // Load the appropriate client library |
586 | { |
587 | int i; |
588 | const char** sonames; |
589 | const char* es1sonames[] = |
590 | { |
591 | #if defined(_GLFW_WIN32) |
592 | "GLESv1_CM.dll" , |
593 | "libGLES_CM.dll" , |
594 | #elif defined(_GLFW_COCOA) |
595 | "libGLESv1_CM.dylib" , |
596 | #else |
597 | "libGLESv1_CM.so.1" , |
598 | "libGLES_CM.so.1" , |
599 | #endif |
600 | NULL |
601 | }; |
602 | const char* es2sonames[] = |
603 | { |
604 | #if defined(_GLFW_WIN32) |
605 | "GLESv2.dll" , |
606 | "libGLESv2.dll" , |
607 | #elif defined(_GLFW_COCOA) |
608 | "libGLESv2.dylib" , |
609 | #else |
610 | "libGLESv2.so.2" , |
611 | #endif |
612 | NULL |
613 | }; |
614 | const char* glsonames[] = |
615 | { |
616 | #if defined(_GLFW_WIN32) |
617 | #elif defined(_GLFW_COCOA) |
618 | #else |
619 | "libGL.so.1" , |
620 | #endif |
621 | NULL |
622 | }; |
623 | |
624 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
625 | { |
626 | if (ctxconfig->major == 1) |
627 | sonames = es1sonames; |
628 | else |
629 | sonames = es2sonames; |
630 | } |
631 | else |
632 | sonames = glsonames; |
633 | |
634 | for (i = 0; sonames[i]; i++) |
635 | { |
636 | // HACK: Match presence of lib prefix to increase chance of finding |
637 | // a matching pair in the jungle that is Win32 EGL/GLES |
638 | if (_glfw.egl.prefix != (strncmp(sonames[i], "lib" , 3) == 0)) |
639 | continue; |
640 | |
641 | window->context.egl.client = _glfw_dlopen(sonames[i]); |
642 | if (window->context.egl.client) |
643 | break; |
644 | } |
645 | |
646 | if (!window->context.egl.client) |
647 | { |
648 | _glfwInputError(GLFW_API_UNAVAILABLE, |
649 | "EGL: Failed to load client library" ); |
650 | return GLFW_FALSE; |
651 | } |
652 | } |
653 | |
654 | window->context.makeCurrent = makeContextCurrentEGL; |
655 | window->context.swapBuffers = swapBuffersEGL; |
656 | window->context.swapInterval = swapIntervalEGL; |
657 | window->context.extensionSupported = extensionSupportedEGL; |
658 | window->context.getProcAddress = getProcAddressEGL; |
659 | window->context.destroy = destroyContextEGL; |
660 | |
661 | return GLFW_TRUE; |
662 | } |
663 | |
664 | #undef setEGLattrib |
665 | |
666 | // Returns the Visual and depth of the chosen EGLConfig |
667 | // |
668 | #if defined(_GLFW_X11) |
669 | GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, |
670 | const _GLFWfbconfig* fbconfig, |
671 | Visual** visual, int* depth) |
672 | { |
673 | XVisualInfo* result; |
674 | XVisualInfo desired; |
675 | EGLConfig native; |
676 | EGLint visualID = 0, count = 0; |
677 | const long vimask = VisualScreenMask | VisualIDMask; |
678 | |
679 | if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) |
680 | { |
681 | _glfwInputError(GLFW_FORMAT_UNAVAILABLE, |
682 | "EGL: Failed to find a suitable EGLConfig" ); |
683 | return GLFW_FALSE; |
684 | } |
685 | |
686 | eglGetConfigAttrib(_glfw.egl.display, native, |
687 | EGL_NATIVE_VISUAL_ID, &visualID); |
688 | |
689 | desired.screen = _glfw.x11.screen; |
690 | desired.visualid = visualID; |
691 | |
692 | result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count); |
693 | if (!result) |
694 | { |
695 | _glfwInputError(GLFW_PLATFORM_ERROR, |
696 | "EGL: Failed to retrieve Visual for EGLConfig" ); |
697 | return GLFW_FALSE; |
698 | } |
699 | |
700 | *visual = result->visual; |
701 | *depth = result->depth; |
702 | |
703 | XFree(result); |
704 | return GLFW_TRUE; |
705 | } |
706 | #endif // _GLFW_X11 |
707 | |
708 | |
709 | ////////////////////////////////////////////////////////////////////////// |
710 | ////// GLFW native API ////// |
711 | ////////////////////////////////////////////////////////////////////////// |
712 | |
713 | GLFWAPI EGLDisplay glfwGetEGLDisplay(void) |
714 | { |
715 | _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); |
716 | return _glfw.egl.display; |
717 | } |
718 | |
719 | GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) |
720 | { |
721 | _GLFWwindow* window = (_GLFWwindow*) handle; |
722 | _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); |
723 | |
724 | if (window->context.client == GLFW_NO_API) |
725 | { |
726 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
727 | return EGL_NO_CONTEXT; |
728 | } |
729 | |
730 | return window->context.egl.handle; |
731 | } |
732 | |
733 | GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) |
734 | { |
735 | _GLFWwindow* window = (_GLFWwindow*) handle; |
736 | _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); |
737 | |
738 | if (window->context.client == GLFW_NO_API) |
739 | { |
740 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
741 | return EGL_NO_SURFACE; |
742 | } |
743 | |
744 | return window->context.egl.surface; |
745 | } |
746 | |
747 | |