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