1//! Everything related to `EGLSurface`.
2
3use std::marker::PhantomData;
4use std::num::NonZeroU32;
5use std::{ffi, fmt};
6
7use glutin_egl_sys::egl;
8use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface, EGLint};
9use raw_window_handle::RawWindowHandle;
10#[cfg(wayland_platform)]
11use wayland_sys::{egl::*, ffi_dispatch};
12
13use crate::api::egl::display::EglDisplay;
14use crate::config::GetGlConfig;
15use crate::display::GetGlDisplay;
16use crate::error::{ErrorKind, Result};
17use crate::prelude::*;
18use crate::private::Sealed;
19use crate::surface::{
20 AsRawSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, Rect, SurfaceAttributes,
21 SurfaceTypeTrait, SwapInterval, WindowSurface,
22};
23
24use super::config::Config;
25use super::context::PossiblyCurrentContext;
26use super::display::Display;
27
28/// Hint for the attribute list size.
29const ATTR_SIZE_HINT: usize = 8;
30
31impl Display {
32 pub(crate) unsafe fn create_pbuffer_surface(
33 &self,
34 config: &Config,
35 surface_attributes: &SurfaceAttributes<PbufferSurface>,
36 ) -> Result<Surface<PbufferSurface>> {
37 let width = surface_attributes.width.unwrap();
38 let height = surface_attributes.height.unwrap();
39
40 // XXX Window surface is using `EGLAttrib` and not `EGLint`.
41 let mut attrs = Vec::<EGLint>::with_capacity(ATTR_SIZE_HINT);
42
43 // Add dimensions.
44 attrs.push(egl::WIDTH as EGLint);
45 attrs.push(width.get() as EGLint);
46
47 attrs.push(egl::HEIGHT as EGLint);
48 attrs.push(height.get() as EGLint);
49
50 // Push `egl::NONE` to terminate the list.
51 attrs.push(egl::NONE as EGLint);
52
53 let config = config.clone();
54 let surface = unsafe {
55 Self::check_surface_error(self.inner.egl.CreatePbufferSurface(
56 *self.inner.raw,
57 *config.inner.raw,
58 attrs.as_ptr(),
59 ))?
60 };
61
62 Ok(Surface {
63 display: self.clone(),
64 native_window: None,
65 config,
66 raw: surface,
67 _ty: PhantomData,
68 })
69 }
70
71 pub(crate) unsafe fn create_pixmap_surface(
72 &self,
73 config: &Config,
74 surface_attributes: &SurfaceAttributes<PixmapSurface>,
75 ) -> Result<Surface<PixmapSurface>> {
76 let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap();
77
78 let mut attrs = Vec::<EGLAttrib>::with_capacity(ATTR_SIZE_HINT);
79
80 if surface_attributes.srgb.is_some() && config.srgb_capable() {
81 attrs.push(egl::GL_COLORSPACE as EGLAttrib);
82 let colorspace = match surface_attributes.srgb {
83 Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib,
84 _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib,
85 };
86 attrs.push(colorspace);
87 }
88
89 // Push `egl::NONE` to terminate the list.
90 attrs.push(egl::NONE as EGLAttrib);
91
92 let config = config.clone();
93 let surface = match self.inner.raw {
94 EglDisplay::Khr(display) => {
95 let platform_pixmap = native_pixmap.as_platform_pixmap();
96 if platform_pixmap.is_null() {
97 return Err(ErrorKind::BadNativePixmap.into());
98 }
99 unsafe {
100 self.inner.egl.CreatePlatformPixmapSurface(
101 display,
102 *config.inner.raw,
103 platform_pixmap,
104 attrs.as_ptr(),
105 )
106 }
107 },
108 EglDisplay::Ext(display) => {
109 let platform_pixmap = native_pixmap.as_platform_pixmap();
110 if platform_pixmap.is_null() {
111 return Err(ErrorKind::BadNativePixmap.into());
112 }
113 unsafe {
114 let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
115 self.inner.egl.CreatePlatformPixmapSurfaceEXT(
116 display,
117 *config.inner.raw,
118 platform_pixmap,
119 attrs.as_ptr(),
120 )
121 }
122 },
123 EglDisplay::Legacy(display) => {
124 let native_pixmap = native_pixmap.as_native_pixmap();
125
126 #[cfg(not(windows))]
127 if native_pixmap.is_null() {
128 return Err(ErrorKind::BadNativePixmap.into());
129 }
130
131 #[cfg(windows)]
132 if native_pixmap == 0 {
133 return Err(ErrorKind::BadNativePixmap.into());
134 }
135
136 unsafe {
137 // This call accepts raw value, instead of pointer.
138 let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
139 self.inner.egl.CreatePixmapSurface(
140 display,
141 *config.inner.raw,
142 native_pixmap,
143 attrs.as_ptr(),
144 )
145 }
146 },
147 };
148
149 let surface = Self::check_surface_error(surface)?;
150
151 Ok(Surface {
152 display: self.clone(),
153 config,
154 native_window: None,
155 raw: surface,
156 _ty: PhantomData,
157 })
158 }
159
160 pub(crate) unsafe fn create_window_surface(
161 &self,
162 config: &Config,
163 surface_attributes: &SurfaceAttributes<WindowSurface>,
164 ) -> Result<Surface<WindowSurface>> {
165 // Create native window.
166 let native_window = NativeWindow::new(
167 surface_attributes.width.unwrap(),
168 surface_attributes.height.unwrap(),
169 surface_attributes.raw_window_handle.as_ref().unwrap(),
170 )?;
171
172 // XXX Window surface is using `EGLAttrib` and not `EGLint`.
173 let mut attrs = Vec::<EGLAttrib>::with_capacity(ATTR_SIZE_HINT);
174
175 // Add information about render buffer.
176 attrs.push(egl::RENDER_BUFFER as EGLAttrib);
177 let buffer =
178 if surface_attributes.single_buffer { egl::SINGLE_BUFFER } else { egl::BACK_BUFFER }
179 as EGLAttrib;
180 attrs.push(buffer);
181
182 // // Add colorspace if the extension is present.
183 if surface_attributes.srgb.is_some() && config.srgb_capable() {
184 attrs.push(egl::GL_COLORSPACE as EGLAttrib);
185 let colorspace = match surface_attributes.srgb {
186 Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib,
187 _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib,
188 };
189 attrs.push(colorspace);
190 }
191
192 // Push `egl::NONE` to terminate the list.
193 attrs.push(egl::NONE as EGLAttrib);
194
195 let config = config.clone();
196
197 let surface = match self.inner.raw {
198 EglDisplay::Khr(display) => unsafe {
199 self.inner.egl.CreatePlatformWindowSurface(
200 display,
201 *config.inner.raw,
202 native_window.as_platform_window(),
203 attrs.as_ptr(),
204 )
205 },
206 EglDisplay::Ext(display) => unsafe {
207 let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
208 self.inner.egl.CreatePlatformWindowSurfaceEXT(
209 display,
210 *config.inner.raw,
211 native_window.as_platform_window(),
212 attrs.as_ptr(),
213 )
214 },
215 EglDisplay::Legacy(display) => unsafe {
216 let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
217 self.inner.egl.CreateWindowSurface(
218 display,
219 *config.inner.raw,
220 native_window.as_native_window(),
221 attrs.as_ptr(),
222 )
223 },
224 };
225
226 let surface = Self::check_surface_error(surface)?;
227
228 Ok(Surface {
229 display: self.clone(),
230 config,
231 native_window: Some(native_window),
232 raw: surface,
233 _ty: PhantomData,
234 })
235 }
236
237 fn check_surface_error(surface: EGLSurface) -> Result<EGLSurface> {
238 if surface == egl::NO_SURFACE {
239 Err(super::check_error().err().unwrap())
240 } else {
241 Ok(surface)
242 }
243 }
244}
245
246/// A wrapper around `EGLSurface`.
247pub struct Surface<T: SurfaceTypeTrait> {
248 display: Display,
249 config: Config,
250 pub(crate) raw: EGLSurface,
251 native_window: Option<NativeWindow>,
252 _ty: PhantomData<T>,
253}
254
255// Impl only `Send` for Surface.
256unsafe impl<T: SurfaceTypeTrait> Send for Surface<T> {}
257
258impl<T: SurfaceTypeTrait> Surface<T> {
259 /// Swaps the underlying back buffers when the surface is not single
260 /// buffered and pass the [`Rect`] information to the system
261 /// compositor. Providing empty slice will damage the entire surface.
262 ///
263 /// When the underlying extensions are not supported the function acts like
264 /// [`Self::swap_buffers`].
265 ///
266 /// This Api doesn't do any partial rendering, it just provides hints for
267 /// the system compositor.
268 pub fn swap_buffers_with_damage(
269 &self,
270 context: &PossiblyCurrentContext,
271 rects: &[Rect],
272 ) -> Result<()> {
273 context.inner.bind_api();
274
275 let res = unsafe {
276 if self.display.inner.display_extensions.contains("EGL_KHR_swap_buffers_with_damage") {
277 self.display.inner.egl.SwapBuffersWithDamageKHR(
278 *self.display.inner.raw,
279 self.raw,
280 rects.as_ptr() as *mut _,
281 rects.len() as _,
282 )
283 } else if self
284 .display
285 .inner
286 .display_extensions
287 .contains("EGL_EXT_swap_buffers_with_damage")
288 {
289 self.display.inner.egl.SwapBuffersWithDamageEXT(
290 *self.display.inner.raw,
291 self.raw,
292 rects.as_ptr() as *mut _,
293 rects.len() as _,
294 )
295 } else {
296 self.display.inner.egl.SwapBuffers(*self.display.inner.raw, self.raw)
297 }
298 };
299
300 if res == egl::FALSE {
301 super::check_error()
302 } else {
303 Ok(())
304 }
305 }
306
307 /// # Safety
308 ///
309 /// The caller must ensure that the attribute could be present.
310 unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
311 unsafe {
312 let mut value = 0;
313 self.display.inner.egl.QuerySurface(
314 *self.display.inner.raw,
315 self.raw,
316 attr,
317 &mut value,
318 );
319 value
320 }
321 }
322}
323
324impl<T: SurfaceTypeTrait> Drop for Surface<T> {
325 fn drop(&mut self) {
326 unsafe {
327 self.display.inner.egl.DestroySurface(*self.display.inner.raw, self.raw);
328 }
329 }
330}
331
332impl<T: SurfaceTypeTrait> GlSurface<T> for Surface<T> {
333 type Context = PossiblyCurrentContext;
334 type SurfaceType = T;
335
336 fn buffer_age(&self) -> u32 {
337 self.display
338 .inner
339 .display_extensions
340 .contains("EGL_EXT_buffer_age")
341 .then(|| unsafe { self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) })
342 .unwrap_or(0) as u32
343 }
344
345 fn width(&self) -> Option<u32> {
346 unsafe { Some(self.raw_attribute(egl::WIDTH as EGLint) as u32) }
347 }
348
349 fn height(&self) -> Option<u32> {
350 unsafe { Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) }
351 }
352
353 fn is_single_buffered(&self) -> bool {
354 unsafe { self.raw_attribute(egl::RENDER_BUFFER as EGLint) == egl::SINGLE_BUFFER as i32 }
355 }
356
357 fn swap_buffers(&self, context: &Self::Context) -> Result<()> {
358 unsafe {
359 context.inner.bind_api();
360
361 if self.display.inner.egl.SwapBuffers(*self.display.inner.raw, self.raw) == egl::FALSE {
362 super::check_error()
363 } else {
364 Ok(())
365 }
366 }
367 }
368
369 fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> {
370 unsafe {
371 context.inner.bind_api();
372
373 let interval = match interval {
374 SwapInterval::DontWait => 0,
375 SwapInterval::Wait(interval) => interval.get() as EGLint,
376 };
377 if self.display.inner.egl.SwapInterval(*self.display.inner.raw, interval) == egl::FALSE
378 {
379 super::check_error()
380 } else {
381 Ok(())
382 }
383 }
384 }
385
386 fn is_current(&self, context: &Self::Context) -> bool {
387 self.is_current_draw(context) && self.is_current_read(context)
388 }
389
390 fn is_current_draw(&self, context: &Self::Context) -> bool {
391 unsafe {
392 context.inner.bind_api();
393 self.display.inner.egl.GetCurrentSurface(egl::DRAW as EGLint) == self.raw
394 }
395 }
396
397 fn is_current_read(&self, context: &Self::Context) -> bool {
398 unsafe {
399 context.inner.bind_api();
400 self.display.inner.egl.GetCurrentSurface(egl::READ as EGLint) == self.raw
401 }
402 }
403
404 fn resize(&self, _context: &Self::Context, width: NonZeroU32, height: NonZeroU32) {
405 self.native_window.as_ref().unwrap().resize(width, height)
406 }
407}
408
409impl<T: SurfaceTypeTrait> GetGlConfig for Surface<T> {
410 type Target = Config;
411
412 fn config(&self) -> Self::Target {
413 self.config.clone()
414 }
415}
416
417impl<T: SurfaceTypeTrait> GetGlDisplay for Surface<T> {
418 type Target = Display;
419
420 fn display(&self) -> Self::Target {
421 self.display.clone()
422 }
423}
424
425impl<T: SurfaceTypeTrait> AsRawSurface for Surface<T> {
426 fn raw_surface(&self) -> RawSurface {
427 RawSurface::Egl(self.raw)
428 }
429}
430
431impl<T: SurfaceTypeTrait> fmt::Debug for Surface<T> {
432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433 f&mut DebugStruct<'_, '_>.debug_struct("Surface")
434 .field("display", &self.display.inner.raw)
435 .field("config", &self.config.inner.raw)
436 .field("raw", &self.raw)
437 .field("native_window", &self.native_window)
438 .field(name:"type", &T::surface_type())
439 .finish()
440 }
441}
442
443impl<T: SurfaceTypeTrait> Sealed for Surface<T> {}
444
445#[derive(Debug)]
446enum NativeWindow {
447 #[cfg(wayland_platform)]
448 Wayland(*mut ffi::c_void),
449
450 #[cfg(x11_platform)]
451 Xlib(std::os::raw::c_ulong),
452
453 #[cfg(x11_platform)]
454 Xcb(u32),
455
456 #[cfg(android_platform)]
457 Android(*mut ffi::c_void),
458
459 #[cfg(windows)]
460 Win32(isize),
461
462 #[cfg(free_unix)]
463 Gbm(*mut ffi::c_void),
464}
465
466impl NativeWindow {
467 fn new(
468 _width: NonZeroU32,
469 _height: NonZeroU32,
470 raw_window_handle: &RawWindowHandle,
471 ) -> Result<Self> {
472 let native_window = match raw_window_handle {
473 #[cfg(wayland_platform)]
474 RawWindowHandle::Wayland(window_handle) => unsafe {
475 if window_handle.surface.is_null() {
476 return Err(ErrorKind::BadNativeWindow.into());
477 }
478
479 let ptr = ffi_dispatch!(
480 wayland_egl_handle(),
481 wl_egl_window_create,
482 window_handle.surface.cast(),
483 _width.get() as _,
484 _height.get() as _
485 );
486 if ptr.is_null() {
487 return Err(ErrorKind::OutOfMemory.into());
488 }
489 Self::Wayland(ptr.cast())
490 },
491 #[cfg(x11_platform)]
492 RawWindowHandle::Xlib(window_handle) => {
493 if window_handle.window == 0 {
494 return Err(ErrorKind::BadNativeWindow.into());
495 }
496
497 Self::Xlib(window_handle.window as _)
498 },
499 #[cfg(x11_platform)]
500 RawWindowHandle::Xcb(window_handle) => {
501 if window_handle.window == 0 {
502 return Err(ErrorKind::BadNativeWindow.into());
503 }
504
505 Self::Xcb(window_handle.window as _)
506 },
507 #[cfg(android_platform)]
508 RawWindowHandle::AndroidNdk(window_handle) => {
509 if window_handle.a_native_window.is_null() {
510 return Err(ErrorKind::BadNativeWindow.into());
511 }
512
513 Self::Android(window_handle.a_native_window)
514 },
515 #[cfg(windows)]
516 RawWindowHandle::Win32(window_handle) => {
517 if window_handle.hwnd.is_null() {
518 return Err(ErrorKind::BadNativeWindow.into());
519 }
520
521 Self::Win32(window_handle.hwnd as _)
522 },
523 #[cfg(free_unix)]
524 RawWindowHandle::Gbm(window_handle) => {
525 if window_handle.gbm_surface.is_null() {
526 return Err(ErrorKind::BadNativeWindow.into());
527 }
528
529 Self::Gbm(window_handle.gbm_surface)
530 },
531 _ => {
532 return Err(
533 ErrorKind::NotSupported("provided native window is not supported").into()
534 )
535 },
536 };
537
538 Ok(native_window)
539 }
540
541 fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) {
542 #[cfg(wayland_platform)]
543 if let Self::Wayland(wl_egl_surface) = self {
544 unsafe {
545 ffi_dispatch!(
546 wayland_egl_handle(),
547 wl_egl_window_resize,
548 *wl_egl_surface as _,
549 _width.get() as _,
550 _height.get() as _,
551 0,
552 0
553 )
554 }
555 }
556 }
557
558 /// Returns the underlying handle value.
559 fn as_native_window(&self) -> egl::NativeWindowType {
560 match *self {
561 #[cfg(wayland_platform)]
562 Self::Wayland(wl_egl_surface) => wl_egl_surface,
563 #[cfg(x11_platform)]
564 Self::Xlib(window_id) => window_id as egl::NativeWindowType,
565 #[cfg(x11_platform)]
566 Self::Xcb(window_id) => window_id as egl::NativeWindowType,
567 #[cfg(windows)]
568 Self::Win32(hwnd) => hwnd,
569 #[cfg(android_platform)]
570 Self::Android(a_native_window) => a_native_window,
571 #[cfg(free_unix)]
572 Self::Gbm(gbm_surface) => gbm_surface,
573 }
574 }
575
576 /// Returns a pointer to the underlying handle value on X11,
577 /// the raw underlying handle value on all other platforms.
578 ///
579 /// This exists because of a discrepancy in the new
580 /// `eglCreatePlatformWindowSurface*` functions which take a pointer to the
581 /// `window_id` on X11 and Xlib, in contrast to the legacy
582 /// `eglCreateWindowSurface` which always takes the raw value.
583 ///
584 /// See also:
585 /// <https://gitlab.freedesktop.org/mesa/mesa/-/blob/4de9a4b2b8c41864aadae89be705ef125a745a0a/src/egl/main/eglapi.c#L1102-1127>
586 ///
587 /// # Safety
588 ///
589 /// On X11 the returned pointer is a cast of the `&self` borrow.
590 fn as_platform_window(&self) -> *mut ffi::c_void {
591 match self {
592 #[cfg(wayland_platform)]
593 Self::Wayland(wl_egl_surface) => *wl_egl_surface,
594 #[cfg(x11_platform)]
595 Self::Xlib(window_id) => window_id as *const _ as *mut ffi::c_void,
596 #[cfg(x11_platform)]
597 Self::Xcb(window_id) => window_id as *const _ as *mut ffi::c_void,
598 #[cfg(windows)]
599 Self::Win32(hwnd) => *hwnd as *const ffi::c_void as *mut _,
600 #[cfg(android_platform)]
601 Self::Android(a_native_window) => *a_native_window,
602 #[cfg(free_unix)]
603 Self::Gbm(gbm_surface) => *gbm_surface,
604 }
605 }
606}
607
608#[cfg(wayland_platform)]
609impl Drop for NativeWindow {
610 fn drop(&mut self) {
611 unsafe {
612 if let Self::Wayland(wl_egl_window: &mut *mut c_void) = self {
613 ffi_dispatch!(wayland_egl_handle(), wl_egl_window_destroy, wl_egl_window.cast());
614 }
615 }
616 }
617}
618
619impl NativePixmap {
620 /// Returns the underlying handle value.
621 fn as_native_pixmap(&self) -> egl::NativePixmapType {
622 match *self {
623 Self::XlibPixmap(xid) => xid as egl::NativePixmapType,
624 Self::XcbPixmap(xid) => xid as egl::NativePixmapType,
625 Self::WindowsPixmap(hbitmap) => hbitmap as egl::NativePixmapType,
626 }
627 }
628
629 /// Returns a pointer to the underlying handle value on X11,
630 /// the raw underlying handle value on all other platforms.
631 ///
632 /// This exists because of a discrepancy in the new
633 /// `eglCreatePlatformPixmapSurface*` functions which take a pointer to the
634 /// `xid` on X11 and Xlib, in contrast to the legacy
635 /// `eglCreatePixmapSurface` which always takes the raw value.
636 ///
637 /// See also:
638 /// <https://gitlab.freedesktop.org/mesa/mesa/-/blob/4de9a4b2b8c41864aadae89be705ef125a745a0a/src/egl/main/eglapi.c#L1166-1190>
639 ///
640 /// # Safety
641 ///
642 /// On X11 the returned pointer is a cast of the `&self` borrow.
643 fn as_platform_pixmap(&self) -> *mut ffi::c_void {
644 match self {
645 Self::XlibPixmap(xid) => xid as *const _ as *mut _,
646 Self::XcbPixmap(xid) => xid as *const _ as *mut _,
647 Self::WindowsPixmap(hbitmap) => *hbitmap as *const ffi::c_void as *mut _,
648 }
649 }
650}
651