use crate::{Context, CoreError};
use glutin_029::{
dpi::PhysicalSize, event_loop::EventLoop, ContextBuilder, ContextCurrentState, CreationError,
NotCurrent, PossiblyCurrent,
};
use std::rc::Rc;
use thiserror::Error;
#[derive(Error, Debug)]
#[allow(missing_docs)]
pub enum HeadlessError {
#[error("glutin error")]
GlutinCreationError(#[from] glutin_029::CreationError),
#[error("glutin error")]
GlutinContextError(#[from] glutin_029::ContextError),
#[error("error in three-d")]
ThreeDError(#[from] CoreError),
}
#[derive(Clone)]
pub struct HeadlessContext {
context: Context,
_glutin_context: Rc<glutin_029::Context<PossiblyCurrent>>,
}
impl HeadlessContext {
#[allow(unsafe_code)]
pub fn new() -> Result<Self, HeadlessError> {
let cb = ContextBuilder::new();
let glutin_context = build_context(cb)?;
let glutin_context = unsafe { glutin_context.make_current().map_err(|(_, e)| e)? };
let context = Context::from_gl_context(std::sync::Arc::new(unsafe {
crate::context::Context::from_loader_function(|s| {
glutin_context.get_proc_address(s) as *const _
})
}))?;
Ok(Self {
context,
_glutin_context: Rc::new(glutin_context),
})
}
}
impl std::ops::Deref for HeadlessContext {
type Target = Context;
fn deref(&self) -> &Self::Target {
&self.context
}
}
fn build_context_headless<T1: ContextCurrentState>(
cb: ContextBuilder<T1>,
el: &EventLoop<()>,
) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
let size_one = PhysicalSize::new(1, 1);
cb.build_headless(&el, size_one)
}
#[cfg(target_os = "linux")]
fn build_context_osmesa<T1: ContextCurrentState>(
cb: ContextBuilder<T1>,
) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
use glutin_029::platform::unix::HeadlessContextExt;
let size_one = PhysicalSize::new(1, 1);
cb.build_osmesa(size_one)
}
#[cfg(target_os = "linux")]
fn build_context<T1: ContextCurrentState>(
cb: ContextBuilder<T1>,
) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
let _err3 = match build_context_osmesa(cb.clone()) {
Ok(ctx) => return Ok(ctx),
Err(err) => err,
};
let el = EventLoop::new();
let err2 = match build_context_headless(cb, &el) {
Ok(ctx) => return Ok(ctx),
Err(err) => err,
};
Err(err2)
}
#[cfg(not(target_os = "linux"))]
fn build_context<T1: ContextCurrentState>(
cb: ContextBuilder<T1>,
) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
let el = EventLoop::new();
build_context_headless(cb.clone(), &el)
}