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: i32 = 0;
198 self.inner.display.inner.glx.GetFBConfigAttrib(
199 self.inner.display.inner.raw.cast(),
200 *self.inner.raw,
201 attribute: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(attr: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: *mut XVisualInfo = 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&mut DebugStruct<'_, '_>.debug_struct("Config")
356 .field("raw", &self.raw)
357 .field(name:"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}
375