#![cfg(feature = "glutin")]
pub extern crate glutin;
pub mod headless;
use takeable_option::Takeable;
use {Frame, IncompatibleOpenGl, SwapBuffersError};
use debug;
use context;
use backend;
use backend::Context;
use backend::Backend;
use std::cell::{Cell, RefCell, Ref};
use std::error::Error;
use std::fmt;
use std::rc::Rc;
use std::ops::Deref;
use std::os::raw::c_void;
use glutin::{PossiblyCurrent as Pc, ContextCurrentState};
#[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 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: gl_window,
context: 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> {
write!(fmt, "{}", self.description())
}
}
impl Error for DisplayCreationError {
#[inline]
fn description(&self) -> &str {
match *self {
DisplayCreationError::GlutinCreationError(ref err) => err.description(),
DisplayCreationError::IncompatibleOpenGl(ref err) => err.description(),
}
}
#[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()
.to_physical(gl_window.hidpi_factor())
.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);
}
}