three_d/window/
headless.rs

1use crate::{Context, CoreError};
2use glutin_029::{
3    dpi::PhysicalSize, event_loop::EventLoop, ContextBuilder, ContextCurrentState, CreationError,
4    NotCurrent, PossiblyCurrent,
5};
6use std::rc::Rc;
7use thiserror::Error;
8
9///
10/// Error associated with a headless context.
11///
12#[derive(Error, Debug)]
13#[allow(missing_docs)]
14pub enum HeadlessError {
15    #[error("glutin error")]
16    GlutinCreationError(#[from] glutin_029::CreationError),
17    #[error("glutin error")]
18    GlutinContextError(#[from] glutin_029::ContextError),
19    #[error("error in three-d")]
20    ThreeDError(#[from] CoreError),
21}
22
23///
24/// A headless graphics context, ie. a graphics context that is not associated with any window.
25/// For a graphics context associated with a window, see [WindowedContext](crate::WindowedContext).
26/// Can only be created on native, not on web.
27///
28#[derive(Clone)]
29pub struct HeadlessContext {
30    context: Context,
31    _glutin_context: Rc<glutin_029::Context<PossiblyCurrent>>,
32}
33
34impl HeadlessContext {
35    ///
36    /// Creates a new headless graphics context.
37    ///
38    #[allow(unsafe_code)]
39    pub fn new() -> Result<Self, HeadlessError> {
40        let cb = ContextBuilder::new();
41        let glutin_context = build_context(cb)?;
42        let glutin_context = unsafe { glutin_context.make_current().map_err(|(_, e)| e)? };
43        let context = Context::from_gl_context(std::sync::Arc::new(unsafe {
44            crate::context::Context::from_loader_function(|s| {
45                glutin_context.get_proc_address(s) as *const _
46            })
47        }))?;
48        Ok(Self {
49            context,
50            _glutin_context: Rc::new(glutin_context),
51        })
52    }
53}
54
55impl std::ops::Deref for HeadlessContext {
56    type Target = Context;
57    fn deref(&self) -> &Self::Target {
58        &self.context
59    }
60}
61
62/*#[cfg(target_os = "linux")]
63fn build_context_surfaceless<T1: ContextCurrentState>(
64    cb: ContextBuilder<T1>,
65    el: &EventLoop<()>,
66) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
67    use glutin_029::platform::unix::HeadlessContextExt;
68    cb.build_surfaceless(&el)
69}*/
70
71fn build_context_headless<T1: ContextCurrentState>(
72    cb: ContextBuilder<T1>,
73    el: &EventLoop<()>,
74) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
75    let size_one = PhysicalSize::new(1, 1);
76    cb.build_headless(&el, size_one)
77}
78
79#[cfg(target_os = "linux")]
80fn build_context_osmesa<T1: ContextCurrentState>(
81    cb: ContextBuilder<T1>,
82) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
83    use glutin_029::platform::unix::HeadlessContextExt;
84    let size_one = PhysicalSize::new(1, 1);
85    cb.build_osmesa(size_one)
86}
87
88#[cfg(target_os = "linux")]
89fn build_context<T1: ContextCurrentState>(
90    cb: ContextBuilder<T1>,
91) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
92    // On unix operating systems, you should always try for surfaceless first,
93    // and if that does not work, headless (pbuffers), and if that too fails,
94    // finally osmesa.
95    //
96    // If willing, you could attempt to use hidden windows instead of os mesa,
97    // but note that you must handle events for the window that come on the
98    // events loop.
99
100    /*
101    let err1 = match build_context_surfaceless(cb.clone(), &el) {
102        Ok(ctx) => return Ok((ctx, el)),
103        Err(err) => err,
104    };*/
105
106    let _err3 = match build_context_osmesa(cb.clone()) {
107        Ok(ctx) => return Ok(ctx),
108        Err(err) => err,
109    };
110
111    let el = EventLoop::new();
112
113    let err2 = match build_context_headless(cb, &el) {
114        Ok(ctx) => return Ok(ctx),
115        Err(err) => err,
116    };
117
118    Err(err2)
119}
120
121#[cfg(not(target_os = "linux"))]
122fn build_context<T1: ContextCurrentState>(
123    cb: ContextBuilder<T1>,
124) -> Result<glutin_029::Context<NotCurrent>, CreationError> {
125    let el = EventLoop::new();
126    build_context_headless(cb.clone(), &el)
127}