1 #![cfg(any(
2     target_os = "linux",
3     target_os = "dragonfly",
4     target_os = "freebsd",
5     target_os = "netbsd",
6     target_os = "openbsd",
7 ))]
8 
9 mod make_current_guard;
10 mod glx {
11     use crate::api::dlloader::{SymTrait, SymWrapper};
12     use glutin_glx_sys as ffi;
13     use std::ops::{Deref, DerefMut};
14 
15     #[derive(Clone)]
16     pub struct Glx(SymWrapper<ffi::glx::Glx>);
17 
18     /// Because `*const raw::c_void` doesn't implement `Sync`.
19     unsafe impl Sync for Glx {}
20 
21     impl SymTrait for ffi::glx::Glx {
load_with<F>(loadfn: F) -> Self where F: FnMut(&'static str) -> *const std::os::raw::c_void,22         fn load_with<F>(loadfn: F) -> Self
23         where
24             F: FnMut(&'static str) -> *const std::os::raw::c_void,
25         {
26             Self::load_with(loadfn)
27         }
28     }
29 
30     impl Glx {
new() -> Result<Self, ()>31         pub fn new() -> Result<Self, ()> {
32             let paths = vec!["libGL.so.1", "libGL.so"];
33 
34             SymWrapper::new(paths).map(|i| Glx(i))
35         }
36     }
37 
38     impl Deref for Glx {
39         type Target = ffi::glx::Glx;
40 
deref(&self) -> &ffi::glx::Glx41         fn deref(&self) -> &ffi::glx::Glx {
42             &self.0
43         }
44     }
45 
46     impl DerefMut for Glx {
deref_mut(&mut self) -> &mut ffi::glx::Glx47         fn deref_mut(&mut self) -> &mut ffi::glx::Glx {
48             &mut self.0
49         }
50     }
51 }
52 
53 pub use self::glx::Glx;
54 use self::make_current_guard::MakeCurrentGuard;
55 use crate::{
56     Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest,
57     PixelFormat, PixelFormatRequirements, ReleaseBehavior, Robustness,
58 };
59 
60 use glutin_glx_sys as ffi;
61 use winit::dpi;
62 use winit::os::unix::x11::XConnection;
63 
64 use std::ffi::{CStr, CString};
65 use std::os::raw;
66 use std::sync::Arc;
67 
68 lazy_static! {
69     pub static ref GLX: Option<Glx> = Glx::new().ok();
70 }
71 
72 #[derive(Debug)]
73 pub struct Context {
74     xconn: Arc<XConnection>,
75     drawable: ffi::Window,
76     context: ffi::GLXContext,
77     pixel_format: PixelFormat,
78 }
79 
80 impl Context {
new<'a>( xconn: Arc<XConnection>, pf_reqs: &PixelFormatRequirements, opengl: &'a GlAttributes<&'a Context>, screen_id: raw::c_int, ) -> Result<ContextPrototype<'a>, CreationError>81     pub fn new<'a>(
82         xconn: Arc<XConnection>,
83         pf_reqs: &PixelFormatRequirements,
84         opengl: &'a GlAttributes<&'a Context>,
85         screen_id: raw::c_int,
86     ) -> Result<ContextPrototype<'a>, CreationError> {
87         let glx = GLX.as_ref().unwrap();
88         // This is completely ridiculous, but VirtualBox's OpenGL driver needs
89         // some call handled by *it* (i.e. not Mesa) to occur before
90         // anything else can happen. That is because VirtualBox's OpenGL
91         // driver is going to apply binary patches to Mesa in the DLL
92         // constructor and until it's loaded it won't have a chance to do that.
93         //
94         // The easiest way to do this is to just call `glXQueryVersion()` before
95         // doing anything else. See: https://www.virtualbox.org/ticket/8293
96         let (mut major, mut minor) = (0, 0);
97         unsafe {
98             glx.QueryVersion(xconn.display as *mut _, &mut major, &mut minor);
99         }
100 
101         // loading the list of extensions
102         let extensions = unsafe {
103             let extensions =
104                 glx.QueryExtensionsString(xconn.display as *mut _, screen_id);
105             if extensions.is_null() {
106                 return Err(CreationError::OsError(
107                     "`glXQueryExtensionsString` found no glX extensions"
108                         .to_string(),
109                 ));
110             }
111             let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();
112             String::from_utf8(extensions).unwrap()
113         };
114 
115         // finding the pixel format we want
116         let (fb_config, pixel_format) = unsafe {
117             choose_fbconfig(
118                 &glx,
119                 &extensions,
120                 &xconn.xlib,
121                 xconn.display,
122                 screen_id,
123                 pf_reqs,
124             )
125             .map_err(|_| CreationError::NoAvailablePixelFormat)?
126         };
127 
128         // getting the visual infos
129         let visual_infos: ffi::glx::types::XVisualInfo = unsafe {
130             let vi =
131                 glx.GetVisualFromFBConfig(xconn.display as *mut _, fb_config);
132             if vi.is_null() {
133                 return Err(CreationError::OsError(
134                     "`glXGetVisualFromFBConfig` failed: invalid `GLXFBConfig`"
135                         .to_string(),
136                 ));
137             }
138             let vi_copy = std::ptr::read(vi as *const _);
139             (xconn.xlib.XFree)(vi as *mut _);
140             vi_copy
141         };
142 
143         Ok(ContextPrototype {
144             extensions,
145             xconn,
146             opengl,
147             fb_config,
148             visual_infos: unsafe { std::mem::transmute(visual_infos) },
149             pixel_format,
150         })
151     }
152 
check_make_current( &self, ret: Option<i32>, ) -> Result<(), ContextError>153     unsafe fn check_make_current(
154         &self,
155         ret: Option<i32>,
156     ) -> Result<(), ContextError> {
157         if ret == Some(0) {
158             let err = self.xconn.check_errors();
159             Err(ContextError::OsError(format!(
160                 "`glXMakeCurrent` failed: {:?}",
161                 err
162             )))
163         } else {
164             Ok(())
165         }
166     }
167 
168     #[inline]
make_current(&self) -> Result<(), ContextError>169     pub unsafe fn make_current(&self) -> Result<(), ContextError> {
170         let glx = GLX.as_ref().unwrap();
171         let res = glx.MakeCurrent(
172             self.xconn.display as *mut _,
173             self.drawable,
174             self.context,
175         );
176         self.check_make_current(Some(res))
177     }
178 
179     #[inline]
make_not_current(&self) -> Result<(), ContextError>180     pub unsafe fn make_not_current(&self) -> Result<(), ContextError> {
181         let glx = GLX.as_ref().unwrap();
182         if self.drawable == glx.GetCurrentDrawable()
183             || self.context == glx.GetCurrentContext()
184         {
185             let res = glx.MakeCurrent(
186                 self.xconn.display as *mut _,
187                 0,
188                 std::ptr::null(),
189             );
190             self.check_make_current(Some(res))
191         } else {
192             self.check_make_current(None)
193         }
194     }
195 
196     #[inline]
is_current(&self) -> bool197     pub fn is_current(&self) -> bool {
198         let glx = GLX.as_ref().unwrap();
199         unsafe { glx.GetCurrentContext() == self.context }
200     }
201 
202     #[inline]
get_api(&self) -> crate::Api203     pub fn get_api(&self) -> crate::Api {
204         crate::Api::OpenGl
205     }
206 
207     #[inline]
raw_handle(&self) -> ffi::GLXContext208     pub unsafe fn raw_handle(&self) -> ffi::GLXContext {
209         self.context
210     }
211 
212     #[inline]
get_proc_address(&self, addr: &str) -> *const ()213     pub fn get_proc_address(&self, addr: &str) -> *const () {
214         let glx = GLX.as_ref().unwrap();
215         let addr = CString::new(addr.as_bytes()).unwrap();
216         let addr = addr.as_ptr();
217         unsafe { glx.GetProcAddress(addr as *const _) as *const _ }
218     }
219 
220     #[inline]
swap_buffers(&self) -> Result<(), ContextError>221     pub fn swap_buffers(&self) -> Result<(), ContextError> {
222         let glx = GLX.as_ref().unwrap();
223         unsafe {
224             glx.SwapBuffers(self.xconn.display as *mut _, self.drawable);
225         }
226         if let Err(err) = self.xconn.check_errors() {
227             Err(ContextError::OsError(format!(
228                 "`glXSwapBuffers` failed: {:?}",
229                 err
230             )))
231         } else {
232             Ok(())
233         }
234     }
235 
236     #[inline]
get_pixel_format(&self) -> PixelFormat237     pub fn get_pixel_format(&self) -> PixelFormat {
238         self.pixel_format.clone()
239     }
240 }
241 
242 unsafe impl Send for Context {}
243 unsafe impl Sync for Context {}
244 
245 impl Drop for Context {
drop(&mut self)246     fn drop(&mut self) {
247         let glx = GLX.as_ref().unwrap();
248         unsafe {
249             // See `drop` for `crate::api::egl::Context` for rationale.
250             let mut guard =
251                 MakeCurrentGuard::new(&self.xconn, self.drawable, self.context)
252                     .map_err(|err| ContextError::OsError(err))
253                     .unwrap();
254 
255             let gl_finish_fn = self.get_proc_address("glFinish");
256             assert!(gl_finish_fn != std::ptr::null());
257             let gl_finish_fn =
258                 std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn);
259             gl_finish_fn();
260 
261             if guard.old_context() == Some(self.context) {
262                 guard.invalidate()
263             }
264             std::mem::drop(guard);
265 
266             glx.DestroyContext(self.xconn.display as *mut _, self.context);
267         }
268     }
269 }
270 
271 #[derive(Debug)]
272 pub struct ContextPrototype<'a> {
273     extensions: String,
274     xconn: Arc<XConnection>,
275     opengl: &'a GlAttributes<&'a Context>,
276     fb_config: ffi::glx::types::GLXFBConfig,
277     visual_infos: ffi::XVisualInfo,
278     pixel_format: PixelFormat,
279 }
280 
281 impl<'a> ContextPrototype<'a> {
282     #[inline]
get_visual_infos(&self) -> &ffi::XVisualInfo283     pub fn get_visual_infos(&self) -> &ffi::XVisualInfo {
284         &self.visual_infos
285     }
286 
287     // creating GL context
create_context( &self, ) -> Result<(ffi::glx_extra::Glx, ffi::GLXContext), CreationError>288     fn create_context(
289         &self,
290     ) -> Result<(ffi::glx_extra::Glx, ffi::GLXContext), CreationError> {
291         let glx = GLX.as_ref().unwrap();
292         let share = match self.opengl.sharing {
293             Some(ctx) => ctx.context,
294             None => std::ptr::null(),
295         };
296 
297         // loading the extra GLX functions
298         let extra_functions = ffi::glx_extra::Glx::load_with(|proc_name| {
299             let c_str = CString::new(proc_name).unwrap();
300             unsafe {
301                 glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _
302             }
303         });
304 
305         let context = match self.opengl.version {
306             GlRequest::Latest => {
307                 let opengl_versions = [
308                     (4, 6),
309                     (4, 5),
310                     (4, 4),
311                     (4, 3),
312                     (4, 2),
313                     (4, 1),
314                     (4, 0),
315                     (3, 3),
316                     (3, 2),
317                     (3, 1),
318                 ];
319                 let ctx;
320                 'outer: loop {
321                     // Try all OpenGL versions in descending order because some
322                     // non-compliant drivers don't return
323                     // the latest supported version but the one requested
324                     for opengl_version in opengl_versions.iter() {
325                         match create_context(
326                             &extra_functions,
327                             &self.extensions,
328                             &self.xconn.xlib,
329                             *opengl_version,
330                             self.opengl.profile,
331                             self.opengl.debug,
332                             self.opengl.robustness,
333                             share,
334                             self.xconn.display,
335                             self.fb_config,
336                             &self.visual_infos,
337                         ) {
338                             Ok(x) => {
339                                 ctx = x;
340                                 break 'outer;
341                             }
342                             Err(_) => continue,
343                         }
344                     }
345                     ctx = create_context(
346                         &extra_functions,
347                         &self.extensions,
348                         &self.xconn.xlib,
349                         (1, 0),
350                         self.opengl.profile,
351                         self.opengl.debug,
352                         self.opengl.robustness,
353                         share,
354                         self.xconn.display,
355                         self.fb_config,
356                         &self.visual_infos,
357                     )?;
358                     break;
359                 }
360                 ctx
361             }
362             GlRequest::Specific(Api::OpenGl, (major, minor)) => create_context(
363                 &extra_functions,
364                 &self.extensions,
365                 &self.xconn.xlib,
366                 (major, minor),
367                 self.opengl.profile,
368                 self.opengl.debug,
369                 self.opengl.robustness,
370                 share,
371                 self.xconn.display,
372                 self.fb_config,
373                 &self.visual_infos,
374             )?,
375             GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
376             GlRequest::GlThenGles {
377                 opengl_version: (major, minor),
378                 ..
379             } => create_context(
380                 &extra_functions,
381                 &self.extensions,
382                 &self.xconn.xlib,
383                 (major, minor),
384                 self.opengl.profile,
385                 self.opengl.debug,
386                 self.opengl.robustness,
387                 share,
388                 self.xconn.display,
389                 self.fb_config,
390                 &self.visual_infos,
391             )?,
392         };
393 
394         Ok((extra_functions, context))
395     }
396 
finish_pbuffer( self, size: dpi::PhysicalSize, ) -> Result<Context, CreationError>397     pub fn finish_pbuffer(
398         self,
399         size: dpi::PhysicalSize,
400     ) -> Result<Context, CreationError> {
401         let glx = GLX.as_ref().unwrap();
402         let size: (u32, u32) = size.into();
403         let (_extra_functions, context) = self.create_context()?;
404 
405         let attributes: Vec<raw::c_int> = vec![
406             ffi::glx::PBUFFER_WIDTH as raw::c_int,
407             size.0 as raw::c_int,
408             ffi::glx::PBUFFER_HEIGHT as raw::c_int,
409             size.1 as raw::c_int,
410             0,
411         ];
412 
413         let pbuffer = unsafe {
414             glx.CreatePbuffer(
415                 self.xconn.display as *mut _,
416                 self.fb_config,
417                 attributes.as_ptr(),
418             )
419         };
420 
421         Ok(Context {
422             xconn: self.xconn,
423             drawable: pbuffer,
424             context,
425             pixel_format: self.pixel_format,
426         })
427     }
428 
finish(self, window: ffi::Window) -> Result<Context, CreationError>429     pub fn finish(self, window: ffi::Window) -> Result<Context, CreationError> {
430         let glx = GLX.as_ref().unwrap();
431         let (extra_functions, context) = self.create_context()?;
432 
433         // vsync
434         if self.opengl.vsync {
435             let _guard = MakeCurrentGuard::new(&self.xconn, window, context)
436                 .map_err(|err| CreationError::OsError(err))?;
437 
438             if check_ext(&self.extensions, "GLX_EXT_swap_control")
439                 && extra_functions.SwapIntervalEXT.is_loaded()
440             {
441                 // this should be the most common extension
442                 unsafe {
443                     extra_functions.SwapIntervalEXT(
444                         self.xconn.display as *mut _,
445                         window,
446                         1,
447                     );
448                 }
449 
450                 let mut swap = unsafe { std::mem::uninitialized() };
451                 unsafe {
452                     glx.QueryDrawable(
453                         self.xconn.display as *mut _,
454                         window,
455                         ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
456                         &mut swap,
457                     );
458                 }
459 
460                 if swap != 1 {
461                     return Err(CreationError::OsError(format!("Couldn't setup vsync: expected interval `1` but got `{}`", swap)));
462                 }
463             } else if check_ext(&self.extensions, "GLX_MESA_swap_control")
464                 && extra_functions.SwapIntervalMESA.is_loaded()
465             {
466                 unsafe {
467                     extra_functions.SwapIntervalMESA(1);
468                 }
469             } else if check_ext(&self.extensions, "GLX_SGI_swap_control")
470                 && extra_functions.SwapIntervalSGI.is_loaded()
471             {
472                 unsafe {
473                     extra_functions.SwapIntervalSGI(1);
474                 }
475             } else {
476                 return Err(CreationError::OsError(
477                     "Couldn't find any available vsync extension".to_string(),
478                 ));
479             }
480         }
481 
482         Ok(Context {
483             xconn: self.xconn,
484             drawable: window,
485             context,
486             pixel_format: self.pixel_format,
487         })
488     }
489 }
490 
x_error_callback( _dpy: *mut ffi::Display, _err: *mut ffi::XErrorEvent, ) -> i32491 extern "C" fn x_error_callback(
492     _dpy: *mut ffi::Display,
493     _err: *mut ffi::XErrorEvent,
494 ) -> i32 {
495     0
496 }
497 
create_context( extra_functions: &ffi::glx_extra::Glx, extensions: &str, xlib: &ffi::Xlib, version: (u8, u8), profile: Option<GlProfile>, debug: bool, robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display, fb_config: ffi::glx::types::GLXFBConfig, visual_infos: &ffi::XVisualInfo, ) -> Result<ffi::GLXContext, CreationError>498 fn create_context(
499     extra_functions: &ffi::glx_extra::Glx,
500     extensions: &str,
501     xlib: &ffi::Xlib,
502     version: (u8, u8),
503     profile: Option<GlProfile>,
504     debug: bool,
505     robustness: Robustness,
506     share: ffi::GLXContext,
507     display: *mut ffi::Display,
508     fb_config: ffi::glx::types::GLXFBConfig,
509     visual_infos: &ffi::XVisualInfo,
510 ) -> Result<ffi::GLXContext, CreationError> {
511     let glx = GLX.as_ref().unwrap();
512     unsafe {
513         let old_callback = (xlib.XSetErrorHandler)(Some(x_error_callback));
514         let context = if check_ext(extensions, "GLX_ARB_create_context") {
515             let mut attributes = Vec::with_capacity(9);
516 
517             attributes
518                 .push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int);
519             attributes.push(version.0 as raw::c_int);
520             attributes
521                 .push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int);
522             attributes.push(version.1 as raw::c_int);
523 
524             if let Some(profile) = profile {
525                 let flag = match profile {
526                     GlProfile::Compatibility => {
527                         ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
528                     }
529                     GlProfile::Core => {
530                         ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB
531                     }
532                 };
533 
534                 attributes.push(
535                     ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int,
536                 );
537                 attributes.push(flag as raw::c_int);
538             }
539 
540             let flags = {
541                 let mut flags = 0;
542 
543                 // robustness
544                 if check_ext(extensions, "GLX_ARB_create_context_robustness") {
545                     match robustness {
546                         Robustness::RobustNoResetNotification
547                         | Robustness::TryRobustNoResetNotification => {
548                             attributes.push(
549                                 ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as raw::c_int,
550                             );
551                             attributes.push(
552                                 ffi::glx_extra::NO_RESET_NOTIFICATION_ARB
553                                     as raw::c_int,
554                             );
555                             flags = flags
556                                 | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB
557                                     as raw::c_int;
558                         }
559                         Robustness::RobustLoseContextOnReset
560                         | Robustness::TryRobustLoseContextOnReset => {
561                             attributes.push(
562                                 ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as raw::c_int,
563                             );
564                             attributes.push(
565                                 ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB
566                                     as raw::c_int,
567                             );
568                             flags = flags
569                                 | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB
570                                     as raw::c_int;
571                         }
572                         Robustness::NotRobust => (),
573                         Robustness::NoError => (),
574                     }
575                 } else {
576                     match robustness {
577                         Robustness::RobustNoResetNotification
578                         | Robustness::RobustLoseContextOnReset => {
579                             return Err(CreationError::RobustnessNotSupported);
580                         }
581                         _ => (),
582                     }
583                 }
584 
585                 if debug {
586                     flags = flags
587                         | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int;
588                 }
589 
590                 flags
591             };
592 
593             attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as raw::c_int);
594             attributes.push(flags);
595 
596             attributes.push(0);
597 
598             extra_functions.CreateContextAttribsARB(
599                 display as *mut _,
600                 fb_config,
601                 share,
602                 1,
603                 attributes.as_ptr(),
604             )
605         } else {
606             let visual_infos: *const ffi::XVisualInfo = visual_infos;
607             glx.CreateContext(
608                 display as *mut _,
609                 visual_infos as *mut _,
610                 share,
611                 1,
612             )
613         };
614 
615         (xlib.XSetErrorHandler)(old_callback);
616 
617         if context.is_null() {
618             // TODO: check for errors and return `OpenGlVersionNotSupported`
619             return Err(CreationError::OsError(
620                 "GL context creation failed".to_string(),
621             ));
622         }
623 
624         Ok(context)
625     }
626 }
627 
628 /// Enumerates all available FBConfigs
choose_fbconfig( glx: &Glx, extensions: &str, xlib: &ffi::Xlib, display: *mut ffi::Display, screen_id: raw::c_int, reqs: &PixelFormatRequirements, ) -> Result<(ffi::glx::types::GLXFBConfig, PixelFormat), ()>629 unsafe fn choose_fbconfig(
630     glx: &Glx,
631     extensions: &str,
632     xlib: &ffi::Xlib,
633     display: *mut ffi::Display,
634     screen_id: raw::c_int,
635     reqs: &PixelFormatRequirements,
636 ) -> Result<(ffi::glx::types::GLXFBConfig, PixelFormat), ()> {
637     let descriptor = {
638         let mut out: Vec<raw::c_int> = Vec::with_capacity(37);
639 
640         out.push(ffi::glx::X_RENDERABLE as raw::c_int);
641         out.push(1);
642 
643         // TODO: If passed an visual xid, maybe we should stop assuming
644         // TRUE_COLOR.
645         out.push(ffi::glx::X_VISUAL_TYPE as raw::c_int);
646         out.push(ffi::glx::TRUE_COLOR as raw::c_int);
647 
648         if let Some(xid) = reqs.x11_visual_xid {
649             out.push(ffi::glx::VISUAL_ID as raw::c_int);
650             out.push(xid as raw::c_int);
651         }
652 
653         out.push(ffi::glx::DRAWABLE_TYPE as raw::c_int);
654         out.push(ffi::glx::WINDOW_BIT as raw::c_int);
655 
656         out.push(ffi::glx::RENDER_TYPE as raw::c_int);
657         if reqs.float_color_buffer {
658             if check_ext(extensions, "GLX_ARB_fbconfig_float") {
659                 out.push(ffi::glx_extra::RGBA_FLOAT_BIT_ARB as raw::c_int);
660             } else {
661                 return Err(());
662             }
663         } else {
664             out.push(ffi::glx::RGBA_BIT as raw::c_int);
665         }
666 
667         if let Some(color) = reqs.color_bits {
668             out.push(ffi::glx::RED_SIZE as raw::c_int);
669             out.push((color / 3) as raw::c_int);
670             out.push(ffi::glx::GREEN_SIZE as raw::c_int);
671             out.push(
672                 (color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int,
673             );
674             out.push(ffi::glx::BLUE_SIZE as raw::c_int);
675             out.push(
676                 (color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int,
677             );
678         }
679 
680         if let Some(alpha) = reqs.alpha_bits {
681             out.push(ffi::glx::ALPHA_SIZE as raw::c_int);
682             out.push(alpha as raw::c_int);
683         }
684 
685         if let Some(depth) = reqs.depth_bits {
686             out.push(ffi::glx::DEPTH_SIZE as raw::c_int);
687             out.push(depth as raw::c_int);
688         }
689 
690         if let Some(stencil) = reqs.stencil_bits {
691             out.push(ffi::glx::STENCIL_SIZE as raw::c_int);
692             out.push(stencil as raw::c_int);
693         }
694 
695         let double_buffer = reqs.double_buffer.unwrap_or(true);
696         out.push(ffi::glx::DOUBLEBUFFER as raw::c_int);
697         out.push(if double_buffer { 1 } else { 0 });
698 
699         if let Some(multisampling) = reqs.multisampling {
700             if check_ext(extensions, "GLX_ARB_multisample") {
701                 out.push(ffi::glx_extra::SAMPLE_BUFFERS_ARB as raw::c_int);
702                 out.push(if multisampling == 0 { 0 } else { 1 });
703                 out.push(ffi::glx_extra::SAMPLES_ARB as raw::c_int);
704                 out.push(multisampling as raw::c_int);
705             } else {
706                 return Err(());
707             }
708         }
709 
710         out.push(ffi::glx::STEREO as raw::c_int);
711         out.push(if reqs.stereoscopy { 1 } else { 0 });
712 
713         if reqs.srgb {
714             if check_ext(extensions, "GLX_ARB_framebuffer_sRGB") {
715                 out.push(
716                     ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int,
717                 );
718                 out.push(1);
719             } else if check_ext(extensions, "GLX_EXT_framebuffer_sRGB") {
720                 out.push(
721                     ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int,
722                 );
723                 out.push(1);
724             } else {
725                 return Err(());
726             }
727         }
728 
729         match reqs.release_behavior {
730             ReleaseBehavior::Flush => (),
731             ReleaseBehavior::None => {
732                 if check_ext(extensions, "GLX_ARB_context_flush_control") {
733                     out.push(
734                         ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB
735                             as raw::c_int,
736                     );
737                     out.push(
738                         ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB
739                             as raw::c_int,
740                     );
741                 }
742             }
743         }
744 
745         out.push(ffi::glx::CONFIG_CAVEAT as raw::c_int);
746         out.push(ffi::glx::DONT_CARE as raw::c_int);
747 
748         out.push(0);
749         out
750     };
751 
752     // calling glXChooseFBConfig
753     let fb_config = {
754         let mut num_configs = 1;
755         let configs = glx.ChooseFBConfig(
756             display as *mut _,
757             screen_id,
758             descriptor.as_ptr(),
759             &mut num_configs,
760         );
761         if configs.is_null() {
762             return Err(());
763         }
764         if num_configs == 0 {
765             return Err(());
766         }
767         let config = Some(&*configs);
768 
769         let res = if let Some(&conf) = config {
770             Ok(conf)
771         } else {
772             Err(())
773         };
774 
775         (xlib.XFree)(configs as *mut _);
776         res?
777     };
778 
779     let get_attrib = |attrib: raw::c_int| -> i32 {
780         let mut value = 0;
781         glx.GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value);
782         // TODO: check return value
783         value
784     };
785 
786     let pf_desc = PixelFormat {
787         hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as raw::c_int)
788             != ffi::glx::SLOW_CONFIG as raw::c_int,
789         color_bits: get_attrib(ffi::glx::RED_SIZE as raw::c_int) as u8
790             + get_attrib(ffi::glx::GREEN_SIZE as raw::c_int) as u8
791             + get_attrib(ffi::glx::BLUE_SIZE as raw::c_int) as u8,
792         alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as raw::c_int) as u8,
793         depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as raw::c_int) as u8,
794         stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as raw::c_int) as u8,
795         stereoscopy: get_attrib(ffi::glx::STEREO as raw::c_int) != 0,
796         double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as raw::c_int) != 0,
797         multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as raw::c_int)
798             != 0
799         {
800             Some(get_attrib(ffi::glx::SAMPLES as raw::c_int) as u16)
801         } else {
802             None
803         },
804         srgb: get_attrib(
805             ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int,
806         ) != 0
807             || get_attrib(
808                 ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int,
809             ) != 0,
810     };
811 
812     Ok((fb_config, pf_desc))
813 }
814 
815 /// Checks if `ext` is available.
check_ext(extensions: &str, ext: &str) -> bool816 fn check_ext(extensions: &str, ext: &str) -> bool {
817     extensions.split(' ').find(|&s| s == ext).is_some()
818 }
819