use glutin::context::{ContextApi, ContextAttributesBuilder, GlProfile, Version};
use glutin::surface::{Surface, SurfaceAttributesBuilder, WindowSurface};
use glutin::display::{GetGlDisplay, GlDisplay};
use glutin::context::PossiblyCurrentContext;
use glutin::config::ConfigTemplateBuilder;
use glutin::prelude::*;
use winit::platform::pump_events::EventLoopExtPumpEvents;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::raw_window_handle::HasWindowHandle;
use winit::application::ApplicationHandler;
use winit::window::{Window, WindowId};
use winit::event::WindowEvent;
use winit::dpi::PhysicalSize;
use glutin_winit::{ApiPreference, DisplayBuilder, GlWindow};
use crate::error::GlInitError;
use std::collections::VecDeque;
#[derive(Debug)]
pub(crate) struct RenderBaseGlState {
pub(crate) window: Window,
pub(crate) gl_context: PossiblyCurrentContext,
pub(crate) gl_surface: Surface<WindowSurface>,
}
#[derive(Debug)]
pub(crate) struct RenderBase {
pub(crate) state: Option<RenderBaseGlState>,
pub(crate) queue: VecDeque<WindowEvent>,
size: (u32, u32),
title: String,
events: bool,
init_error: Option<GlInitError>,
}
impl RenderBase {
pub(crate) fn new(width: u32, height: u32, title: String, event_loop: &mut EventLoop<()>, events: bool) -> Result<Self, GlInitError> {
let mut s = Self {
state: None,
queue: VecDeque::new(),
size: (width, height),
title,
events,
init_error: None,
};
event_loop.pump_app_events(None, &mut s);
if let Some(err) = s.init_error.take() {
return Err(err);
}
if s.state.is_none() {
return Err(GlInitError::NoWindow);
}
Ok(s)
}
fn maybe_forward_event(&mut self, event: WindowEvent) {
if self.events {
self.queue.push_back(event);
}
}
}
impl ApplicationHandler for RenderBase {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let result = (|| -> Result<RenderBaseGlState, GlInitError> {
let window_attrs = Window::default_attributes()
.with_title(&self.title)
.with_inner_size(PhysicalSize::new(self.size.0, self.size.1))
.with_visible(self.events);
let template = ConfigTemplateBuilder::new()
.with_alpha_size(0)
.with_depth_size(24)
.with_stencil_size(8);
let display_builder = DisplayBuilder::new()
.with_preference(ApiPreference::FallbackEgl)
.with_window_attributes(Some(window_attrs));
let (maybe_window, gl_config) = display_builder
.build(event_loop, template, |configs| {
configs.into_iter().reduce(|current, cfg|
if cfg.num_samples() > current.num_samples() {cfg} else { current }
).expect("display produced no GL configs")
})
.map_err(|e| GlInitError::DisplayBuild(e.to_string()))?;
let window = maybe_window
.ok_or(GlInitError::NoWindow)?;
let raw_window_handle = Some(window.window_handle()
.map(|x| x.as_raw())
.map_err(|e| GlInitError::WindowHandle(e.to_string()))?);
let context_attrs = ContextAttributesBuilder::new()
.with_profile(GlProfile::Compatibility)
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 0))))
.build(raw_window_handle);
let gl_display = gl_config.display();
let not_current = unsafe {
gl_display
.create_context(&gl_config, &context_attrs)
.map_err(GlInitError::ContextCreation)?
};
let attrs = window
.build_surface_attributes(SurfaceAttributesBuilder::<WindowSurface>::new())
.map_err(|e| GlInitError::SurfaceAttributes(e.to_string()))?;
let gl_surface = unsafe {
gl_display
.create_window_surface(&gl_config, &attrs)
.map_err(GlInitError::SurfaceCreation)?
};
let gl_context = not_current.make_current(&gl_surface)
.map_err(GlInitError::MakeCurrent)?;
Ok(RenderBaseGlState { gl_surface, gl_context, window })
})();
match result {
Ok(state) => self.state = Some(state),
Err(e) => self.init_error = Some(e),
}
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_id: WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
event_loop.exit();
self.maybe_forward_event(WindowEvent::CloseRequested);
}
WindowEvent::Resized(_) => {
if let Some(RenderBaseGlState { window, gl_context, gl_surface }) = &self.state {
window.resize_surface(gl_surface, gl_context);
}
}
_ => self.maybe_forward_event(event)
}
}
}