| 1 | //! GLX object creation. |
| 2 | |
| 3 | use std::collections::HashSet; |
| 4 | use std::ffi::{self, CStr}; |
| 5 | use std::fmt; |
| 6 | use std::ops::Deref; |
| 7 | use std::sync::atomic::Ordering; |
| 8 | use std::sync::Arc; |
| 9 | |
| 10 | use glutin_glx_sys::glx; |
| 11 | use glutin_glx_sys::glx::types::Display as GLXDisplay; |
| 12 | use raw_window_handle::RawDisplayHandle; |
| 13 | |
| 14 | use crate::config::ConfigTemplate; |
| 15 | use crate::context::Version; |
| 16 | use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay}; |
| 17 | use crate::error::{ErrorKind, Result}; |
| 18 | use crate::prelude::*; |
| 19 | use crate::private::Sealed; |
| 20 | use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface}; |
| 21 | |
| 22 | use super::config::Config; |
| 23 | use super::context::NotCurrentContext; |
| 24 | use super::surface::Surface; |
| 25 | use super::{Glx, GlxExtra, XlibErrorHookRegistrar, GLX, GLX_BASE_ERROR, GLX_EXTRA}; |
| 26 | |
| 27 | /// A wrapper for the `GLXDisplay`, which is basically an `XDisplay`. |
| 28 | #[derive (Debug, Clone)] |
| 29 | pub struct Display { |
| 30 | pub(crate) inner: Arc<DisplayInner>, |
| 31 | } |
| 32 | |
| 33 | impl Display { |
| 34 | /// Create GLX display. |
| 35 | /// |
| 36 | /// # Safety |
| 37 | /// |
| 38 | /// The `display` must point to the valid Xlib display and |
| 39 | /// `error_hook_registrar` must be registered in your Xlib error handling |
| 40 | /// callback. |
| 41 | pub unsafe fn new( |
| 42 | display: RawDisplayHandle, |
| 43 | error_hook_registrar: XlibErrorHookRegistrar, |
| 44 | ) -> Result<Self> { |
| 45 | // Don't load GLX when unsupported platform was requested. |
| 46 | let (display, screen) = match display { |
| 47 | RawDisplayHandle::Xlib(handle) => match handle.display { |
| 48 | Some(display) => (GlxDisplay(display.as_ptr() as *mut _), handle.screen as i32), |
| 49 | None => return Err(ErrorKind::BadDisplay.into()), |
| 50 | }, |
| 51 | _ => { |
| 52 | return Err( |
| 53 | ErrorKind::NotSupported("provided native display isn't supported" ).into() |
| 54 | ) |
| 55 | }, |
| 56 | }; |
| 57 | |
| 58 | let glx = match GLX.as_ref() { |
| 59 | Some(glx) => glx, |
| 60 | None => return Err(ErrorKind::NotFound.into()), |
| 61 | }; |
| 62 | |
| 63 | // Set the base for errors coming from GLX. |
| 64 | unsafe { |
| 65 | let mut error_base = 0; |
| 66 | let mut event_base = 0; |
| 67 | if glx.QueryExtension(display.0, &mut error_base, &mut event_base) == 0 { |
| 68 | // The glx extension isn't present. |
| 69 | return Err(ErrorKind::InitializationFailed.into()); |
| 70 | } |
| 71 | GLX_BASE_ERROR.store(error_base, Ordering::Relaxed); |
| 72 | } |
| 73 | |
| 74 | // This is completely ridiculous, but VirtualBox's OpenGL driver needs |
| 75 | // some call handled by *it* (i.e. not Mesa) to occur before |
| 76 | // anything else can happen. That is because VirtualBox's OpenGL |
| 77 | // driver is going to apply binary patches to Mesa in the DLL |
| 78 | // constructor and until it's loaded it won't have a chance to do that. |
| 79 | // |
| 80 | // The easiest way to do this is to just call `glXQueryVersion()` before |
| 81 | // doing anything else. See: https://www.virtualbox.org/ticket/8293 |
| 82 | let version = unsafe { |
| 83 | let (mut major, mut minor) = (0, 0); |
| 84 | if glx.QueryVersion(display.0, &mut major, &mut minor) == 0 { |
| 85 | return Err(ErrorKind::InitializationFailed.into()); |
| 86 | } |
| 87 | Version::new(major as u8, minor as u8) |
| 88 | }; |
| 89 | |
| 90 | if version < Version::new(1, 3) { |
| 91 | return Err(ErrorKind::NotSupported("the glx below 1.3 isn't supported" ).into()); |
| 92 | } |
| 93 | |
| 94 | // Register the error handling hook. |
| 95 | error_hook_registrar(Box::new(super::glx_error_hook)); |
| 96 | |
| 97 | let client_extensions = get_extensions(glx, display); |
| 98 | let features = Self::extract_display_features(&client_extensions, version); |
| 99 | |
| 100 | let inner = Arc::new(DisplayInner { |
| 101 | raw: display, |
| 102 | glx, |
| 103 | glx_extra: GLX_EXTRA.as_ref(), |
| 104 | version, |
| 105 | screen, |
| 106 | features, |
| 107 | client_extensions, |
| 108 | }); |
| 109 | |
| 110 | Ok(Self { inner }) |
| 111 | } |
| 112 | |
| 113 | /// Get a reference to the initialized GLX API. |
| 114 | pub fn glx(&self) -> &'static Glx { |
| 115 | self.inner.glx |
| 116 | } |
| 117 | |
| 118 | fn extract_display_features( |
| 119 | extensions: &HashSet<&'static str>, |
| 120 | version: Version, |
| 121 | ) -> DisplayFeatures { |
| 122 | let mut features = DisplayFeatures::empty(); |
| 123 | |
| 124 | features.set( |
| 125 | DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS, |
| 126 | version >= Version::new(1, 4) || extensions.contains("GLX_ARB_multisample" ), |
| 127 | ); |
| 128 | |
| 129 | features.set( |
| 130 | DisplayFeatures::FLOAT_PIXEL_FORMAT, |
| 131 | extensions.contains("GLX_ARB_fbconfig_float" ), |
| 132 | ); |
| 133 | |
| 134 | features.set( |
| 135 | DisplayFeatures::SRGB_FRAMEBUFFERS, |
| 136 | extensions.contains("GLX_ARB_framebuffer_sRGB" ) |
| 137 | || extensions.contains("GLX_EXT_framebuffer_sRGB" ), |
| 138 | ); |
| 139 | |
| 140 | features.set( |
| 141 | DisplayFeatures::CREATE_ES_CONTEXT, |
| 142 | extensions.contains("GLX_EXT_create_context_es2_profile" ) |
| 143 | || extensions.contains("GLX_EXT_create_context_es_profile" ), |
| 144 | ); |
| 145 | |
| 146 | features.set( |
| 147 | DisplayFeatures::SWAP_CONTROL, |
| 148 | extensions.contains("GLX_EXT_swap_control" ) |
| 149 | || extensions.contains("GLX_SGI_swap_control" ) |
| 150 | || extensions.contains("GLX_MESA_swap_control" ), |
| 151 | ); |
| 152 | |
| 153 | features.set( |
| 154 | DisplayFeatures::CONTEXT_ROBUSTNESS, |
| 155 | extensions.contains("GLX_ARB_create_context_robustness" ), |
| 156 | ); |
| 157 | |
| 158 | features.set( |
| 159 | DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR, |
| 160 | extensions.contains("GLX_ARB_context_flush_control" ), |
| 161 | ); |
| 162 | |
| 163 | features.set( |
| 164 | DisplayFeatures::CONTEXT_NO_ERROR, |
| 165 | extensions.contains("GLX_ARB_create_context_no_error" ), |
| 166 | ); |
| 167 | |
| 168 | features |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | impl GlDisplay for Display { |
| 173 | type Config = Config; |
| 174 | type NotCurrentContext = NotCurrentContext; |
| 175 | type PbufferSurface = Surface<PbufferSurface>; |
| 176 | type PixmapSurface = Surface<PixmapSurface>; |
| 177 | type WindowSurface = Surface<WindowSurface>; |
| 178 | |
| 179 | unsafe fn find_configs( |
| 180 | &self, |
| 181 | template: ConfigTemplate, |
| 182 | ) -> Result<Box<dyn Iterator<Item = Self::Config> + '_>> { |
| 183 | unsafe { Self::find_configs(self, template) } |
| 184 | } |
| 185 | |
| 186 | unsafe fn create_window_surface( |
| 187 | &self, |
| 188 | config: &Self::Config, |
| 189 | surface_attributes: &SurfaceAttributes<WindowSurface>, |
| 190 | ) -> Result<Self::WindowSurface> { |
| 191 | unsafe { Self::create_window_surface(self, config, surface_attributes) } |
| 192 | } |
| 193 | |
| 194 | unsafe fn create_pbuffer_surface( |
| 195 | &self, |
| 196 | config: &Self::Config, |
| 197 | surface_attributes: &SurfaceAttributes<PbufferSurface>, |
| 198 | ) -> Result<Self::PbufferSurface> { |
| 199 | unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) } |
| 200 | } |
| 201 | |
| 202 | unsafe fn create_context( |
| 203 | &self, |
| 204 | config: &Self::Config, |
| 205 | context_attributes: &crate::context::ContextAttributes, |
| 206 | ) -> Result<Self::NotCurrentContext> { |
| 207 | unsafe { Self::create_context(self, config, context_attributes) } |
| 208 | } |
| 209 | |
| 210 | unsafe fn create_pixmap_surface( |
| 211 | &self, |
| 212 | config: &Self::Config, |
| 213 | surface_attributes: &SurfaceAttributes<PixmapSurface>, |
| 214 | ) -> Result<Self::PixmapSurface> { |
| 215 | unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } |
| 216 | } |
| 217 | |
| 218 | fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { |
| 219 | unsafe { self.inner.glx.GetProcAddress(addr.as_ptr() as *const _) as *const _ } |
| 220 | } |
| 221 | |
| 222 | fn version_string(&self) -> String { |
| 223 | format!("GLX {}. {}" , self.inner.version.major, self.inner.version.minor) |
| 224 | } |
| 225 | |
| 226 | fn supported_features(&self) -> DisplayFeatures { |
| 227 | self.inner.features |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | impl GetDisplayExtensions for Display { |
| 232 | fn extensions(&self) -> &HashSet<&'static str> { |
| 233 | &self.inner.client_extensions |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | impl AsRawDisplay for Display { |
| 238 | fn raw_display(&self) -> RawDisplay { |
| 239 | RawDisplay::Glx(self.inner.raw.cast()) |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | impl Sealed for Display {} |
| 244 | |
| 245 | pub(crate) struct DisplayInner { |
| 246 | pub(crate) glx: &'static Glx, |
| 247 | pub(crate) glx_extra: Option<&'static GlxExtra>, |
| 248 | pub(crate) raw: GlxDisplay, |
| 249 | pub(crate) screen: i32, |
| 250 | pub(crate) version: Version, |
| 251 | pub(crate) features: DisplayFeatures, |
| 252 | /// Client GLX extensions. |
| 253 | pub(crate) client_extensions: HashSet<&'static str>, |
| 254 | } |
| 255 | |
| 256 | impl fmt::Debug for DisplayInner { |
| 257 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 258 | f&mut DebugStruct<'_, '_>.debug_struct("Display" ) |
| 259 | .field("raw" , &self.raw) |
| 260 | .field("version" , &self.version) |
| 261 | .field("screen" , &self.screen) |
| 262 | .field("features" , &self.features) |
| 263 | .field(name:"extensions" , &self.client_extensions) |
| 264 | .finish() |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | #[derive (Debug, Clone, Copy)] |
| 269 | pub(crate) struct GlxDisplay(*mut GLXDisplay); |
| 270 | |
| 271 | unsafe impl Send for GlxDisplay {} |
| 272 | unsafe impl Sync for GlxDisplay {} |
| 273 | |
| 274 | impl Deref for GlxDisplay { |
| 275 | type Target = *mut GLXDisplay; |
| 276 | |
| 277 | fn deref(&self) -> &Self::Target { |
| 278 | &self.0 |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /// Load the GLX extensions. |
| 283 | fn get_extensions(glx: &Glx, display: GlxDisplay) -> HashSet<&'static str> { |
| 284 | unsafe { |
| 285 | let extensions: *const {unknown} = glx.GetClientString(dpy:display.0, name:glx::EXTENSIONS as i32); |
| 286 | if extensions.is_null() { |
| 287 | return HashSet::new(); |
| 288 | } |
| 289 | |
| 290 | if let Ok(extensions: &str) = CStr::from_ptr(extensions).to_str() { |
| 291 | extensions.split(' ' ).collect::<HashSet<_>>() |
| 292 | } else { |
| 293 | HashSet::new() |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | |