use glutin::{ dpi::{PhysicalPosition, PhysicalSize}, event_loop::EventLoop, monitor::VideoMode, window::{Fullscreen, Window as GlutinWindow, WindowBuilder}, Api, ContextBuilder, ContextWrapper, GlProfile, GlRequest, PossiblyCurrent, }; use log::{error, info}; use crate::{resources::Config, Error}; pub struct Window { context: ContextWrapper, } impl Window { pub fn new(event_loop: &EventLoop, config: &Config) -> Result { let monitor = event_loop .primary_monitor() .or_else(|| event_loop.available_monitors().next()); let (fullscreen, border) = match (config.video.windowed, config.video.fullscreen) { (false, true) => { let mut video_mode: Option = None; if let Some(monitor) = monitor { for mode in monitor.video_modes() { video_mode = Some(match video_mode { Some(current) => { if mode.size().width <= config.video.resolution.0 && mode.size().height <= config.video.resolution.1 && mode.size().width >= current.size().width && mode.size().height >= current.size().height && mode.bit_depth() >= current.bit_depth() && mode.refresh_rate() >= current.refresh_rate() { mode } else { current } } None => mode, }); } } match video_mode { Some(video_mode) => (Some(Fullscreen::Exclusive(video_mode)), false), None => { error!("Unable to find video mode for exclusive fullscreen!"); (Some(Fullscreen::Borderless(None)), false) } } } (true, true) => (Some(Fullscreen::Borderless(None)), false), (false, false) => (None, false), (true, false) => (None, true), }; let mut multisampling = [16u16, 8, 4, 2, 1, 0].iter(); let context = loop { let multisampling = match multisampling.next() { Some(multisampling) => *multisampling, None => return Err(Error::CreateContext), }; if config.video.multisampling < multisampling { continue; } info!("Create OpenGL context (multisampling={})", multisampling); let window_builder = WindowBuilder::new() .with_title("space-crush") .with_visible(true) .with_decorations(border) .with_fullscreen(fullscreen.clone()) .with_inner_size::>(config.video.resolution.into()); let ret = ContextBuilder::new() .with_double_buffer(Some(true)) .with_hardware_acceleration(Some(true)) .with_pixel_format(24, 8) .with_multisampling(multisampling) .with_vsync(false) .with_gl(GlRequest::Specific(Api::OpenGl, (4, 5))) .with_gl_profile(GlProfile::Core) .build_windowed(window_builder, event_loop); if let Ok(context) = ret { break context; } }; let window = context.window(); let window_size = window.outer_size(); let monitor_size = window.current_monitor().unwrap().size(); window.set_outer_position(PhysicalPosition::new( (monitor_size.width - window_size.width) / 2, (monitor_size.height - window_size.height) / 2, )); let context = unsafe { context.make_current().unwrap() }; gl::load_with(|s| context.get_proc_address(s)); gl::disable(gl::CULL_FACE); gl::disable(gl::DEPTH_TEST); gl::clear_color(0.1, 0.1, 0.1, 1.0); gl::pixel_store_i(gl::UNPACK_ALIGNMENT, 1); Ok(Self { context }) } pub fn swap_buffers(&self) -> Result<(), Error> { self.context.swap_buffers()?; Ok(()) } pub fn set_mouse_locked(&self, value: bool) -> Result<(), Error> { let window = self.context.window(); window.set_cursor_grab(value)?; if !value { window.set_cursor_visible(true); } Ok(()) } pub fn set_mouse_pos(&self, x: f64, y: f64) -> Result<(), Error> { let window = self.context.window(); window.set_cursor_visible(false); window.set_cursor_position(PhysicalPosition { x, y })?; Ok(()) } }