#![cfg(feature = "glutin")]
pub use glutin;
pub mod headless;
use crate::backend;
use crate::backend::Backend;
use crate::backend::Context;
use crate::context;
use crate::debug;
use crate::glutin::{ContextCurrentState, PossiblyCurrent as Pc};
use std::cell::{Cell, Ref, RefCell};
use std::error::Error;
use std::fmt;
use std::ops::Deref;
use std::os::raw::c_void;
use std::rc::Rc;
use takeable_option::Takeable;
use crate::{Frame, IncompatibleOpenGl, SwapBuffersError};
#[derive(Clone)]
pub struct Display {
context: Rc<context::Context>,
gl_window: Rc<RefCell<Takeable<glutin::WindowedContext<Pc>>>>,
last_framebuffer_dimensions: Cell<(u32, u32)>,
}
#[derive(Clone)]
pub struct GlutinBackend(Rc<RefCell<Takeable<glutin::WindowedContext<Pc>>>>);
#[derive(Debug)]
pub enum DisplayCreationError {
GlutinCreationError(glutin::CreationError),
IncompatibleOpenGl(IncompatibleOpenGl),
}
impl std::fmt::Debug for Display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[glium::backend::glutin::Display]")
}
}
impl Display {
pub fn new<T: ContextCurrentState, E>(
wb: glutin::window::WindowBuilder,
cb: glutin::ContextBuilder<'_, T>,
events_loop: &glutin::event_loop::EventLoop<E>,
) -> Result<Self, DisplayCreationError> {
let gl_window = cb.build_windowed(wb, events_loop)?;
Self::from_gl_window(gl_window).map_err(From::from)
}
pub fn from_gl_window<T: ContextCurrentState>(
gl_window: glutin::WindowedContext<T>,
) -> Result<Self, IncompatibleOpenGl> {
Self::with_debug(gl_window, Default::default())
}
pub unsafe fn unchecked<T: ContextCurrentState>(
gl_window: glutin::WindowedContext<T>,
) -> Result<Self, IncompatibleOpenGl> {
Self::unchecked_with_debug(gl_window, Default::default())
}
pub fn with_debug<T: ContextCurrentState>(
gl_window: glutin::WindowedContext<T>,
debug: debug::DebugCallbackBehavior,
) -> Result<Self, IncompatibleOpenGl> {
Self::new_inner(gl_window, debug, true)
}
pub unsafe fn unchecked_with_debug<T: ContextCurrentState>(
gl_window: glutin::WindowedContext<T>,
debug: debug::DebugCallbackBehavior,
) -> Result<Self, IncompatibleOpenGl> {
Self::new_inner(gl_window, debug, false)
}
fn new_inner<T: ContextCurrentState>(
gl_window: glutin::WindowedContext<T>,
debug: debug::DebugCallbackBehavior,
checked: bool,
) -> Result<Self, IncompatibleOpenGl> {
let gl_window = unsafe { gl_window.treat_as_current() };
let gl_window = Rc::new(RefCell::new(Takeable::new(gl_window)));
let glutin_backend = GlutinBackend(gl_window.clone());
let framebuffer_dimensions = glutin_backend.get_framebuffer_dimensions();
let context = unsafe { context::Context::new(glutin_backend, checked, debug) }?;
Ok(Display {
gl_window,
context,
last_framebuffer_dimensions: Cell::new(framebuffer_dimensions),
})
}
pub fn rebuild<T: ContextCurrentState>(
&self,
wb: glutin::window::WindowBuilder,
cb: glutin::ContextBuilder<'_, T>,
events_loop: &glutin::event_loop::EventLoop<()>,
) -> Result<(), DisplayCreationError> {
let new_gl_window = {
let gl_window = self.gl_window.borrow();
let cb = cb.with_shared_lists(gl_window.context());
cb.build_windowed(wb, events_loop)?
};
let new_gl_window = unsafe { new_gl_window.treat_as_current() };
{
let mut gl_window = self.gl_window.borrow_mut();
Takeable::insert(&mut gl_window, new_gl_window);
}
let backend = GlutinBackend(self.gl_window.clone());
unsafe { self.context.rebuild(backend) }?;
Ok(())
}
#[inline]
pub fn gl_window(&self) -> Ref<'_, Takeable<glutin::WindowedContext<Pc>>> {
self.gl_window.borrow()
}
#[inline]
pub fn draw(&self) -> Frame {
let (w, h) = self.get_framebuffer_dimensions();
if self.last_framebuffer_dimensions.get() != (w, h) {
self.last_framebuffer_dimensions.set((w, h));
self.gl_window.borrow().resize((w, h).into());
}
Frame::new(self.context.clone(), (w, h))
}
}
impl fmt::Display for DisplayCreationError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
DisplayCreationError::GlutinCreationError(err) => write!(fmt, "{}", err),
DisplayCreationError::IncompatibleOpenGl(err) => write!(fmt, "{}", err),
}
}
}
impl Error for DisplayCreationError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
DisplayCreationError::GlutinCreationError(ref err) => Some(err),
DisplayCreationError::IncompatibleOpenGl(ref err) => Some(err),
}
}
}
impl From<glutin::CreationError> for DisplayCreationError {
#[inline]
fn from(err: glutin::CreationError) -> DisplayCreationError {
DisplayCreationError::GlutinCreationError(err)
}
}
impl From<IncompatibleOpenGl> for DisplayCreationError {
#[inline]
fn from(err: IncompatibleOpenGl) -> DisplayCreationError {
DisplayCreationError::IncompatibleOpenGl(err)
}
}
impl Deref for Display {
type Target = Context;
#[inline]
fn deref(&self) -> &Context {
&self.context
}
}
impl backend::Facade for Display {
#[inline]
fn get_context(&self) -> &Rc<Context> {
&self.context
}
}
impl Deref for GlutinBackend {
type Target = Rc<RefCell<Takeable<glutin::WindowedContext<Pc>>>>;
#[inline]
fn deref(&self) -> &Rc<RefCell<Takeable<glutin::WindowedContext<Pc>>>> {
&self.0
}
}
unsafe impl Backend for GlutinBackend {
#[inline]
fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
match self.borrow().swap_buffers() {
Ok(()) => Ok(()),
Err(glutin::ContextError::IoError(e)) => {
panic!("I/O Error while swapping buffers: {:?}", e)
}
Err(glutin::ContextError::OsError(e)) => {
panic!("OS Error while swapping buffers: {:?}", e)
}
Err(glutin::ContextError::FunctionUnavailable) => {
panic!("function unavailable error while swapping buffers")
}
Err(glutin::ContextError::ContextLost) => Err(SwapBuffersError::ContextLost),
}
}
#[inline]
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.borrow().get_proc_address(symbol) as *const _
}
#[inline]
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
let gl_window_takeable = self.borrow();
let gl_window = gl_window_takeable.window();
let (width, height) = gl_window.inner_size().into();
(width, height)
}
#[inline]
fn is_current(&self) -> bool {
self.borrow().is_current()
}
#[inline]
unsafe fn make_current(&self) {
let mut gl_window_takeable = self.borrow_mut();
let gl_window = Takeable::take(&mut gl_window_takeable);
let new_gl_window = gl_window.make_current().unwrap();
Takeable::insert(&mut gl_window_takeable, new_gl_window);
}
}