yuno 0.2.0

Multimedia UI layout and rendering framework powered by Skia.
use anyhow::{Context, Result};
use std::ffi::c_void;

pub struct EglGlContext {
    pub egl: khronos_egl::DynamicInstance<khronos_egl::EGL1_5>,
    pub display: khronos_egl::Display,
    pub context: khronos_egl::Context,
    pub surface: khronos_egl::Surface,
    pub config: khronos_egl::Config,
}

impl EglGlContext {
    /// Creates a new EGL OpenGL ES context.
    ///
    /// # Safety
    ///
    /// * `native_display` must be a valid pointer to a live native display handle (e.g. `EGLNativeDisplayType`).
    /// * `native_window` must be a valid pointer to a live native window handle (e.g. `EGLNativeWindowType`).
    /// * The caller must ensure these underlying native resources remain valid for the lifetime of the returned `EglGlContext`.
    pub unsafe fn new(native_display: *mut c_void, native_window: *mut c_void) -> Result<Self> {
        let egl_lib =
            unsafe { libloading::Library::new("libEGL.so.1") }.context("loading libEGL.so.1")?;

        let egl = unsafe {
            khronos_egl::DynamicInstance::<khronos_egl::EGL1_5>::load_required_from(egl_lib)
        }
        .context("initializing EGL dynamic instance")?;

        let display = unsafe { egl.get_display(native_display) }
            .ok_or_else(|| anyhow::anyhow!("eglGetDisplay returned no display"))?;

        egl.initialize(display).context("eglInitialize")?;
        egl.bind_api(khronos_egl::OPENGL_ES_API)
            .context("eglBindAPI(EGL_OPENGL_ES_API)")?;

        let config_attributes = [
            khronos_egl::SURFACE_TYPE,
            khronos_egl::WINDOW_BIT,
            khronos_egl::RED_SIZE,
            8,
            khronos_egl::GREEN_SIZE,
            8,
            khronos_egl::BLUE_SIZE,
            8,
            khronos_egl::ALPHA_SIZE,
            8,
            khronos_egl::RENDERABLE_TYPE,
            khronos_egl::OPENGL_ES2_BIT,
            khronos_egl::NONE,
        ];

        let config = egl
            .choose_first_config(display, &config_attributes)
            .context("eglChooseConfig")?
            .ok_or_else(|| anyhow::anyhow!("no suitable EGL config found"))?;

        let context_attributes = [khronos_egl::CONTEXT_CLIENT_VERSION, 2, khronos_egl::NONE];

        let context = egl
            .create_context(display, config, None, &context_attributes)
            .context("eglCreateContext")?;

        let surface = unsafe { egl.create_window_surface(display, config, native_window, None) }
            .context("eglCreateWindowSurface")?;

        egl.make_current(display, Some(surface), Some(surface), Some(context))
            .context("eglMakeCurrent")?;

        Ok(Self {
            egl,
            display,
            context,
            surface,
            config,
        })
    }

    pub fn swap_buffers(&self) -> Result<()> {
        self.egl
            .swap_buffers(self.display, self.surface)
            .context("eglSwapBuffers")
    }
}

impl Drop for EglGlContext {
    fn drop(&mut self) {
        let _ = self.egl.make_current(self.display, None, None, None);
        let _ = self.egl.destroy_surface(self.display, self.surface);
        let _ = self.egl.destroy_context(self.display, self.context);
        let _ = self.egl.terminate(self.display);
    }
}