use std::{ffi::CStr, marker::PhantomData, mem};
use glutin::{
dpi::{LogicalSize, Size},
event::Event,
event_loop::EventLoop,
window::{Fullscreen, Icon, WindowBuilder},
Api, ContextBuilder, GlProfile, GlRequest,
};
use ogl33::load_gl_with;
use crate::{wrapper::Wrapper, Context, Events, Surface};
pub struct Config {
ctx_attribs: ContextAttributes,
win_attribs: WindowAttributes,
pel_attribs: PelAttributes,
phantom: PhantomData<*const ()>, }
impl Config {
pub fn vsync(mut self, vsync: bool) -> Self {
self.ctx_attribs.vsync = vsync;
self
}
pub fn srgb(mut self, srgb: bool) -> Self {
self.ctx_attribs.srgb = srgb;
self
}
pub fn window_size(mut self, width: u32, height: u32) -> Self {
self.win_attribs.size = Some(LogicalSize::new(width, height).into());
self
}
pub fn min_window_size(mut self, width: u32, height: u32) -> Self {
self.win_attribs.min_size = Some(LogicalSize::new(width, height).into());
self
}
pub fn max_window_size(mut self, width: u32, height: u32) -> Self {
self.win_attribs.max_size = Some(LogicalSize::new(width, height).into());
self
}
pub fn resizable(mut self, resizable: bool) -> Self {
self.win_attribs.resizable = resizable;
self
}
pub fn maximized(mut self, maximized: bool) -> Self {
self.win_attribs.maximized = maximized;
self
}
pub fn decorations(mut self, decorations: bool) -> Self {
self.win_attribs.decorations = decorations;
self
}
pub fn icon<S: Into<Surface>>(mut self: Self, icon: S) -> Self {
let (surface, w, h) = icon.into().into_raw_parts();
let buf = Vec::<u8>::with_capacity(surface.len() * 4);
let buf = surface.into_iter().fold(buf, |mut vec, color| {
let bytes: [u8; 4] = color.into();
vec.extend_from_slice(&[bytes[1], bytes[2], bytes[3], bytes[0]]);
vec
});
self.win_attribs.icon = Some(Icon::from_rgba(buf, w as u32, h as u32).expect("malformed surface?"));
self
}
pub fn title<T: Into<String>>(mut self, title: T) -> Self {
self.win_attribs.title = Some(title.into());
self
}
pub fn fullscreen(mut self, fullscreen: bool) -> Self {
self.win_attribs.fullscreen = fullscreen;
self
}
pub fn always_on_top(mut self, always_on_top: bool) -> Self {
self.win_attribs.always_on_top = always_on_top;
self
}
pub fn texture_size(mut self, width: usize, height: usize) -> Self {
self.pel_attribs.texture_size = Some((width, height));
self
}
pub fn run<E>(self, event_handler: E) -> !
where
E: Events + 'static,
{
let mut wb = WindowBuilder::new();
wb.window.inner_size = self.win_attribs.size;
wb.window.min_inner_size = self.win_attribs.min_size;
wb.window.max_inner_size = self.win_attribs.max_size;
wb.window.resizable = self.win_attribs.resizable;
wb.window.maximized = self.win_attribs.maximized;
wb.window.visible = self.win_attribs.visible;
wb.window.decorations = self.win_attribs.decorations;
wb.window.always_on_top = self.win_attribs.always_on_top;
wb.window.window_icon = self.win_attribs.icon.clone();
wb.window.title = self
.win_attribs
.title
.clone()
.unwrap_or_else(|| String::from("pel window"));
let el = EventLoop::new();
let context = ContextBuilder::new()
.with_gl(GlRequest::Specific(Api::OpenGl, (3, 3)))
.with_gl_profile(GlProfile::Core)
.with_vsync(self.ctx_attribs.vsync)
.with_srgb(self.ctx_attribs.srgb)
.with_double_buffer(Some(true))
.with_hardware_acceleration(Some(true))
.build_windowed(wb, &el)
.expect("could not create a window");
let window = unsafe {
context
.make_current()
.expect("could not make OpenGL 3.3 the current context")
};
unsafe {
load_gl_with(|ptr| {
window
.context()
.get_proc_address(CStr::from_ptr(ptr).to_str().expect("invalid c_str")) as *const _
});
}
if self.win_attribs.fullscreen {
let window_handle = window.window();
let monitor_handle = window_handle.current_monitor();
window_handle.set_fullscreen(Some(Fullscreen::Borderless(monitor_handle)));
}
let mut pel = Wrapper::build(event_handler);
let mut ctx = Context::new(window, &self.pel_attribs);
mem::drop(self);
el.run(move |event, _, flow| match event {
Event::WindowEvent { window_id, event } => pel.window_event(&mut ctx, window_id, event, flow),
Event::DeviceEvent { device_id, event } => pel.device_event(&mut ctx, device_id, event),
Event::NewEvents(cause) => pel.start(&mut ctx, cause),
Event::MainEventsCleared => pel.update(&mut ctx),
Event::RedrawRequested(window_id) => pel.draw(&mut ctx, window_id),
Event::LoopDestroyed => pel.loop_destroyed(),
_ => (),
})
}
pub(crate) fn new() -> Self {
Self::default()
}
}
impl Default for Config {
fn default() -> Self {
Self {
ctx_attribs: ContextAttributes::default(),
win_attribs: WindowAttributes::default(),
pel_attribs: PelAttributes::default(),
phantom: PhantomData,
}
}
}
struct ContextAttributes {
vsync: bool,
srgb: bool,
}
impl Default for ContextAttributes {
fn default() -> Self {
Self {
vsync: true,
srgb: true,
}
}
}
struct WindowAttributes {
size: Option<Size>,
min_size: Option<Size>,
max_size: Option<Size>,
resizable: bool,
maximized: bool,
visible: bool,
decorations: bool,
always_on_top: bool,
icon: Option<Icon>,
title: Option<String>,
fullscreen: bool,
}
impl Default for WindowAttributes {
fn default() -> Self {
Self {
size: None,
min_size: None,
max_size: None,
resizable: true,
maximized: false,
visible: true,
decorations: true,
always_on_top: false,
icon: None,
title: None,
fullscreen: false,
}
}
}
pub struct PelAttributes {
pub texture_size: Option<(usize, usize)>,
}
impl Default for PelAttributes {
fn default() -> Self {
Self { texture_size: None }
}
}