| 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 | |