1 #![cfg(any(
2     target_os = "windows",
3     target_os = "linux",
4     target_os = "android",
5     target_os = "dragonfly",
6     target_os = "freebsd",
7     target_os = "netbsd",
8     target_os = "openbsd",
9 ))]
10 #![allow(unused_variables)]
11 
12 #[cfg(not(target_os = "android"))]
13 mod egl {
14     use super::ffi;
15     use crate::api::dlloader::{SymTrait, SymWrapper};
16     use libloading;
17     use std::sync::{Arc, Mutex};
18 
19     #[cfg(unix)]
20     use libloading::os::unix as libloading_os;
21     #[cfg(windows)]
22     use libloading::os::windows as libloading_os;
23 
24     #[derive(Clone)]
25     pub struct Egl(pub SymWrapper<ffi::egl::Egl>);
26 
27     /// Because `*const raw::c_void` doesn't implement `Sync`.
28     unsafe impl Sync for Egl {}
29 
30     type EglGetProcAddressType = libloading_os::Symbol<
31         unsafe extern "C" fn(*const std::os::raw::c_void) -> *const std::os::raw::c_void,
32     >;
33 
34     lazy_static! {
35         static ref EGL_GET_PROC_ADDRESS: Arc<Mutex<Option<EglGetProcAddressType>>> =
36             Arc::new(Mutex::new(None));
37     }
38 
39     impl SymTrait for ffi::egl::Egl {
load_with(lib: &libloading::Library) -> Self40         fn load_with(lib: &libloading::Library) -> Self {
41             let f = move |s: &'static str| -> *const std::os::raw::c_void {
42                 // Check if the symbol is available in the library directly. If
43                 // it is, just return it.
44                 match unsafe {
45                     lib.get(std::ffi::CString::new(s.as_bytes()).unwrap().as_bytes_with_nul())
46                 } {
47                     Ok(sym) => return *sym,
48                     Err(_) => (),
49                 };
50 
51                 let mut egl_get_proc_address = (*EGL_GET_PROC_ADDRESS).lock().unwrap();
52                 if egl_get_proc_address.is_none() {
53                     unsafe {
54                         let sym: libloading::Symbol<
55                             unsafe extern "C" fn(
56                                 *const std::os::raw::c_void,
57                             )
58                                 -> *const std::os::raw::c_void,
59                         > = lib.get(b"eglGetProcAddress").unwrap();
60                         *egl_get_proc_address = Some(sym.into_raw());
61                     }
62                 }
63 
64                 // The symbol was not available in the library, so ask
65                 // eglGetProcAddress for it. Note that eglGetProcAddress was
66                 // only able to look up extension functions prior to EGL 1.5,
67                 // hence this two-part dance.
68                 unsafe {
69                     (egl_get_proc_address.as_ref().unwrap())(
70                         std::ffi::CString::new(s.as_bytes()).unwrap().as_bytes_with_nul().as_ptr()
71                             as *const std::os::raw::c_void,
72                     )
73                 }
74             };
75 
76             Self::load_with(f)
77         }
78     }
79 
80     impl Egl {
new() -> Result<Self, ()>81         pub fn new() -> Result<Self, ()> {
82             #[cfg(target_os = "windows")]
83             let paths = vec!["libEGL.dll", "atioglxx.dll"];
84 
85             #[cfg(not(target_os = "windows"))]
86             let paths = vec!["libEGL.so.1", "libEGL.so"];
87 
88             SymWrapper::new(paths).map(|i| Egl(i))
89         }
90     }
91 }
92 
93 #[cfg(target_os = "android")]
94 mod egl {
95     use super::ffi;
96 
97     #[derive(Clone)]
98     pub struct Egl(pub ffi::egl::Egl);
99 
100     impl Egl {
new() -> Result<Self, ()>101         pub fn new() -> Result<Self, ()> {
102             Ok(Egl(ffi::egl::Egl))
103         }
104     }
105 }
106 
107 mod make_current_guard;
108 
109 pub use self::egl::Egl;
110 use self::make_current_guard::MakeCurrentGuard;
111 use crate::{
112     Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat,
113     PixelFormatRequirements, Rect, ReleaseBehavior, Robustness,
114 };
115 
116 use glutin_egl_sys as ffi;
117 use parking_lot::Mutex;
118 #[cfg(any(
119     target_os = "android",
120     target_os = "windows",
121     target_os = "linux",
122     target_os = "dragonfly",
123     target_os = "freebsd",
124     target_os = "netbsd",
125     target_os = "openbsd",
126 ))]
127 use winit::dpi;
128 
129 use std::ffi::{CStr, CString};
130 use std::ops::{Deref, DerefMut};
131 use std::os::raw;
132 
133 impl Deref for Egl {
134     type Target = ffi::egl::Egl;
135 
deref(&self) -> &Self::Target136     fn deref(&self) -> &Self::Target {
137         &self.0
138     }
139 }
140 
141 impl DerefMut for Egl {
deref_mut(&mut self) -> &mut ffi::egl::Egl142     fn deref_mut(&mut self) -> &mut ffi::egl::Egl {
143         &mut self.0
144     }
145 }
146 
147 lazy_static! {
148     pub static ref EGL: Option<Egl> = Egl::new().ok();
149 }
150 
151 /// Specifies the type of display passed as `native_display`.
152 #[derive(Debug)]
153 #[allow(dead_code)]
154 pub enum NativeDisplay {
155     /// `None` means `EGL_DEFAULT_DISPLAY`.
156     X11(Option<ffi::EGLNativeDisplayType>),
157     /// `None` means `EGL_DEFAULT_DISPLAY`.
158     Gbm(Option<ffi::EGLNativeDisplayType>),
159     /// `None` means `EGL_DEFAULT_DISPLAY`.
160     Wayland(Option<ffi::EGLNativeDisplayType>),
161     /// `EGL_DEFAULT_DISPLAY` is mandatory for Android.
162     Android,
163     // TODO: should be `EGLDeviceEXT`
164     Device(ffi::EGLNativeDisplayType),
165     /// Don't specify any display type. Useful on windows. `None` means
166     /// `EGL_DEFAULT_DISPLAY`.
167     Other(Option<ffi::EGLNativeDisplayType>),
168 }
169 
170 #[derive(Debug)]
171 pub struct Context {
172     display: ffi::egl::types::EGLDisplay,
173     context: ffi::egl::types::EGLContext,
174     surface: Option<Mutex<ffi::egl::types::EGLSurface>>,
175     api: Api,
176     pixel_format: PixelFormat,
177     #[cfg(target_os = "android")]
178     config_id: ffi::egl::types::EGLConfig,
179 }
180 
181 #[cfg(target_os = "android")]
182 #[inline]
get_native_display(native_display: &NativeDisplay) -> *const raw::c_void183 fn get_native_display(native_display: &NativeDisplay) -> *const raw::c_void {
184     let egl = EGL.as_ref().unwrap();
185     unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }
186 }
187 
get_egl_version( display: ffi::egl::types::EGLDisplay, ) -> Result<(ffi::egl::types::EGLint, ffi::egl::types::EGLint), CreationError>188 fn get_egl_version(
189     display: ffi::egl::types::EGLDisplay,
190 ) -> Result<(ffi::egl::types::EGLint, ffi::egl::types::EGLint), CreationError> {
191     unsafe {
192         let egl = EGL.as_ref().unwrap();
193         let mut major: ffi::egl::types::EGLint = std::mem::zeroed();
194         let mut minor: ffi::egl::types::EGLint = std::mem::zeroed();
195 
196         if egl.Initialize(display, &mut major, &mut minor) == 0 {
197             return Err(CreationError::OsError("eglInitialize failed".to_string()));
198         }
199 
200         Ok((major, minor))
201     }
202 }
203 
bind_and_get_api<'a>( opengl: &'a GlAttributes<&'a Context>, egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint), ) -> Result<(Option<(u8, u8)>, Api), CreationError>204 unsafe fn bind_and_get_api<'a>(
205     opengl: &'a GlAttributes<&'a Context>,
206     egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint),
207 ) -> Result<(Option<(u8, u8)>, Api), CreationError> {
208     let egl = EGL.as_ref().unwrap();
209     match opengl.version {
210         GlRequest::Latest => {
211             if egl_version >= (1, 4) {
212                 if egl.BindAPI(ffi::egl::OPENGL_API) != 0 {
213                     Ok((None, Api::OpenGl))
214                 } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
215                     Ok((None, Api::OpenGlEs))
216                 } else {
217                     Err(CreationError::OpenGlVersionNotSupported)
218                 }
219             } else {
220                 Ok((None, Api::OpenGlEs))
221             }
222         }
223         GlRequest::Specific(Api::OpenGlEs, version) => {
224             if egl_version >= (1, 2) {
225                 if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
226                     return Err(CreationError::OpenGlVersionNotSupported);
227                 }
228             }
229             Ok((Some(version), Api::OpenGlEs))
230         }
231         GlRequest::Specific(Api::OpenGl, version) => {
232             if egl_version < (1, 4) {
233                 return Err(CreationError::OpenGlVersionNotSupported);
234             }
235             if egl.BindAPI(ffi::egl::OPENGL_API) == 0 {
236                 return Err(CreationError::OpenGlVersionNotSupported);
237             }
238             Ok((Some(version), Api::OpenGl))
239         }
240         GlRequest::Specific(_, _) => Err(CreationError::OpenGlVersionNotSupported),
241         GlRequest::GlThenGles { opengles_version, opengl_version } => {
242             if egl_version >= (1, 4) {
243                 if egl.BindAPI(ffi::egl::OPENGL_API) != 0 {
244                     Ok((Some(opengl_version), Api::OpenGl))
245                 } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
246                     Ok((Some(opengles_version), Api::OpenGlEs))
247                 } else {
248                     Err(CreationError::OpenGlVersionNotSupported)
249                 }
250             } else {
251                 Ok((Some(opengles_version), Api::OpenGlEs))
252             }
253         }
254     }
255 }
256 
257 #[cfg(not(target_os = "android"))]
get_native_display(native_display: &NativeDisplay) -> *const raw::c_void258 fn get_native_display(native_display: &NativeDisplay) -> *const raw::c_void {
259     let egl = EGL.as_ref().unwrap();
260     // the first step is to query the list of extensions without any display, if
261     // supported
262     let dp_extensions = unsafe {
263         let p = egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
264 
265         // this possibility is available only with EGL 1.5 or
266         // EGL_EXT_platform_base, otherwise `eglQueryString` returns an
267         // error
268         if p.is_null() {
269             vec![]
270         } else {
271             let p = CStr::from_ptr(p);
272             let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
273             list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
274         }
275     };
276 
277     let has_dp_extension = |e: &str| dp_extensions.iter().find(|s| s == &e).is_some();
278 
279     match *native_display {
280         // Note: Some EGL implementations are missing the
281         // `eglGetPlatformDisplay(EXT)` symbol       despite reporting
282         // `EGL_EXT_platform_base`. I'm pretty sure this is a bug.
283         //       Therefore we detect whether the symbol is loaded in addition to
284         // checking for       extensions.
285         NativeDisplay::X11(display)
286             if has_dp_extension("EGL_KHR_platform_x11") && egl.GetPlatformDisplay.is_loaded() =>
287         {
288             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
289             // TODO: `PLATFORM_X11_SCREEN_KHR`
290             unsafe {
291                 egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, d as *mut _, std::ptr::null())
292             }
293         }
294 
295         NativeDisplay::X11(display)
296             if has_dp_extension("EGL_EXT_platform_x11")
297                 && egl.GetPlatformDisplayEXT.is_loaded() =>
298         {
299             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
300             // TODO: `PLATFORM_X11_SCREEN_EXT`
301             unsafe {
302                 egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, d as *mut _, std::ptr::null())
303             }
304         }
305 
306         NativeDisplay::Gbm(display)
307             if has_dp_extension("EGL_KHR_platform_gbm") && egl.GetPlatformDisplay.is_loaded() =>
308         {
309             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
310             unsafe {
311                 egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, std::ptr::null())
312             }
313         }
314 
315         NativeDisplay::Gbm(display)
316             if has_dp_extension("EGL_MESA_platform_gbm")
317                 && egl.GetPlatformDisplayEXT.is_loaded() =>
318         {
319             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
320             unsafe {
321                 egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, std::ptr::null())
322             }
323         }
324 
325         NativeDisplay::Wayland(display)
326             if has_dp_extension("EGL_KHR_platform_wayland")
327                 && egl.GetPlatformDisplay.is_loaded() =>
328         {
329             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
330             unsafe {
331                 egl.GetPlatformDisplay(
332                     ffi::egl::PLATFORM_WAYLAND_KHR,
333                     d as *mut _,
334                     std::ptr::null(),
335                 )
336             }
337         }
338 
339         NativeDisplay::Wayland(display)
340             if has_dp_extension("EGL_EXT_platform_wayland")
341                 && egl.GetPlatformDisplayEXT.is_loaded() =>
342         {
343             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
344             unsafe {
345                 egl.GetPlatformDisplayEXT(
346                     ffi::egl::PLATFORM_WAYLAND_EXT,
347                     d as *mut _,
348                     std::ptr::null(),
349                 )
350             }
351         }
352 
353         // TODO: This will never be reached right now, as the android egl
354         // bindings use the static generator, so can't rely on
355         // GetPlatformDisplay(EXT).
356         NativeDisplay::Android
357             if has_dp_extension("EGL_KHR_platform_android")
358                 && egl.GetPlatformDisplay.is_loaded() =>
359         unsafe {
360             egl.GetPlatformDisplay(
361                 ffi::egl::PLATFORM_ANDROID_KHR,
362                 ffi::egl::DEFAULT_DISPLAY as *mut _,
363                 std::ptr::null(),
364             )
365         }
366 
367         NativeDisplay::Device(display)
368             if has_dp_extension("EGL_EXT_platform_device")
369                 && egl.GetPlatformDisplay.is_loaded() =>
370         unsafe {
371             egl.GetPlatformDisplay(
372                 ffi::egl::PLATFORM_DEVICE_EXT,
373                 display as *mut _,
374                 std::ptr::null(),
375             )
376         }
377 
378         NativeDisplay::X11(Some(display))
379         | NativeDisplay::Gbm(Some(display))
380         | NativeDisplay::Wayland(Some(display))
381         | NativeDisplay::Device(display)
382         | NativeDisplay::Other(Some(display)) => unsafe { egl.GetDisplay(display as *mut _) },
383 
384         NativeDisplay::X11(None)
385         | NativeDisplay::Gbm(None)
386         | NativeDisplay::Wayland(None)
387         | NativeDisplay::Android
388         | NativeDisplay::Other(None) => unsafe {
389             egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _)
390         },
391     }
392 }
393 
394 #[allow(dead_code)] // Not all platforms use all
395 #[derive(Copy, Clone, PartialEq, Debug)]
396 pub enum SurfaceType {
397     PBuffer,
398     Window,
399     Surfaceless,
400 }
401 
402 impl Context {
403     /// Start building an EGL context.
404     ///
405     /// This function initializes some things and chooses the pixel format.
406     ///
407     /// To finish the process, you must call `.finish(window)` on the
408     /// `ContextPrototype`.
new<'a, F>( pf_reqs: &PixelFormatRequirements, opengl: &'a GlAttributes<&'a Context>, native_display: NativeDisplay, surface_type: SurfaceType, config_selector: F, ) -> Result<ContextPrototype<'a>, CreationError> where F: FnMut( Vec<ffi::egl::types::EGLConfig>, ffi::egl::types::EGLDisplay, ) -> Result<ffi::egl::types::EGLConfig, ()>,409     pub fn new<'a, F>(
410         pf_reqs: &PixelFormatRequirements,
411         opengl: &'a GlAttributes<&'a Context>,
412         native_display: NativeDisplay,
413         surface_type: SurfaceType,
414         config_selector: F,
415     ) -> Result<ContextPrototype<'a>, CreationError>
416     where
417         F: FnMut(
418             Vec<ffi::egl::types::EGLConfig>,
419             ffi::egl::types::EGLDisplay,
420         ) -> Result<ffi::egl::types::EGLConfig, ()>,
421     {
422         let egl = EGL.as_ref().unwrap();
423         // calling `eglGetDisplay` or equivalent
424         let display = get_native_display(&native_display);
425 
426         if display.is_null() {
427             return Err(CreationError::OsError("Could not create EGL display object".to_string()));
428         }
429 
430         let egl_version = get_egl_version(display)?;
431 
432         // the list of extensions supported by the client once initialized is
433         // different from the list of extensions obtained earlier
434         let extensions = if egl_version >= (1, 2) {
435             let p =
436                 unsafe { CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)) };
437             let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
438             list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
439         } else {
440             vec![]
441         };
442 
443         // binding the right API and choosing the version
444         let (version, api) = unsafe { bind_and_get_api(&opengl, egl_version)? };
445 
446         let (config_id, pixel_format) = unsafe {
447             choose_fbconfig(
448                 display,
449                 &egl_version,
450                 api,
451                 version,
452                 pf_reqs,
453                 surface_type,
454                 opengl,
455                 config_selector,
456             )?
457         };
458 
459         Ok(ContextPrototype {
460             opengl,
461             display,
462             egl_version,
463             extensions,
464             api,
465             version,
466             config_id,
467             pixel_format,
468         })
469     }
470 
check_make_current(&self, ret: Option<u32>) -> Result<(), ContextError>471     unsafe fn check_make_current(&self, ret: Option<u32>) -> Result<(), ContextError> {
472         let egl = EGL.as_ref().unwrap();
473         if ret == Some(0) {
474             match egl.GetError() as u32 {
475                 ffi::egl::CONTEXT_LOST => Err(ContextError::ContextLost),
476                 err => {
477                     panic!("make_current: eglMakeCurrent failed (eglGetError returned 0x{:x})", err)
478                 }
479             }
480         } else {
481             Ok(())
482         }
483     }
484 
make_current(&self) -> Result<(), ContextError>485     pub unsafe fn make_current(&self) -> Result<(), ContextError> {
486         let egl = EGL.as_ref().unwrap();
487         let surface = self.surface.as_ref().map(|s| *s.lock()).unwrap_or(ffi::egl::NO_SURFACE);
488         let ret = egl.MakeCurrent(self.display, surface, surface, self.context);
489 
490         self.check_make_current(Some(ret))
491     }
492 
make_not_current(&self) -> Result<(), ContextError>493     pub unsafe fn make_not_current(&self) -> Result<(), ContextError> {
494         let egl = EGL.as_ref().unwrap();
495 
496         let surface_eq = if let Some(surface) = self.surface.as_ref().map(|s| *s.lock()) {
497             egl.GetCurrentSurface(ffi::egl::DRAW as i32) == surface
498                 || egl.GetCurrentSurface(ffi::egl::READ as i32) == surface
499         } else {
500             false
501         };
502 
503         if surface_eq || egl.GetCurrentContext() == self.context {
504             let ret = egl.MakeCurrent(
505                 self.display,
506                 ffi::egl::NO_SURFACE,
507                 ffi::egl::NO_SURFACE,
508                 ffi::egl::NO_CONTEXT,
509             );
510 
511             self.check_make_current(Some(ret))
512         } else {
513             self.check_make_current(None)
514         }
515     }
516 
517     #[inline]
is_current(&self) -> bool518     pub fn is_current(&self) -> bool {
519         let egl = EGL.as_ref().unwrap();
520         unsafe { egl.GetCurrentContext() == self.context }
521     }
522 
523     #[inline]
get_api(&self) -> Api524     pub fn get_api(&self) -> Api {
525         self.api
526     }
527 
528     #[inline]
raw_handle(&self) -> ffi::egl::types::EGLContext529     pub unsafe fn raw_handle(&self) -> ffi::egl::types::EGLContext {
530         self.context
531     }
532 
533     #[inline]
get_egl_display(&self) -> ffi::egl::types::EGLDisplay534     pub unsafe fn get_egl_display(&self) -> ffi::egl::types::EGLDisplay {
535         self.display
536     }
537 
538     // Handle Android Life Cycle.
539     // Android has started the activity or sent it to foreground.
540     // Create a new surface and attach it to the recreated ANativeWindow.
541     // Restore the EGLContext.
542     #[cfg(target_os = "android")]
on_surface_created(&self, nwin: ffi::EGLNativeWindowType)543     pub unsafe fn on_surface_created(&self, nwin: ffi::EGLNativeWindowType) {
544         let egl = EGL.as_ref().unwrap();
545         let mut surface = self.surface.as_ref().unwrap().lock();
546         if *surface != ffi::egl::NO_SURFACE {
547             return;
548         }
549         *surface = egl.CreateWindowSurface(self.display, self.config_id, nwin, std::ptr::null());
550         if surface.is_null() {
551             panic!("on_surface_created: eglCreateWindowSurface failed with 0x{:x}", egl.GetError())
552         }
553         let ret = egl.MakeCurrent(self.display, *surface, *surface, self.context);
554         if ret == 0 {
555             panic!("on_surface_created: eglMakeCurrent failed with 0x{:x}", egl.GetError())
556         }
557     }
558 
559     // Handle Android Life Cycle.
560     // Android has stopped the activity or sent it to background.
561     // Release the surface attached to the destroyed ANativeWindow.
562     // The EGLContext is not destroyed so it can be restored later.
563     #[cfg(target_os = "android")]
on_surface_destroyed(&self)564     pub unsafe fn on_surface_destroyed(&self) {
565         let egl = EGL.as_ref().unwrap();
566         let mut surface = self.surface.as_ref().unwrap().lock();
567         if *surface == ffi::egl::NO_SURFACE {
568             return;
569         }
570         let ret = egl.MakeCurrent(
571             self.display,
572             ffi::egl::NO_SURFACE,
573             ffi::egl::NO_SURFACE,
574             ffi::egl::NO_CONTEXT,
575         );
576         if ret == 0 {
577             panic!("on_surface_destroyed: eglMakeCurrent failed with 0x{:x}", egl.GetError())
578         }
579 
580         egl.DestroySurface(self.display, *surface);
581         *surface = ffi::egl::NO_SURFACE;
582     }
583 
584     #[inline]
get_proc_address(&self, addr: &str) -> *const core::ffi::c_void585     pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void {
586         let egl = EGL.as_ref().unwrap();
587         let addr = CString::new(addr.as_bytes()).unwrap();
588         let addr = addr.as_ptr();
589         unsafe { egl.GetProcAddress(addr) as *const _ }
590     }
591 
592     #[inline]
swap_buffers(&self) -> Result<(), ContextError>593     pub fn swap_buffers(&self) -> Result<(), ContextError> {
594         let egl = EGL.as_ref().unwrap();
595         let surface = self.surface.as_ref().unwrap().lock();
596         if *surface == ffi::egl::NO_SURFACE {
597             return Err(ContextError::ContextLost);
598         }
599 
600         let ret = unsafe { egl.SwapBuffers(self.display, *surface) };
601 
602         if ret == 0 {
603             match unsafe { egl.GetError() } as u32 {
604                 ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
605                 err => {
606                     panic!("swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})", err)
607                 }
608             }
609         } else {
610             Ok(())
611         }
612     }
613 
614     #[inline]
swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError>615     pub fn swap_buffers_with_damage(&self, rects: &[Rect]) -> Result<(), ContextError> {
616         let egl = EGL.as_ref().unwrap();
617 
618         if !egl.SwapBuffersWithDamageKHR.is_loaded() {
619             return Err(ContextError::FunctionUnavailable);
620         }
621 
622         let surface = self.surface.as_ref().unwrap().lock();
623         if *surface == ffi::egl::NO_SURFACE {
624             return Err(ContextError::ContextLost);
625         }
626 
627         let mut ffirects: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(rects.len() * 4);
628 
629         for rect in rects {
630             ffirects.push(rect.x as ffi::egl::types::EGLint);
631             ffirects.push(rect.y as ffi::egl::types::EGLint);
632             ffirects.push(rect.width as ffi::egl::types::EGLint);
633             ffirects.push(rect.height as ffi::egl::types::EGLint);
634         }
635 
636         let ret = unsafe {
637             egl.SwapBuffersWithDamageKHR(
638                 self.display,
639                 *surface,
640                 ffirects.as_mut_ptr(),
641                 rects.len() as ffi::egl::types::EGLint,
642             )
643         };
644 
645         if ret == ffi::egl::FALSE {
646             match unsafe { egl.GetError() } as u32 {
647                 ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
648                 err => {
649                     panic!("swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})", err)
650                 }
651             }
652         } else {
653             Ok(())
654         }
655     }
656 
657     #[inline]
swap_buffers_with_damage_supported(&self) -> bool658     pub fn swap_buffers_with_damage_supported(&self) -> bool {
659         let egl = EGL.as_ref().unwrap();
660         egl.SwapBuffersWithDamageKHR.is_loaded()
661     }
662 
663     #[inline]
get_pixel_format(&self) -> PixelFormat664     pub fn get_pixel_format(&self) -> PixelFormat {
665         self.pixel_format.clone()
666     }
667 }
668 
669 unsafe impl Send for Context {}
670 unsafe impl Sync for Context {}
671 
672 impl Drop for Context {
drop(&mut self)673     fn drop(&mut self) {
674         unsafe {
675             // https://stackoverflow.com/questions/54402688/recreate-eglcreatewindowsurface-with-same-native-window
676             let egl = EGL.as_ref().unwrap();
677             let surface = self.surface.as_ref().map(|s| *s.lock()).unwrap_or(ffi::egl::NO_SURFACE);
678             // Ok, so we got to call `glFinish` before destroying the context
679             // to ensure it actually gets destroyed. This requires making the
680             // this context current.
681             let mut guard = MakeCurrentGuard::new(self.display, surface, surface, self.context)
682                 .map_err(|err| ContextError::OsError(err))
683                 .unwrap();
684 
685             guard.if_any_same_then_invalidate(surface, surface, self.context);
686 
687             let gl_finish_fn = self.get_proc_address("glFinish");
688             assert!(gl_finish_fn != std::ptr::null());
689             let gl_finish_fn = std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn);
690             gl_finish_fn();
691 
692             egl.DestroyContext(self.display, self.context);
693             self.context = ffi::egl::NO_CONTEXT;
694             egl.DestroySurface(self.display, surface);
695             if let Some(ref surface) = self.surface {
696                 let mut surface = surface.lock();
697                 *surface = ffi::egl::NO_SURFACE;
698             }
699 
700             // In a reasonable world, we could uncomment the line bellow.
701             //
702             // This is no such world. Lets talk about something.
703             //
704             // You see, every call to `get_native_display` returns the exact
705             // same display, just look at the docs:
706             //
707             // "Multiple calls made to eglGetDisplay with the same display_id
708             // will return the same EGLDisplay handle."
709             //
710             // My EGL implementation does not do any ref counting, nor do the
711             // EGL docs mention ref counting anywhere. In fact, the docs state
712             // that there will be *no effect*, which, in a way, implies no ref
713             // counting:
714             //
715             // "Initializing an already initialized EGL display connection has
716             // no effect besides returning the version numbers."
717             //
718             // So, if we terminate the display, other people who are using it
719             // won't be so happy.
720             //
721             // Well, how did I stumble on this issue you might ask...
722             //
723             // In this case, the "other people" was us, for it appears my EGL
724             // implementation does not follow the docs,  or maybe I'm misreading
725             // them. You see, according to the egl docs:
726             //
727             // "To release the current context without assigning a new one, set
728             // context to EGL_NO_CONTEXT and set draw and read to
729             // EGL_NO_SURFACE.  [...] ******This is the only case where an
730             // uninitialized display may be passed to eglMakeCurrent.******"
731             // (Emphasis mine).
732             //
733             // Well, my computer returns EGL_BAD_DISPLAY if the display passed
734             // to eglMakeCurrent is uninitialized, which allowed to me to spot
735             // this issue.
736             //
737             // I would have assumed that if EGL was going to provide us with
738             // the same EGLDisplay that they'd at least do
739             // some ref counting, but they don't.
740             //
741             // FIXME: Technically we are leaking resources, not much we can do.
742             // Yeah, we could have a global static that does ref counting
743             // ourselves, but what if some other library is using the display.
744             //
745             // On unix operating systems, we could preload a little lib that
746             // does ref counting on that level, but:
747             //      A) What about other platforms?
748             //      B) Do you *really* want all glutin programs to preload a
749             //      library?
750             //      C) Who the hell is going to maintain that?
751             //
752             // egl.Terminate(self.display);
753         }
754     }
755 }
756 
757 #[derive(Debug)]
758 pub struct ContextPrototype<'a> {
759     opengl: &'a GlAttributes<&'a Context>,
760     display: ffi::egl::types::EGLDisplay,
761     egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint),
762     extensions: Vec<String>,
763     api: Api,
764     version: Option<(u8, u8)>,
765     config_id: ffi::egl::types::EGLConfig,
766     pixel_format: PixelFormat,
767 }
768 
769 #[cfg(any(
770     target_os = "linux",
771     target_os = "dragonfly",
772     target_os = "freebsd",
773     target_os = "netbsd",
774     target_os = "openbsd",
775 ))]
776 #[cfg(feature = "x11")]
get_native_visual_id( display: ffi::egl::types::EGLDisplay, config_id: ffi::egl::types::EGLConfig, ) -> ffi::egl::types::EGLint777 pub fn get_native_visual_id(
778     display: ffi::egl::types::EGLDisplay,
779     config_id: ffi::egl::types::EGLConfig,
780 ) -> ffi::egl::types::EGLint {
781     let egl = EGL.as_ref().unwrap();
782     let mut value = unsafe { std::mem::zeroed() };
783     let ret = unsafe {
784         egl.GetConfigAttrib(
785             display,
786             config_id,
787             ffi::egl::NATIVE_VISUAL_ID as ffi::egl::types::EGLint,
788             &mut value,
789         )
790     };
791     if ret == 0 {
792         panic!("get_native_visual_id: eglGetConfigAttrib failed with 0x{:x}", unsafe {
793             egl.GetError()
794         })
795     };
796     value
797 }
798 
799 impl<'a> ContextPrototype<'a> {
800     #[cfg(any(
801         target_os = "linux",
802         target_os = "dragonfly",
803         target_os = "freebsd",
804         target_os = "netbsd",
805         target_os = "openbsd",
806     ))]
807     #[cfg(feature = "x11")]
get_native_visual_id(&self) -> ffi::egl::types::EGLint808     pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint {
809         get_native_visual_id(self.display, self.config_id)
810     }
811 
finish(self, nwin: ffi::EGLNativeWindowType) -> Result<Context, CreationError>812     pub fn finish(self, nwin: ffi::EGLNativeWindowType) -> Result<Context, CreationError> {
813         let egl = EGL.as_ref().unwrap();
814         let surface = unsafe {
815             let surface =
816                 egl.CreateWindowSurface(self.display, self.config_id, nwin, std::ptr::null());
817             if surface.is_null() {
818                 return Err(CreationError::OsError("eglCreateWindowSurface failed".to_string()));
819             }
820             surface
821         };
822 
823         self.finish_impl(Some(surface))
824     }
825 
826     #[cfg(any(
827         target_os = "linux",
828         target_os = "dragonfly",
829         target_os = "freebsd",
830         target_os = "netbsd",
831         target_os = "openbsd",
832     ))]
finish_surfaceless(self) -> Result<Context, CreationError>833     pub fn finish_surfaceless(self) -> Result<Context, CreationError> {
834         // FIXME: Also check for the GL_OES_surfaceless_context *CONTEXT*
835         // extension
836         if self.extensions.iter().find(|s| s == &"EGL_KHR_surfaceless_context").is_none() {
837             Err(CreationError::NotSupported("EGL surfaceless not supported".to_string()))
838         } else {
839             self.finish_impl(None)
840         }
841     }
842 
843     #[cfg(any(
844         target_os = "android",
845         target_os = "windows",
846         target_os = "linux",
847         target_os = "dragonfly",
848         target_os = "freebsd",
849         target_os = "netbsd",
850         target_os = "openbsd",
851     ))]
finish_pbuffer(self, size: dpi::PhysicalSize<u32>) -> Result<Context, CreationError>852     pub fn finish_pbuffer(self, size: dpi::PhysicalSize<u32>) -> Result<Context, CreationError> {
853         let size: (u32, u32) = size.into();
854 
855         let egl = EGL.as_ref().unwrap();
856         let tex_fmt = if self.pixel_format.alpha_bits > 0 {
857             ffi::egl::TEXTURE_RGBA
858         } else {
859             ffi::egl::TEXTURE_RGB
860         };
861         let attrs = &[
862             ffi::egl::WIDTH as raw::c_int,
863             size.0 as raw::c_int,
864             ffi::egl::HEIGHT as raw::c_int,
865             size.1 as raw::c_int,
866             ffi::egl::NONE as raw::c_int,
867         ];
868 
869         let surface = unsafe {
870             let surface = egl.CreatePbufferSurface(self.display, self.config_id, attrs.as_ptr());
871             if surface.is_null() || surface == ffi::egl::NO_SURFACE {
872                 return Err(CreationError::OsError("eglCreatePbufferSurface failed".to_string()));
873             }
874             surface
875         };
876 
877         self.finish_impl(Some(surface))
878     }
879 
finish_impl( self, surface: Option<ffi::egl::types::EGLSurface>, ) -> Result<Context, CreationError>880     fn finish_impl(
881         self,
882         surface: Option<ffi::egl::types::EGLSurface>,
883     ) -> Result<Context, CreationError> {
884         let share = match self.opengl.sharing {
885             Some(ctx) => ctx.context,
886             None => std::ptr::null(),
887         };
888 
889         let context = unsafe {
890             if let Some(version) = self.version {
891                 create_context(
892                     self.display,
893                     &self.egl_version,
894                     &self.extensions,
895                     self.api,
896                     version,
897                     self.config_id,
898                     self.opengl.debug,
899                     self.opengl.robustness,
900                     share,
901                 )?
902             } else if self.api == Api::OpenGlEs {
903                 if let Ok(ctx) = create_context(
904                     self.display,
905                     &self.egl_version,
906                     &self.extensions,
907                     self.api,
908                     (2, 0),
909                     self.config_id,
910                     self.opengl.debug,
911                     self.opengl.robustness,
912                     share,
913                 ) {
914                     ctx
915                 } else if let Ok(ctx) = create_context(
916                     self.display,
917                     &self.egl_version,
918                     &self.extensions,
919                     self.api,
920                     (1, 0),
921                     self.config_id,
922                     self.opengl.debug,
923                     self.opengl.robustness,
924                     share,
925                 ) {
926                     ctx
927                 } else {
928                     return Err(CreationError::OpenGlVersionNotSupported);
929                 }
930             } else {
931                 if let Ok(ctx) = create_context(
932                     self.display,
933                     &self.egl_version,
934                     &self.extensions,
935                     self.api,
936                     (3, 2),
937                     self.config_id,
938                     self.opengl.debug,
939                     self.opengl.robustness,
940                     share,
941                 ) {
942                     ctx
943                 } else if let Ok(ctx) = create_context(
944                     self.display,
945                     &self.egl_version,
946                     &self.extensions,
947                     self.api,
948                     (3, 1),
949                     self.config_id,
950                     self.opengl.debug,
951                     self.opengl.robustness,
952                     share,
953                 ) {
954                     ctx
955                 } else if let Ok(ctx) = create_context(
956                     self.display,
957                     &self.egl_version,
958                     &self.extensions,
959                     self.api,
960                     (1, 0),
961                     self.config_id,
962                     self.opengl.debug,
963                     self.opengl.robustness,
964                     share,
965                 ) {
966                     ctx
967                 } else {
968                     return Err(CreationError::OpenGlVersionNotSupported);
969                 }
970             }
971         };
972 
973         if let Some(surface) = surface {
974             // VSync defaults to enabled; disable it if it was not requested.
975             if !self.opengl.vsync {
976                 let _guard = MakeCurrentGuard::new(self.display, surface, surface, context)
977                     .map_err(|err| CreationError::OsError(err))?;
978 
979                 let egl = EGL.as_ref().unwrap();
980                 unsafe {
981                     if egl.SwapInterval(self.display, 0) == ffi::egl::FALSE {
982                         panic!("finish_impl: eglSwapInterval failed: 0x{:x}", egl.GetError());
983                     }
984                 }
985             }
986         }
987 
988         Ok(Context {
989             display: self.display,
990             context,
991             surface: surface.map(|s| Mutex::new(s)),
992             api: self.api,
993             pixel_format: self.pixel_format,
994             #[cfg(target_os = "android")]
995             config_id: self.config_id,
996         })
997     }
998 }
999 
choose_fbconfig<F>( display: ffi::egl::types::EGLDisplay, egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), api: Api, version: Option<(u8, u8)>, pf_reqs: &PixelFormatRequirements, surface_type: SurfaceType, opengl: &GlAttributes<&Context>, mut config_selector: F, ) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError> where F: FnMut( Vec<ffi::egl::types::EGLConfig>, ffi::egl::types::EGLDisplay, ) -> Result<ffi::egl::types::EGLConfig, ()>,1000 unsafe fn choose_fbconfig<F>(
1001     display: ffi::egl::types::EGLDisplay,
1002     egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
1003     api: Api,
1004     version: Option<(u8, u8)>,
1005     pf_reqs: &PixelFormatRequirements,
1006     surface_type: SurfaceType,
1007     opengl: &GlAttributes<&Context>,
1008     mut config_selector: F,
1009 ) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError>
1010 where
1011     F: FnMut(
1012         Vec<ffi::egl::types::EGLConfig>,
1013         ffi::egl::types::EGLDisplay,
1014     ) -> Result<ffi::egl::types::EGLConfig, ()>,
1015 {
1016     let egl = EGL.as_ref().unwrap();
1017 
1018     let descriptor = {
1019         let mut out: Vec<raw::c_int> = Vec::with_capacity(37);
1020 
1021         if egl_version >= &(1, 2) {
1022             out.push(ffi::egl::COLOR_BUFFER_TYPE as raw::c_int);
1023             out.push(ffi::egl::RGB_BUFFER as raw::c_int);
1024         }
1025 
1026         out.push(ffi::egl::SURFACE_TYPE as raw::c_int);
1027         let surface_type = match surface_type {
1028             SurfaceType::Window => ffi::egl::WINDOW_BIT,
1029             SurfaceType::PBuffer => ffi::egl::PBUFFER_BIT,
1030             SurfaceType::Surfaceless => 0,
1031         };
1032         out.push(surface_type as raw::c_int);
1033 
1034         match (api, version) {
1035             (Api::OpenGlEs, Some((3, _))) => {
1036                 if egl_version < &(1, 3) {
1037                     return Err(CreationError::NoAvailablePixelFormat);
1038                 }
1039                 out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
1040                 out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int);
1041                 out.push(ffi::egl::CONFORMANT as raw::c_int);
1042                 out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int);
1043             }
1044             (Api::OpenGlEs, Some((2, _))) => {
1045                 if egl_version < &(1, 3) {
1046                     return Err(CreationError::NoAvailablePixelFormat);
1047                 }
1048                 out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
1049                 out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int);
1050                 out.push(ffi::egl::CONFORMANT as raw::c_int);
1051                 out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int);
1052             }
1053             (Api::OpenGlEs, Some((1, _))) => {
1054                 if egl_version >= &(1, 3) {
1055                     out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
1056                     out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int);
1057                     out.push(ffi::egl::CONFORMANT as raw::c_int);
1058                     out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int);
1059                 }
1060             }
1061             (Api::OpenGlEs, _) => unimplemented!(),
1062             (Api::OpenGl, _) => {
1063                 if egl_version < &(1, 3) {
1064                     return Err(CreationError::NoAvailablePixelFormat);
1065                 }
1066                 out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
1067                 out.push(ffi::egl::OPENGL_BIT as raw::c_int);
1068                 out.push(ffi::egl::CONFORMANT as raw::c_int);
1069                 out.push(ffi::egl::OPENGL_BIT as raw::c_int);
1070             }
1071             (_, _) => unimplemented!(),
1072         };
1073 
1074         if let Some(hardware_accelerated) = pf_reqs.hardware_accelerated {
1075             out.push(ffi::egl::CONFIG_CAVEAT as raw::c_int);
1076             out.push(if hardware_accelerated {
1077                 ffi::egl::NONE as raw::c_int
1078             } else {
1079                 ffi::egl::SLOW_CONFIG as raw::c_int
1080             });
1081         }
1082 
1083         if let Some(color) = pf_reqs.color_bits {
1084             out.push(ffi::egl::RED_SIZE as raw::c_int);
1085             out.push((color / 3) as raw::c_int);
1086             out.push(ffi::egl::GREEN_SIZE as raw::c_int);
1087             out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int);
1088             out.push(ffi::egl::BLUE_SIZE as raw::c_int);
1089             out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int);
1090         }
1091 
1092         if let Some(alpha) = pf_reqs.alpha_bits {
1093             out.push(ffi::egl::ALPHA_SIZE as raw::c_int);
1094             out.push(alpha as raw::c_int);
1095         }
1096 
1097         if let Some(depth) = pf_reqs.depth_bits {
1098             out.push(ffi::egl::DEPTH_SIZE as raw::c_int);
1099             out.push(depth as raw::c_int);
1100         }
1101 
1102         if let Some(stencil) = pf_reqs.stencil_bits {
1103             out.push(ffi::egl::STENCIL_SIZE as raw::c_int);
1104             out.push(stencil as raw::c_int);
1105         }
1106 
1107         if let Some(true) = pf_reqs.double_buffer {
1108             return Err(CreationError::NoAvailablePixelFormat);
1109         }
1110 
1111         if let Some(multisampling) = pf_reqs.multisampling {
1112             out.push(ffi::egl::SAMPLES as raw::c_int);
1113             out.push(multisampling as raw::c_int);
1114         }
1115 
1116         if pf_reqs.stereoscopy {
1117             return Err(CreationError::NoAvailablePixelFormat);
1118         }
1119 
1120         if let Some(xid) = pf_reqs.x11_visual_xid {
1121             out.push(ffi::egl::NATIVE_VISUAL_ID as raw::c_int);
1122             out.push(xid as raw::c_int);
1123         }
1124 
1125         // FIXME: srgb is not taken into account
1126 
1127         match pf_reqs.release_behavior {
1128             ReleaseBehavior::Flush => (),
1129             ReleaseBehavior::None => {
1130                 // TODO: with EGL you need to manually set the behavior
1131                 unimplemented!()
1132             }
1133         }
1134 
1135         out.push(ffi::egl::NONE as raw::c_int);
1136         out
1137     };
1138 
1139     // calling `eglChooseConfig`
1140     let mut num_configs = std::mem::zeroed();
1141     if egl.ChooseConfig(display, descriptor.as_ptr(), std::ptr::null_mut(), 0, &mut num_configs)
1142         == 0
1143     {
1144         return Err(CreationError::OsError("eglChooseConfig failed".to_string()));
1145     }
1146 
1147     if num_configs == 0 {
1148         return Err(CreationError::NoAvailablePixelFormat);
1149     }
1150 
1151     let mut config_ids = Vec::with_capacity(num_configs as usize);
1152     config_ids.resize_with(num_configs as usize, || std::mem::zeroed());
1153     if egl.ChooseConfig(
1154         display,
1155         descriptor.as_ptr(),
1156         config_ids.as_mut_ptr(),
1157         num_configs,
1158         &mut num_configs,
1159     ) == 0
1160     {
1161         return Err(CreationError::OsError("eglChooseConfig failed".to_string()));
1162     }
1163 
1164     // We're interested in those configs which allow our desired VSync.
1165     let desired_swap_interval = if opengl.vsync { 1 } else { 0 };
1166 
1167     let config_ids = config_ids
1168         .into_iter()
1169         .filter(|&config| {
1170             let mut min_swap_interval = 0;
1171             let res = egl.GetConfigAttrib(
1172                 display,
1173                 config,
1174                 ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint,
1175                 &mut min_swap_interval,
1176             );
1177 
1178             if desired_swap_interval < min_swap_interval {
1179                 return false;
1180             }
1181 
1182             let mut max_swap_interval = 0;
1183             let res = egl.GetConfigAttrib(
1184                 display,
1185                 config,
1186                 ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint,
1187                 &mut max_swap_interval,
1188             );
1189 
1190             if desired_swap_interval > max_swap_interval {
1191                 return false;
1192             }
1193 
1194             true
1195         })
1196         .collect::<Vec<_>>();
1197 
1198     if config_ids.is_empty() {
1199         return Err(CreationError::NoAvailablePixelFormat);
1200     }
1201 
1202     let config_id =
1203         config_selector(config_ids, display).map_err(|_| CreationError::NoAvailablePixelFormat)?;
1204 
1205     // analyzing each config
1206     macro_rules! attrib {
1207         ($egl:expr, $display:expr, $config:expr, $attr:expr) => {{
1208             let mut value = std::mem::zeroed();
1209             let res = $egl.GetConfigAttrib(
1210                 $display,
1211                 $config,
1212                 $attr as ffi::egl::types::EGLint,
1213                 &mut value,
1214             );
1215             if res == 0 {
1216                 return Err(CreationError::OsError("eglGetConfigAttrib failed".to_string()));
1217             }
1218             value
1219         }};
1220     };
1221 
1222     let desc = PixelFormat {
1223         hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT)
1224             != ffi::egl::SLOW_CONFIG as i32,
1225         color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8
1226             + attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8
1227             + attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8,
1228         alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) as u8,
1229         depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) as u8,
1230         stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) as u8,
1231         stereoscopy: false,
1232         double_buffer: true,
1233         multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES) {
1234             0 | 1 => None,
1235             a => Some(a as u16),
1236         },
1237         srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
1238     };
1239 
1240     Ok((config_id, desc))
1241 }
1242 
create_context( display: ffi::egl::types::EGLDisplay, egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), extensions: &[String], api: Api, version: (u8, u8), config_id: ffi::egl::types::EGLConfig, gl_debug: bool, gl_robustness: Robustness, share: ffi::EGLContext, ) -> Result<ffi::egl::types::EGLContext, CreationError>1243 unsafe fn create_context(
1244     display: ffi::egl::types::EGLDisplay,
1245     egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
1246     extensions: &[String],
1247     api: Api,
1248     version: (u8, u8),
1249     config_id: ffi::egl::types::EGLConfig,
1250     gl_debug: bool,
1251     gl_robustness: Robustness,
1252     share: ffi::EGLContext,
1253 ) -> Result<ffi::egl::types::EGLContext, CreationError> {
1254     let egl = EGL.as_ref().unwrap();
1255 
1256     let mut context_attributes = Vec::with_capacity(10);
1257     let mut flags = 0;
1258 
1259     if egl_version >= &(1, 5)
1260         || extensions.iter().find(|s| s == &"EGL_KHR_create_context").is_some()
1261     {
1262         context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
1263         context_attributes.push(version.0 as i32);
1264         context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
1265         context_attributes.push(version.1 as i32);
1266 
1267         // handling robustness
1268         let supports_robustness = egl_version >= &(1, 5)
1269             || extensions.iter().find(|s| s == &"EGL_EXT_create_context_robustness").is_some();
1270 
1271         match gl_robustness {
1272             Robustness::NotRobust => (),
1273 
1274             Robustness::NoError => {
1275                 if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() {
1276                     context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as raw::c_int);
1277                     context_attributes.push(1);
1278                 }
1279             }
1280 
1281             Robustness::RobustNoResetNotification => {
1282                 if supports_robustness {
1283                     context_attributes
1284                         .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int);
1285                     context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int);
1286                     flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1287                 } else {
1288                     return Err(CreationError::RobustnessNotSupported);
1289                 }
1290             }
1291 
1292             Robustness::TryRobustNoResetNotification => {
1293                 if supports_robustness {
1294                     context_attributes
1295                         .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int);
1296                     context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int);
1297                     flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1298                 }
1299             }
1300 
1301             Robustness::RobustLoseContextOnReset => {
1302                 if supports_robustness {
1303                     context_attributes
1304                         .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int);
1305                     context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int);
1306                     flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1307                 } else {
1308                     return Err(CreationError::RobustnessNotSupported);
1309                 }
1310             }
1311 
1312             Robustness::TryRobustLoseContextOnReset => {
1313                 if supports_robustness {
1314                     context_attributes
1315                         .push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as raw::c_int);
1316                     context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int);
1317                     flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1318                 }
1319             }
1320         }
1321 
1322         if gl_debug {
1323             if egl_version >= &(1, 5) {
1324                 context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
1325                 context_attributes.push(ffi::egl::TRUE as i32);
1326             }
1327 
1328             // TODO: using this flag sometimes generates an error
1329             //       there was a change in the specs that added this flag, so it
1330             // may not be       supported everywhere ; however it is
1331             // not possible to know whether it is       supported or
1332             // not flags = flags |
1333             // ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32;
1334         }
1335 
1336         // In at least some configurations, the Android emulator’s GL
1337         // implementation advertises support for the
1338         // EGL_KHR_create_context extension but returns BAD_ATTRIBUTE
1339         // when CONTEXT_FLAGS_KHR is used.
1340         if flags != 0 {
1341             context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
1342             context_attributes.push(flags);
1343         }
1344     } else if egl_version >= &(1, 3) && api == Api::OpenGlEs {
1345         // robustness is not supported
1346         match gl_robustness {
1347             Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
1348                 return Err(CreationError::RobustnessNotSupported);
1349             }
1350             _ => (),
1351         }
1352 
1353         context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
1354         context_attributes.push(version.0 as i32);
1355     }
1356 
1357     context_attributes.push(ffi::egl::NONE as i32);
1358 
1359     let context = egl.CreateContext(display, config_id, share, context_attributes.as_ptr());
1360 
1361     if context.is_null() {
1362         match egl.GetError() as u32 {
1363             ffi::egl::BAD_MATCH | ffi::egl::BAD_ATTRIBUTE => {
1364                 return Err(CreationError::OpenGlVersionNotSupported);
1365             }
1366             e => panic!("create_context: eglCreateContext failed: 0x{:x}", e),
1367         }
1368     }
1369 
1370     Ok(context)
1371 }
1372