1use 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
28const 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 let mut attrs = Vec::<EGLint>::with_capacity(ATTR_SIZE_HINT);
42
43 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 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 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 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 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 let mut attrs = Vec::<EGLAttrib>::with_capacity(ATTR_SIZE_HINT);
174
175 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 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 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
246pub 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
255unsafe impl<T: SurfaceTypeTrait> Send for Surface<T> {}
257
258impl<T: SurfaceTypeTrait> Surface<T> {
259 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 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.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("type", &T::surface_type())
439 .finish()
440 }
441}
442
443impl<T: SurfaceTypeTrait> Sealed for Surface<T> {}
444
445#[derive(Debug)]
446enum NativeWindow {
447 #[allow(dead_code)]
448 Wayland(*mut ffi::c_void),
449 Xlib(std::os::raw::c_ulong),
450 Xcb(u32),
451 Android(*mut ffi::c_void),
452 Ohos(*mut ffi::c_void),
453 Win32(isize),
454 Gbm(*mut ffi::c_void),
455}
456
457impl NativeWindow {
458 fn new(
459 _width: NonZeroU32,
460 _height: NonZeroU32,
461 raw_window_handle: &RawWindowHandle,
462 ) -> Result<Self> {
463 let native_window = match raw_window_handle {
464 #[cfg(wayland_platform)]
465 RawWindowHandle::Wayland(window_handle) => unsafe {
466 let ptr = ffi_dispatch!(
467 wayland_egl_handle(),
468 wl_egl_window_create,
469 window_handle.surface.as_ptr().cast(),
470 _width.get() as _,
471 _height.get() as _
472 );
473 if ptr.is_null() {
474 return Err(ErrorKind::OutOfMemory.into());
475 }
476 Self::Wayland(ptr.cast())
477 },
478 RawWindowHandle::Xlib(window_handle) => {
479 if window_handle.window == 0 {
480 return Err(ErrorKind::BadNativeWindow.into());
481 }
482
483 Self::Xlib(window_handle.window as _)
484 },
485 RawWindowHandle::Xcb(window_handle) => Self::Xcb(window_handle.window.get() as _),
486 RawWindowHandle::AndroidNdk(window_handle) => {
487 Self::Android(window_handle.a_native_window.as_ptr())
488 },
489 RawWindowHandle::OhosNdk(window_handle) => {
490 Self::Ohos(window_handle.native_window.as_ptr())
491 },
492 RawWindowHandle::Win32(window_handle) => Self::Win32(window_handle.hwnd.get() as _),
493 RawWindowHandle::Gbm(window_handle) => Self::Gbm(window_handle.gbm_surface.as_ptr()),
494 _ => {
495 return Err(
496 ErrorKind::NotSupported("provided native window is not supported").into()
497 )
498 },
499 };
500
501 Ok(native_window)
502 }
503
504 fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) {
505 #[cfg(wayland_platform)]
506 if let Self::Wayland(wl_egl_surface) = self {
507 unsafe {
508 ffi_dispatch!(
509 wayland_egl_handle(),
510 wl_egl_window_resize,
511 *wl_egl_surface as _,
512 _width.get() as _,
513 _height.get() as _,
514 0,
515 0
516 )
517 }
518 }
519 }
520
521 fn as_native_window(&self) -> egl::NativeWindowType {
523 match *self {
524 Self::Wayland(wl_egl_surface) => wl_egl_surface as egl::NativeWindowType,
525 Self::Xlib(window_id) => window_id as egl::NativeWindowType,
526 Self::Xcb(window_id) => window_id as egl::NativeWindowType,
527 Self::Win32(hwnd) => hwnd as egl::NativeWindowType,
528 Self::Android(a_native_window) => a_native_window as egl::NativeWindowType,
529 Self::Ohos(native_window) => native_window as egl::NativeWindowType,
530 Self::Gbm(gbm_surface) => gbm_surface as egl::NativeWindowType,
531 }
532 }
533
534 fn as_platform_window(&self) -> *mut ffi::c_void {
549 match self {
550 Self::Wayland(wl_egl_surface) => *wl_egl_surface,
551 Self::Xlib(window_id) => window_id as *const _ as *mut ffi::c_void,
552 Self::Xcb(window_id) => window_id as *const _ as *mut ffi::c_void,
553 Self::Win32(hwnd) => *hwnd as *const ffi::c_void as *mut _,
554 Self::Android(a_native_window) => *a_native_window,
555 Self::Ohos(native_window) => *native_window,
556 Self::Gbm(gbm_surface) => *gbm_surface,
557 }
558 }
559}
560
561#[cfg(wayland_platform)]
562impl Drop for NativeWindow {
563 fn drop(&mut self) {
564 unsafe {
565 if let Self::Wayland(wl_egl_window) = self {
566 ffi_dispatch!(wayland_egl_handle(), wl_egl_window_destroy, wl_egl_window.cast());
567 }
568 }
569 }
570}
571
572impl NativePixmap {
573 fn as_native_pixmap(&self) -> egl::NativePixmapType {
575 match *self {
576 Self::XlibPixmap(xid) => xid as egl::NativePixmapType,
577 Self::XcbPixmap(xid) => xid as egl::NativePixmapType,
578 Self::WindowsPixmap(hbitmap) => hbitmap as egl::NativePixmapType,
579 }
580 }
581
582 fn as_platform_pixmap(&self) -> *mut ffi::c_void {
597 match self {
598 Self::XlibPixmap(xid) => xid as *const _ as *mut _,
599 Self::XcbPixmap(xid) => xid as *const _ as *mut _,
600 Self::WindowsPixmap(hbitmap) => *hbitmap as *const ffi::c_void as *mut _,
601 }
602 }
603}