1//! Everything related to finding and manipulating the `EGLConfig`.
2#![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support
3
4use std::ops::Deref;
5use std::sync::Arc;
6use std::{fmt, mem};
7
8use raw_window_handle::RawWindowHandle;
9
10use glutin_egl_sys::egl;
11use glutin_egl_sys::egl::types::{EGLConfig, EGLint};
12
13use crate::config::{
14 Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig,
15};
16use crate::display::{DisplayFeatures, GetGlDisplay};
17use crate::error::{ErrorKind, Result};
18use crate::prelude::*;
19use crate::private::Sealed;
20
21#[cfg(x11_platform)]
22use crate::platform::x11::{X11GlConfigExt, X11VisualInfo};
23
24use super::display::Display;
25
26impl Display {
27 pub(crate) unsafe fn find_configs(
28 &self,
29 template: ConfigTemplate,
30 ) -> Result<Box<dyn Iterator<Item = Config> + '_>> {
31 let mut config_attributes = Vec::<EGLint>::new();
32
33 // Add color buffer type.
34 match template.color_buffer_type {
35 ColorBufferType::Rgb { r_size, g_size, b_size } => {
36 // Type.
37 config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint);
38 config_attributes.push(egl::RGB_BUFFER as EGLint);
39
40 // R.
41 config_attributes.push(egl::RED_SIZE as EGLint);
42 config_attributes.push(r_size as EGLint);
43
44 // G.
45 config_attributes.push(egl::GREEN_SIZE as EGLint);
46 config_attributes.push(g_size as EGLint);
47
48 // B.
49 config_attributes.push(egl::BLUE_SIZE as EGLint);
50 config_attributes.push(b_size as EGLint);
51 },
52 ColorBufferType::Luminance(luminance) => {
53 // Type.
54 config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint);
55 config_attributes.push(egl::LUMINANCE_BUFFER as EGLint);
56
57 // L.
58 config_attributes.push(egl::LUMINANCE_SIZE as EGLint);
59 config_attributes.push(luminance as EGLint);
60 },
61 };
62
63 if template.float_pixels
64 && self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT)
65 {
66 config_attributes.push(egl::COLOR_COMPONENT_TYPE_EXT as EGLint);
67 config_attributes.push(egl::COLOR_COMPONENT_TYPE_FLOAT_EXT as EGLint);
68 } else if template.float_pixels {
69 return Err(ErrorKind::NotSupported("float pixels not supported").into());
70 }
71
72 // Add alpha.
73 config_attributes.push(egl::ALPHA_SIZE as EGLint);
74 config_attributes.push(template.alpha_size as EGLint);
75
76 // Add depth.
77 config_attributes.push(egl::DEPTH_SIZE as EGLint);
78 config_attributes.push(template.depth_size as EGLint);
79
80 // Add stencil.
81 config_attributes.push(egl::STENCIL_SIZE as EGLint);
82 config_attributes.push(template.stencil_size as EGLint);
83
84 // Add surface type.
85 config_attributes.push(egl::SURFACE_TYPE as EGLint);
86 let mut surface_type = 0;
87 if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) {
88 surface_type |= egl::WINDOW_BIT;
89 }
90 if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) {
91 surface_type |= egl::PBUFFER_BIT;
92 }
93 if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) {
94 surface_type |= egl::PIXMAP_BIT;
95 }
96 config_attributes.push(surface_type as EGLint);
97
98 // Add caveat.
99 if let Some(hardware_accelerated) = template.hardware_accelerated {
100 config_attributes.push(egl::CONFIG_CAVEAT as EGLint);
101 if hardware_accelerated {
102 config_attributes.push(egl::NONE as EGLint);
103 } else {
104 config_attributes.push(egl::SLOW_CONFIG as EGLint);
105 }
106 }
107
108 // Add minimum swap interval.
109 if let Some(min_swap_interval) = template.min_swap_interval {
110 config_attributes.push(egl::MIN_SWAP_INTERVAL as EGLint);
111 config_attributes.push(min_swap_interval as EGLint)
112 }
113
114 // Add maximum swap interval.
115 if let Some(max_swap_interval) = template.max_swap_interval {
116 config_attributes.push(egl::MAX_SWAP_INTERVAL as EGLint);
117 config_attributes.push(max_swap_interval as EGLint)
118 }
119
120 // Add multisampling.
121 if let Some(num_samples) = template.num_samples {
122 config_attributes.push(egl::SAMPLE_BUFFERS as EGLint);
123 config_attributes.push(1);
124 config_attributes.push(egl::SAMPLES as EGLint);
125 config_attributes.push(num_samples as EGLint);
126 }
127
128 config_attributes.push(egl::RENDERABLE_TYPE as EGLint);
129 let api = if let Some(requested_api) = template.api {
130 let mut api = 0;
131 if requested_api.contains(Api::GLES1) {
132 api |= egl::OPENGL_ES_BIT;
133 }
134 if requested_api.contains(Api::GLES2) {
135 api |= egl::OPENGL_ES2_BIT;
136 }
137 if requested_api.contains(Api::GLES3) {
138 api |= egl::OPENGL_ES3_BIT;
139 }
140 if requested_api.contains(Api::OPENGL) {
141 api |= egl::OPENGL_BIT;
142 }
143 api
144 } else {
145 // NOTE: use ES2 by default to avoid matching pure ES1 configs,
146 // for more see https://github.com/rust-windowing/glutin/issues/1586.
147 egl::OPENGL_ES2_BIT
148 };
149 config_attributes.push(api as EGLint);
150
151 // Add maximum height of pbuffer.
152 if let Some(pbuffer_width) = template.max_pbuffer_width {
153 config_attributes.push(egl::MAX_PBUFFER_WIDTH as EGLint);
154 config_attributes.push(pbuffer_width as EGLint);
155 }
156
157 // Add maximum width of pbuffer.
158 if let Some(pbuffer_height) = template.max_pbuffer_height {
159 config_attributes.push(egl::MAX_PBUFFER_HEIGHT as EGLint);
160 config_attributes.push(pbuffer_height as EGLint);
161 }
162
163 // Push `egl::NONE` to terminate the list.
164 config_attributes.push(egl::NONE as EGLint);
165
166 let mut configs_number = self.configs_number() as EGLint;
167 let mut found_configs: Vec<EGLConfig> =
168 unsafe { vec![mem::zeroed(); configs_number as usize] };
169
170 unsafe {
171 let result = self.inner.egl.ChooseConfig(
172 *self.inner.raw,
173 config_attributes.as_ptr(),
174 found_configs.as_mut_ptr(),
175 configs_number as EGLint,
176 &mut configs_number,
177 );
178
179 if result == egl::FALSE {
180 return Err(ErrorKind::BadConfig.into());
181 }
182
183 found_configs.set_len(configs_number as usize);
184 }
185
186 let configs = found_configs
187 .into_iter()
188 .map(move |raw| {
189 let raw = EglConfig(raw);
190 let inner = Arc::new(ConfigInner { display: self.clone(), raw });
191 Config { inner }
192 })
193 .filter(move |config| {
194 // Filter configs not compatible with the native window.
195 //
196 // XXX This can't be done by passing visual in the EGL attributes
197 // when calling `eglChooseConfig` since the visual is ignored.
198 match template.native_window {
199 Some(RawWindowHandle::Xcb(xcb)) if xcb.visual_id > 0 => {
200 xcb.visual_id as u32 == config.native_visual()
201 },
202 Some(RawWindowHandle::Xlib(xlib)) if xlib.visual_id > 0 => {
203 xlib.visual_id as u32 == config.native_visual()
204 },
205 _ => true,
206 }
207 })
208 .filter(move |config| {
209 !template.transparency || config.supports_transparency().unwrap_or(true)
210 });
211
212 Ok(Box::new(configs))
213 }
214
215 fn configs_number(&self) -> usize {
216 unsafe {
217 let mut num_configs = 0;
218 self.inner.egl.GetConfigs(*self.inner.raw, std::ptr::null_mut(), 0, &mut num_configs);
219 num_configs as usize
220 }
221 }
222}
223
224/// A simple wrapper around `EGLConfig` that could be used with `EGLContext`
225/// and `EGLSurface`.
226#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct Config {
228 pub(crate) inner: Arc<ConfigInner>,
229}
230
231impl Config {
232 /// The native visual identifier.
233 ///
234 /// The interpretation of this value is platform dependant. Consult
235 /// `platform` extension you're ended up using.
236 pub fn native_visual(&self) -> u32 {
237 unsafe { self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 }
238 }
239
240 /// # Safety
241 ///
242 /// The caller must ensure that the attribute could be present.
243 unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
244 unsafe {
245 let mut val = 0;
246 self.inner.display.inner.egl.GetConfigAttrib(
247 *self.inner.display.inner.raw,
248 *self.inner.raw,
249 attr,
250 &mut val,
251 );
252 val as EGLint
253 }
254 }
255}
256
257impl GlConfig for Config {
258 fn color_buffer_type(&self) -> Option<ColorBufferType> {
259 unsafe {
260 match self.raw_attribute(egl::COLOR_BUFFER_TYPE as EGLint) as _ {
261 egl::LUMINANCE_BUFFER => {
262 let luma = self.raw_attribute(egl::LUMINANCE_SIZE as EGLint);
263 Some(ColorBufferType::Luminance(luma as u8))
264 },
265 egl::RGB_BUFFER => {
266 let r_size = self.raw_attribute(egl::RED_SIZE as EGLint) as u8;
267 let g_size = self.raw_attribute(egl::GREEN_SIZE as EGLint) as u8;
268 let b_size = self.raw_attribute(egl::BLUE_SIZE as EGLint) as u8;
269 Some(ColorBufferType::Rgb { r_size, g_size, b_size })
270 },
271 _ => None,
272 }
273 }
274 }
275
276 fn float_pixels(&self) -> bool {
277 unsafe {
278 if self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) {
279 matches!(
280 self.raw_attribute(egl::COLOR_COMPONENT_TYPE_EXT as EGLint) as _,
281 egl::COLOR_COMPONENT_TYPE_FLOAT_EXT
282 )
283 } else {
284 false
285 }
286 }
287 }
288
289 fn alpha_size(&self) -> u8 {
290 unsafe { self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 }
291 }
292
293 fn srgb_capable(&self) -> bool {
294 self.inner.display.inner.features.contains(DisplayFeatures::SRGB_FRAMEBUFFERS)
295 }
296
297 fn depth_size(&self) -> u8 {
298 unsafe { self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 }
299 }
300
301 fn stencil_size(&self) -> u8 {
302 unsafe { self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 }
303 }
304
305 fn num_samples(&self) -> u8 {
306 unsafe { self.raw_attribute(egl::SAMPLES as EGLint) as u8 }
307 }
308
309 fn config_surface_types(&self) -> ConfigSurfaceTypes {
310 let mut ty = ConfigSurfaceTypes::empty();
311
312 let raw_ty = unsafe { self.raw_attribute(egl::SURFACE_TYPE as EGLint) as u32 };
313 if raw_ty & egl::WINDOW_BIT as u32 != 0 {
314 ty.insert(ConfigSurfaceTypes::WINDOW);
315 }
316 if raw_ty & egl::PBUFFER_BIT as u32 != 0 {
317 ty.insert(ConfigSurfaceTypes::PBUFFER);
318 }
319 if raw_ty & egl::PIXMAP_BIT as u32 != 0 {
320 ty.insert(ConfigSurfaceTypes::PIXMAP);
321 }
322
323 ty
324 }
325
326 fn hardware_accelerated(&self) -> bool {
327 unsafe { self.raw_attribute(egl::CONFIG_CAVEAT as EGLint) != egl::SLOW_CONFIG as EGLint }
328 }
329
330 #[cfg(not(any(wayland_platform, x11_platform)))]
331 fn supports_transparency(&self) -> Option<bool> {
332 None
333 }
334
335 #[cfg(any(wayland_platform, x11_platform))]
336 fn supports_transparency(&self) -> Option<bool> {
337 use raw_window_handle::RawDisplayHandle;
338 match *self.inner.display.inner._native_display? {
339 #[cfg(x11_platform)]
340 RawDisplayHandle::Xlib(_) | RawDisplayHandle::Xcb(_) => {
341 self.x11_visual().map(|visual| visual.supports_transparency())
342 },
343 #[cfg(wayland_platform)]
344 RawDisplayHandle::Wayland(_) => Some(self.alpha_size() != 0),
345 _ => None,
346 }
347 }
348
349 fn api(&self) -> Api {
350 let mut api = Api::empty();
351 let raw_api = unsafe { self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32 };
352 if raw_api & egl::OPENGL_BIT as u32 != 0 {
353 api.insert(Api::OPENGL);
354 }
355 if raw_api & egl::OPENGL_ES_BIT as u32 != 0 {
356 api.insert(Api::GLES1);
357 }
358 if raw_api & egl::OPENGL_ES2_BIT as u32 != 0 {
359 api.insert(Api::GLES2);
360 }
361 if raw_api & egl::OPENGL_ES3_BIT as u32 != 0 {
362 api.insert(Api::GLES3);
363 }
364
365 api
366 }
367}
368
369impl GetGlDisplay for Config {
370 type Target = Display;
371
372 fn display(&self) -> Self::Target {
373 self.inner.display.clone()
374 }
375}
376
377impl AsRawConfig for Config {
378 fn raw_config(&self) -> RawConfig {
379 RawConfig::Egl(*self.inner.raw)
380 }
381}
382
383#[cfg(x11_platform)]
384impl X11GlConfigExt for Config {
385 fn x11_visual(&self) -> Option<X11VisualInfo> {
386 match *self.inner.display.inner._native_display? {
387 raw_window_handle::RawDisplayHandle::Xlib(display_handle: XlibDisplayHandle) => unsafe {
388 let xid: u32 = self.native_visual();
389 X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _)
390 },
391 _ => None,
392 }
393 }
394}
395
396impl Sealed for Config {}
397
398pub(crate) struct ConfigInner {
399 display: Display,
400 pub(crate) raw: EglConfig,
401}
402
403impl PartialEq for ConfigInner {
404 fn eq(&self, other: &Self) -> bool {
405 self.raw == other.raw
406 }
407}
408
409impl Eq for ConfigInner {}
410
411impl fmt::Debug for ConfigInner {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 f&mut DebugStruct<'_, '_>.debug_struct("Config")
414 .field("raw", &self.raw)
415 .field(name:"display", &self.display.inner.raw)
416 .finish()
417 }
418}
419
420#[derive(Debug, Clone, PartialEq, Eq)]
421pub(crate) struct EglConfig(EGLConfig);
422
423unsafe impl Send for EglConfig {}
424unsafe impl Sync for EglConfig {}
425
426impl Deref for EglConfig {
427 type Target = EGLConfig;
428
429 fn deref(&self) -> &Self::Target {
430 &self.0
431 }
432}
433