sprocket_engine 0.2.1

A vulkan game engine
Documentation
use std::sync::mpsc::Receiver;

use glfw::WindowHint;
use glfw::*;

#[derive(Copy, Clone, Debug)]
pub enum Error {
    InitError,
    WindowCreationError,
    NoPrimaryMonitor,
    NoVideoMode,
}

impl From<glfw::InitError> for Error {
    fn from(_: glfw::InitError) -> Self {
        Self::InitError
    }
}

pub enum WindowMode {
    Windowed,
    Fullscreen,
    Borderless,
}

pub struct Window {
    glfw_window: glfw::Window,
    glfw_context: Glfw,
    event_receiver: Receiver<(f64, WindowEvent)>,
}

pub use glfw::Key;
pub use glfw::MouseButton;
pub use glfw::WindowEvent;

pub type WindowEventStream<'a> = std::iter::Map<
    std::sync::mpsc::TryIter<'a, (f64, WindowEvent)>,
    fn((f64, WindowEvent)) -> WindowEvent,
>;

impl Window {
    pub fn new(spec: &WindowSpec) -> Result<Self, Error> {
        let mut glfw_context = glfw::init(glfw::FAIL_ON_ERRORS)?;

        let (mut window, event_receiver) =
            glfw_context.with_primary_monitor(|glfw_context, monitor| {
                let monitor = monitor.unwrap();
                let mode = monitor.get_video_mode().ok_or(Error::NoVideoMode)?;

                // Set width and height correctly
                let (width, height) = match spec.native {
                    true => (mode.width, mode.height),
                    false => (spec.width, spec.height),
                };

                glfw_context.window_hint(WindowHint::Resizable(spec.resizable));

                let mode = match spec.mode {
                    WindowMode::Windowed => glfw::WindowMode::Windowed,
                    WindowMode::Borderless => {
                        glfw_context.window_hint(WindowHint::Decorated(false));
                        glfw::WindowMode::Windowed
                    }
                    WindowMode::Fullscreen => {
                        glfw_context.window_hint(WindowHint::RedBits(Some(mode.red_bits)));
                        glfw_context.window_hint(WindowHint::GreenBits(Some(mode.green_bits)));
                        glfw_context.window_hint(WindowHint::BlueBits(Some(mode.blue_bits)));
                        glfw_context.window_hint(WindowHint::RefreshRate(Some(mode.refresh_rate)));

                        glfw::WindowMode::FullScreen(monitor)
                    }
                };

                glfw_context
                    .create_window(width, height, &spec.title, mode)
                    .ok_or(Error::WindowCreationError)
            })?;

        // Enable event polling
        window.set_all_polling(true);
        Ok(Self {
            glfw_window: window,
            glfw_context,
            event_receiver,
        })
    }

    pub fn process_events<'a>(&'a mut self) -> WindowEventStream {
        self.glfw_context.poll_events();
        (&self.event_receiver)
            .try_iter()
            .map(map_event as fn((f64, WindowEvent)) -> WindowEvent)
    }

    /// Returns if the window should close. E.g; the user pressed the X button
    pub fn should_close(&self) -> bool {
        self.glfw_window.should_close()
    }

    /// Returns the windows width
    pub fn width(&self) -> u32 {
        self.glfw_window.get_size().0 as u32
    }

    /// Returns the windows height
    pub fn height(&self) -> u32 {
        self.glfw_window.get_size().1 as u32
    }

    /// Returns a tuple of the windows width and height
    pub fn size(&self) -> (u32, u32) {
        let (w, h) = self.glfw_window.get_size();
        (w as u32, h as u32)
    }
}

/// Specifications/builder for window creation
pub struct WindowSpec {
    width: u32,
    height: u32,
    title: String,
    native: bool,
    resizable: bool,
    mode: WindowMode,
}

impl WindowSpec {
    /// Creates a new window builder with default values
    /// width: 800
    /// height: 600
    /// title: ""
    /// resizable: true
    /// mode: Windowed
    pub fn new() -> Self {
        Self {
            width: 800,
            height: 600,
            title: String::new(),
            native: false,
            resizable: true,
            mode: WindowMode::Windowed,
        }
    }

    /// Sets width and height of window
    pub fn size(mut self, width: u32, height: u32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Sets the window to fullscreen
    pub fn fullscreen(mut self) -> Self {
        self.mode = WindowMode::Fullscreen;
        self
    }

    /// Sets the window to borderless
    pub fn borderless(mut self) -> Self {
        self.mode = WindowMode::Borderless;
        self
    }

    /// Sets the window to normal
    pub fn windowed(mut self) -> Self {
        self.mode = WindowMode::Windowed;
        self
    }

    /// Sets the window dimensions to the dimensions of the monitor
    pub fn native(mut self) -> Self {
        self.native = true;
        self
    }

    pub fn title(mut self, title: &str) -> Self {
        self.title = title.into();
        self
    }

    /// Sets wether the window should be able to resize or not
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Creates the window
    /// Is the same as doing Window::new(spec)
    pub fn create(&self) -> Result<Window, Error> {
        Window::new(&self)
    }
}

pub fn map_event(event: (f64, WindowEvent)) -> WindowEvent {
    event.1
}