1 | //! Everything related to finding and manipulating the `GLXFBConfig`. |
2 | |
3 | use std::ops::Deref; |
4 | use std::os::raw::c_int; |
5 | use std::sync::Arc; |
6 | use std::{fmt, slice}; |
7 | |
8 | use glutin_glx_sys::glx::types::GLXFBConfig; |
9 | use glutin_glx_sys::{glx, glx_extra}; |
10 | use raw_window_handle::RawWindowHandle; |
11 | |
12 | use crate::config::{ |
13 | Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, GlConfig, RawConfig, |
14 | }; |
15 | use crate::display::{DisplayFeatures, GetGlDisplay}; |
16 | use crate::error::{ErrorKind, Result}; |
17 | use crate::platform::x11::{X11GlConfigExt, X11VisualInfo, XLIB}; |
18 | use crate::private::Sealed; |
19 | |
20 | use super::display::Display; |
21 | |
22 | impl 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)] |
187 | pub struct Config { |
188 | pub(crate) inner: Arc<ConfigInner>, |
189 | } |
190 | |
191 | impl 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 | |
213 | impl 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 | |
303 | impl 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 | |
324 | impl GetGlDisplay for Config { |
325 | type Target = Display; |
326 | |
327 | fn display(&self) -> Self::Target { |
328 | self.inner.display.clone() |
329 | } |
330 | } |
331 | |
332 | impl AsRawConfig for Config { |
333 | fn raw_config(&self) -> RawConfig { |
334 | RawConfig::Glx(*self.inner.raw) |
335 | } |
336 | } |
337 | |
338 | impl Sealed for Config {} |
339 | |
340 | pub(crate) struct ConfigInner { |
341 | display: Display, |
342 | pub(crate) raw: GlxConfig, |
343 | } |
344 | |
345 | impl PartialEq for ConfigInner { |
346 | fn eq(&self, other: &Self) -> bool { |
347 | self.raw == other.raw |
348 | } |
349 | } |
350 | |
351 | impl Eq for ConfigInner {} |
352 | |
353 | impl 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)] |
363 | pub(crate) struct GlxConfig(GLXFBConfig); |
364 | |
365 | unsafe impl Send for GlxConfig {} |
366 | unsafe impl Sync for GlxConfig {} |
367 | |
368 | impl Deref for GlxConfig { |
369 | type Target = GLXFBConfig; |
370 | |
371 | fn deref(&self) -> &Self::Target { |
372 | &self.0 |
373 | } |
374 | } |
375 | |