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