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