glutin/api/glx/
config.rs

1//! Everything related to finding and manipulating the `GLXFBConfig`.
2
3use std::ops::Deref;
4use std::os::raw::c_int;
5use std::sync::Arc;
6use std::{fmt, slice};
7
8use glutin_glx_sys::glx::types::GLXFBConfig;
9use glutin_glx_sys::{glx, glx_extra};
10use raw_window_handle::RawWindowHandle;
11
12use crate::config::{
13    Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig,
14};
15use crate::display::{DisplayFeatures, GetGlDisplay};
16use crate::error::{ErrorKind, Result};
17use crate::platform::x11::{X11GlConfigExt, X11VisualInfo, XLIB};
18use crate::private::Sealed;
19
20use super::display::Display;
21
22impl Display {
23    pub(crate) unsafe fn find_configs(
24        &self,
25        template: ConfigTemplate,
26    ) -> Result<Box<dyn Iterator<Item = Config> + '_>> {
27        let mut config_attributes = Vec::<c_int>::new();
28
29        // Add color buffer type.
30        match template.color_buffer_type {
31            ColorBufferType::Rgb { r_size, g_size, b_size } => {
32                // Type.
33                config_attributes.push(glx::X_VISUAL_TYPE as c_int);
34                config_attributes.push(glx::TRUE_COLOR as c_int);
35
36                // R.
37                config_attributes.push(glx::RED_SIZE as c_int);
38                config_attributes.push(r_size as c_int);
39
40                // G.
41                config_attributes.push(glx::GREEN_SIZE as c_int);
42                config_attributes.push(g_size as c_int);
43
44                // B.
45                config_attributes.push(glx::BLUE_SIZE as c_int);
46                config_attributes.push(b_size as c_int);
47            },
48            ColorBufferType::Luminance(luminance) => {
49                // Type.
50                config_attributes.push(glx::X_VISUAL_TYPE as c_int);
51                config_attributes.push(glx::GRAY_SCALE as c_int);
52
53                // L.
54                config_attributes.push(glx::RED_SIZE as c_int);
55                config_attributes.push(luminance as c_int);
56            },
57        };
58
59        // Render type.
60        config_attributes.push(glx::RENDER_TYPE as c_int);
61
62        if template.float_pixels
63            && self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT)
64        {
65            config_attributes.push(glx_extra::RGBA_FLOAT_BIT_ARB as c_int);
66        } else if template.float_pixels {
67            return Err(ErrorKind::NotSupported("float pixels are not supported").into());
68        } else {
69            config_attributes.push(glx::RGBA_BIT as c_int);
70        }
71
72        // Add caveat.
73        if let Some(hardware_accelerated) = template.hardware_accelerated {
74            config_attributes.push(glx::CONFIG_CAVEAT as c_int);
75            if hardware_accelerated {
76                config_attributes.push(glx::NONE as c_int);
77            } else {
78                config_attributes.push(glx::SLOW_CONFIG as c_int);
79            }
80        }
81
82        // Double buffer.
83        config_attributes.push(glx::DOUBLEBUFFER as c_int);
84        config_attributes.push(!template.single_buffering as c_int);
85
86        // Add alpha.
87        config_attributes.push(glx::ALPHA_SIZE as c_int);
88        config_attributes.push(template.alpha_size as c_int);
89
90        // Add depth.
91        config_attributes.push(glx::DEPTH_SIZE as c_int);
92        config_attributes.push(template.depth_size as c_int);
93
94        // Add stencil.
95        config_attributes.push(glx::STENCIL_SIZE as c_int);
96        config_attributes.push(template.stencil_size as c_int);
97
98        // Add visual if was provided.
99        if let Some(RawWindowHandle::Xlib(window)) = template.native_window {
100            if window.visual_id > 0 {
101                config_attributes.push(glx::VISUAL_ID as c_int);
102                config_attributes.push(window.visual_id as c_int);
103            }
104        }
105
106        // Add surface type.
107        config_attributes.push(glx::DRAWABLE_TYPE as c_int);
108        let mut surface_type = 0;
109        if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) {
110            surface_type |= glx::WINDOW_BIT;
111        }
112        if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) {
113            surface_type |= glx::PBUFFER_BIT;
114        }
115        if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) {
116            surface_type |= glx::PIXMAP_BIT;
117        }
118        config_attributes.push(surface_type as c_int);
119
120        // Add maximum height of pbuffer.
121        if let Some(pbuffer_width) = template.max_pbuffer_width {
122            config_attributes.push(glx::MAX_PBUFFER_WIDTH as c_int);
123            config_attributes.push(pbuffer_width as c_int);
124        }
125
126        // Add maximum width of pbuffer.
127        if let Some(pbuffer_height) = template.max_pbuffer_height {
128            config_attributes.push(glx::MAX_PBUFFER_HEIGHT as c_int);
129            config_attributes.push(pbuffer_height as c_int);
130        }
131
132        // Add stereoscopy, if present.
133        if let Some(stereoscopy) = template.stereoscopy {
134            config_attributes.push(glx::STEREO as c_int);
135            config_attributes.push(stereoscopy as c_int);
136        }
137
138        // Add multisampling.
139        if let Some(num_samples) = template.num_samples {
140            if self.inner.features.contains(DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS) {
141                config_attributes.push(glx::SAMPLE_BUFFERS as c_int);
142                config_attributes.push(1);
143                config_attributes.push(glx::SAMPLES as c_int);
144                config_attributes.push(num_samples as c_int);
145            }
146        }
147
148        // Push X11 `None` to terminate the list.
149        config_attributes.push(0);
150
151        unsafe {
152            let mut num_configs = 0;
153            let raw_configs = self.inner.glx.ChooseFBConfig(
154                self.inner.raw.cast(),
155                self.inner.screen as _,
156                config_attributes.as_ptr() as *const _,
157                &mut num_configs,
158            );
159
160            if raw_configs.is_null() {
161                return Err(ErrorKind::BadConfig.into());
162            }
163
164            let configs = slice::from_raw_parts_mut(raw_configs, num_configs as usize).to_vec();
165
166            // Free the memory from the Xlib, since we've just copied it.
167            (XLIB.as_ref().unwrap().XFree)(raw_configs as *mut _);
168
169            let iter = configs
170                .into_iter()
171                .map(move |raw| {
172                    let raw = GlxConfig(raw);
173                    let inner = Arc::new(ConfigInner { display: self.clone(), raw });
174                    Config { inner }
175                })
176                .filter(move |config| {
177                    !template.transparency || config.supports_transparency().unwrap_or(false)
178                });
179
180            Ok(Box::new(iter))
181        }
182    }
183}
184
185/// A wrapper around `GLXFBConfig`.
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct Config {
188    pub(crate) inner: Arc<ConfigInner>,
189}
190
191impl Config {
192    /// # Safety
193    ///
194    /// The caller must ensure that the attribute could be present.
195    unsafe fn raw_attribute(&self, attr: c_int) -> c_int {
196        unsafe {
197            let mut val = 0;
198            self.inner.display.inner.glx.GetFBConfigAttrib(
199                self.inner.display.inner.raw.cast(),
200                *self.inner.raw,
201                attr,
202                &mut val,
203            );
204            val as c_int
205        }
206    }
207
208    pub(crate) fn is_single_buffered(&self) -> bool {
209        unsafe { self.raw_attribute(glx::DOUBLEBUFFER as c_int) == 0 }
210    }
211}
212
213impl GlConfig for Config {
214    fn color_buffer_type(&self) -> Option<ColorBufferType> {
215        unsafe {
216            match self.raw_attribute(glx::X_VISUAL_TYPE as c_int) as _ {
217                glx::TRUE_COLOR => {
218                    let r_size = self.raw_attribute(glx::RED_SIZE as c_int) as u8;
219                    let g_size = self.raw_attribute(glx::GREEN_SIZE as c_int) as u8;
220                    let b_size = self.raw_attribute(glx::BLUE_SIZE as c_int) as u8;
221                    Some(ColorBufferType::Rgb { r_size, g_size, b_size })
222                },
223                glx::GRAY_SCALE => {
224                    let luma = self.raw_attribute(glx::RED_SIZE as c_int);
225                    Some(ColorBufferType::Luminance(luma as u8))
226                },
227                _ => None,
228            }
229        }
230    }
231
232    fn float_pixels(&self) -> bool {
233        if self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) {
234            let render_type =
235                unsafe { self.raw_attribute(glx::RENDER_TYPE as c_int) as glx::types::GLenum };
236            render_type == glx_extra::RGBA_FLOAT_BIT_ARB
237        } else {
238            false
239        }
240    }
241
242    fn alpha_size(&self) -> u8 {
243        unsafe { self.raw_attribute(glx::ALPHA_SIZE as c_int) as u8 }
244    }
245
246    fn hardware_accelerated(&self) -> bool {
247        unsafe { self.raw_attribute(glx::CONFIG_CAVEAT as c_int) != glx::SLOW_CONFIG as c_int }
248    }
249
250    fn srgb_capable(&self) -> bool {
251        if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") {
252            unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 }
253        } else if self.inner.display.inner.client_extensions.contains("GLX_EXT_framebuffer_sRGB") {
254            unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 }
255        } else {
256            false
257        }
258    }
259
260    fn depth_size(&self) -> u8 {
261        unsafe { self.raw_attribute(glx::DEPTH_SIZE as c_int) as u8 }
262    }
263
264    fn stencil_size(&self) -> u8 {
265        unsafe { self.raw_attribute(glx::STENCIL_SIZE as c_int) as u8 }
266    }
267
268    fn num_samples(&self) -> u8 {
269        unsafe { self.raw_attribute(glx::SAMPLES as c_int) as u8 }
270    }
271
272    fn config_surface_types(&self) -> ConfigSurfaceTypes {
273        let mut ty = ConfigSurfaceTypes::empty();
274
275        let raw_ty = unsafe { self.raw_attribute(glx::DRAWABLE_TYPE as c_int) as u32 };
276        if raw_ty & glx::WINDOW_BIT as u32 != 0 {
277            ty.insert(ConfigSurfaceTypes::WINDOW);
278        }
279        if raw_ty & glx::PBUFFER_BIT as u32 != 0 {
280            ty.insert(ConfigSurfaceTypes::PBUFFER);
281        }
282        if raw_ty & glx::PIXMAP_BIT as u32 != 0 {
283            ty.insert(ConfigSurfaceTypes::PIXMAP);
284        }
285
286        ty
287    }
288
289    fn supports_transparency(&self) -> Option<bool> {
290        self.x11_visual().map(|visual| visual.supports_transparency())
291    }
292
293    fn api(&self) -> Api {
294        let mut api = Api::OPENGL;
295        if self.inner.display.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT) {
296            api |= Api::GLES1 | Api::GLES2;
297        }
298
299        api
300    }
301}
302
303impl X11GlConfigExt for Config {
304    fn x11_visual(&self) -> Option<X11VisualInfo> {
305        unsafe {
306            let raw_visual = self
307                .inner
308                .display
309                .inner
310                .glx
311                .GetVisualFromFBConfig(self.inner.display.inner.raw.cast(), *self.inner.raw);
312            if raw_visual.is_null() {
313                None
314            } else {
315                Some(X11VisualInfo::from_raw(
316                    self.inner.display.inner.raw.cast(),
317                    raw_visual as *mut _,
318                ))
319            }
320        }
321    }
322}
323
324impl GetGlDisplay for Config {
325    type Target = Display;
326
327    fn display(&self) -> Self::Target {
328        self.inner.display.clone()
329    }
330}
331
332impl AsRawConfig for Config {
333    fn raw_config(&self) -> RawConfig {
334        RawConfig::Glx(*self.inner.raw)
335    }
336}
337
338impl Sealed for Config {}
339
340pub(crate) struct ConfigInner {
341    display: Display,
342    pub(crate) raw: GlxConfig,
343}
344
345impl PartialEq for ConfigInner {
346    fn eq(&self, other: &Self) -> bool {
347        self.raw == other.raw
348    }
349}
350
351impl Eq for ConfigInner {}
352
353impl fmt::Debug for ConfigInner {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        f.debug_struct("Config")
356            .field("raw", &self.raw)
357            .field("display", &self.display.inner.raw)
358            .finish()
359    }
360}
361
362#[derive(Debug, Clone, PartialEq, Eq)]
363pub(crate) struct GlxConfig(GLXFBConfig);
364
365unsafe impl Send for GlxConfig {}
366unsafe impl Sync for GlxConfig {}
367
368impl Deref for GlxConfig {
369    type Target = GLXFBConfig;
370
371    fn deref(&self) -> &Self::Target {
372        &self.0
373    }
374}