speedy2d 1.9.0

Hardware-accelerated drawing of shapes, images, and text, with an easy to use API. Speedy2D aims to be the simplest Rust API for creating a window, rendering graphics, and handling input.
Documentation
/*
 *  Copyright 2021 QuantumBadger
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

use std::fmt::{Display, Formatter};
use std::marker::PhantomData;

use crate::dimen::{IVec2, UVec2, Vec2};
use crate::error::{BacktraceError, ErrorMessage};
use crate::{GLRenderer, Graphics2D};

#[cfg(all(not(target_arch = "wasm32"), not(any(doc, doctest))))]
type WindowHelperInnerType<UserEventType> =
    crate::window_internal_glutin::WindowHelperGlutin<UserEventType>;

#[cfg(all(not(target_arch = "wasm32"), not(any(doc, doctest))))]
type UserEventSenderInnerType<UserEventType> =
    crate::window_internal_glutin::UserEventSenderGlutin<UserEventType>;

#[cfg(all(target_arch = "wasm32", not(any(doc, doctest))))]
type WindowHelperInnerType<UserEventType> =
    crate::window_internal_web::WindowHelperWeb<UserEventType>;

#[cfg(all(target_arch = "wasm32", not(any(doc, doctest))))]
type UserEventSenderInnerType<UserEventType> =
    crate::window_internal_web::UserEventSenderWeb<UserEventType>;

#[cfg(any(doc, doctest))]
type WindowHelperInnerType<UserEventType> = PhantomData<UserEventType>;

#[cfg(any(doc, doctest))]
type UserEventSenderInnerType<UserEventType> = PhantomData<UserEventType>;

/// Error occurring when sending a user event.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Copy)]
pub enum EventLoopSendError
{
    /// Send failed as the event loop no longer exists.
    EventLoopNoLongerExists
}

/// Allows user events to be sent to the event loop from other threads.
#[derive(Clone)]
pub struct UserEventSender<UserEventType: 'static>
{
    inner: UserEventSenderInnerType<UserEventType>
}

impl<UserEventType> UserEventSender<UserEventType>
{
    pub(crate) fn new(inner: UserEventSenderInnerType<UserEventType>) -> Self
    {
        Self { inner }
    }

    /// Sends a user-defined event to the event loop. This will cause
    /// [WindowHandler::on_user_event] to be invoked on the event loop
    /// thread.
    ///
    /// This may be invoked from a different thread to the one running the event
    /// loop.
    #[inline]
    pub fn send_event(&self, event: UserEventType) -> Result<(), EventLoopSendError>
    {
        self.inner.send_event(event)
    }
}

/// Error occurring when creating a window.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum WindowCreationError
{
    /// Could not find the primary monitor.
    PrimaryMonitorNotFound,
    /// Could not find a suitable graphics context. Speedy2D attempts to find
    /// the best possible context configuration by trying multiple options for
    /// vsync and multisampling.
    SuitableContextNotFound,
    /// Failed to make the graphics context current.
    MakeContextCurrentFailed,
    /// Failed to instantiate the renderer.
    RendererCreationFailed
}

impl Display for WindowCreationError
{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
    {
        match self {
            WindowCreationError::PrimaryMonitorNotFound => {
                f.write_str("Primary monitor not found")
            }
            WindowCreationError::SuitableContextNotFound => {
                f.write_str("Could not find a suitable graphics context")
            }
            WindowCreationError::MakeContextCurrentFailed => {
                f.write_str("Failed to make the graphics context current")
            }
            WindowCreationError::RendererCreationFailed => {
                f.write_str("Failed to create the renderer")
            }
        }
    }
}

/// A set of callbacks for an active window. If a callback is not implemented,
/// it will do nothing by default, so it is only necessary to implement the
/// callbacks you actually need.
pub trait WindowHandler<UserEventType = ()>
{
    /// Invoked once when the window first starts.
    #[allow(unused_variables)]
    #[inline]
    fn on_start(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        info: WindowStartupInfo
    )
    {
    }

    /// Invoked when a user-defined event is received, allowing you to wake up
    /// the event loop to handle events from other threads.
    ///
    /// See [WindowHelper::create_user_event_sender].
    #[allow(unused_variables)]
    #[inline]
    fn on_user_event(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        user_event: UserEventType
    )
    {
    }

    /// Invoked when the window is resized.
    #[allow(unused_variables)]
    #[inline]
    fn on_resize(&mut self, helper: &mut WindowHelper<UserEventType>, size_pixels: UVec2)
    {
    }

    /// Invoked if the mouse cursor becomes grabbed or un-grabbed. See
    /// [WindowHelper::set_cursor_grab].
    ///
    /// Note: mouse movement events will behave differently depending on the
    /// current cursor grabbing status.
    #[allow(unused_variables)]
    #[inline]
    fn on_mouse_grab_status_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        mouse_grabbed: bool
    )
    {
    }

    /// Invoked if the window enters or exits fullscreen mode. See
    /// [WindowHelper::set_fullscreen_mode].
    #[allow(unused_variables)]
    #[inline]
    fn on_fullscreen_status_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        fullscreen: bool
    )
    {
    }

    /// Invoked when the window scale factor changes.
    #[allow(unused_variables)]
    #[inline]
    fn on_scale_factor_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        scale_factor: f64
    )
    {
    }

    /// Invoked when the contents of the window needs to be redrawn.
    ///
    /// It is possible to request a redraw from any callback using
    /// [WindowHelper::request_redraw].
    #[allow(unused_variables)]
    #[inline]
    fn on_draw(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        graphics: &mut Graphics2D
    )
    {
    }

    /// Invoked when the mouse changes position.
    ///
    /// Normally, this provides the absolute  position of the mouse in the
    /// window/canvas. However, if the mouse cursor is grabbed, this will
    /// instead provide the amount of relative movement since the last move
    /// event.
    ///
    /// See [WindowHandler::on_mouse_grab_status_changed].
    #[allow(unused_variables)]
    #[inline]
    fn on_mouse_move(&mut self, helper: &mut WindowHelper<UserEventType>, position: Vec2)
    {
    }

    /// Invoked when a mouse button is pressed.
    #[allow(unused_variables)]
    #[inline]
    fn on_mouse_button_down(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        button: MouseButton
    )
    {
    }

    /// Invoked when a mouse button is released.
    #[allow(unused_variables)]
    #[inline]
    fn on_mouse_button_up(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        button: MouseButton
    )
    {
    }

    /// Invoked when the mouse wheel moves.
    #[allow(unused_variables)]
    #[inline]
    fn on_mouse_wheel_scroll(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        distance: MouseScrollDistance
    )
    {
    }

    /// Invoked when a keyboard key is pressed.
    ///
    /// To detect when a character is typed, see the
    /// [WindowHandler::on_keyboard_char] callback.
    #[allow(unused_variables)]
    #[inline]
    fn on_key_down(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        virtual_key_code: Option<VirtualKeyCode>,
        scancode: KeyScancode
    )
    {
    }

    /// Invoked when a keyboard key is released.
    #[allow(unused_variables)]
    #[inline]
    fn on_key_up(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        virtual_key_code: Option<VirtualKeyCode>,
        scancode: KeyScancode
    )
    {
    }

    /// Invoked when a character is typed on the keyboard.
    ///
    /// This is invoked in addition to the [WindowHandler::on_key_up] and
    /// [WindowHandler::on_key_down] callbacks.
    #[allow(unused_variables)]
    #[inline]
    fn on_keyboard_char(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        unicode_codepoint: char
    )
    {
    }

    /// Invoked when the state of the modifier keys has changed.
    #[allow(unused_variables)]
    #[inline]
    fn on_keyboard_modifiers_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        state: ModifiersState
    )
    {
    }
}

pub(crate) struct DrawingWindowHandler<UserEventType, H>
where
    UserEventType: 'static,
    H: WindowHandler<UserEventType>
{
    window_handler: H,
    renderer: GLRenderer,
    phantom: PhantomData<UserEventType>
}

impl<UserEventType, H> DrawingWindowHandler<UserEventType, H>
where
    H: WindowHandler<UserEventType>,
    UserEventType: 'static
{
    pub fn new(window_handler: H, renderer: GLRenderer) -> Self
    {
        DrawingWindowHandler {
            window_handler,
            renderer,
            phantom: PhantomData::default()
        }
    }

    #[inline]
    pub fn on_start(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        info: WindowStartupInfo
    )
    {
        self.window_handler.on_start(helper, info);
    }

    #[inline]
    pub fn on_user_event(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        user_event: UserEventType
    )
    {
        self.window_handler.on_user_event(helper, user_event)
    }

    #[inline]
    pub fn on_resize(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        size_pixels: UVec2
    )
    {
        self.renderer.set_viewport_size_pixels(size_pixels);
        self.window_handler.on_resize(helper, size_pixels)
    }

    #[inline]
    pub fn on_mouse_grab_status_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        mouse_grabbed: bool
    )
    {
        self.window_handler
            .on_mouse_grab_status_changed(helper, mouse_grabbed)
    }

    #[inline]
    pub fn on_fullscreen_status_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        fullscreen: bool
    )
    {
        self.window_handler
            .on_fullscreen_status_changed(helper, fullscreen)
    }

    #[inline]
    pub fn on_scale_factor_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        scale_factor: f64
    )
    {
        self.window_handler
            .on_scale_factor_changed(helper, scale_factor)
    }

    #[inline]
    pub fn on_draw(&mut self, helper: &mut WindowHelper<UserEventType>)
    {
        let renderer = &mut self.renderer;
        let window_handler = &mut self.window_handler;

        renderer.draw_frame(|graphics| window_handler.on_draw(helper, graphics))
    }

    #[inline]
    pub fn on_mouse_move(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        position: Vec2
    )
    {
        self.window_handler.on_mouse_move(helper, position)
    }

    #[inline]
    pub fn on_mouse_button_down(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        button: MouseButton
    )
    {
        self.window_handler.on_mouse_button_down(helper, button)
    }

    #[inline]
    pub fn on_mouse_button_up(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        button: MouseButton
    )
    {
        self.window_handler.on_mouse_button_up(helper, button)
    }

    #[inline]
    pub fn on_mouse_wheel_scroll(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        distance: MouseScrollDistance
    )
    {
        self.window_handler.on_mouse_wheel_scroll(helper, distance)
    }

    #[inline]
    pub fn on_key_down(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        virtual_key_code: Option<VirtualKeyCode>,
        scancode: KeyScancode
    )
    {
        self.window_handler
            .on_key_down(helper, virtual_key_code, scancode)
    }

    #[inline]
    pub fn on_key_up(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        virtual_key_code: Option<VirtualKeyCode>,
        scancode: KeyScancode
    )
    {
        self.window_handler
            .on_key_up(helper, virtual_key_code, scancode)
    }

    #[inline]
    pub fn on_keyboard_char(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        unicode_codepoint: char
    )
    {
        self.window_handler
            .on_keyboard_char(helper, unicode_codepoint)
    }

    #[inline]
    pub fn on_keyboard_modifiers_changed(
        &mut self,
        helper: &mut WindowHelper<UserEventType>,
        state: ModifiersState
    )
    {
        self.window_handler
            .on_keyboard_modifiers_changed(helper, state)
    }
}

/// A set of helper methods to perform actions on a [crate::Window].
pub struct WindowHelper<UserEventType = ()>
where
    UserEventType: 'static
{
    inner: WindowHelperInnerType<UserEventType>
}

impl<UserEventType> WindowHelper<UserEventType>
{
    pub(crate) fn new(inner: WindowHelperInnerType<UserEventType>) -> Self
    {
        WindowHelper { inner }
    }

    #[inline]
    #[must_use]
    pub(crate) fn inner(&mut self) -> &mut WindowHelperInnerType<UserEventType>
    {
        &mut self.inner
    }

    /// Causes the event loop to stop processing events, and terminate the
    /// application.
    ///
    /// Note: The event loop will stop only once the current callback has
    /// returned, rather than terminating immediately.
    ///
    /// Once the event loop has stopped, the entire process will end with error
    /// code 0, even if other threads are running.
    ///
    /// If your `WindowHandler` struct implements `Drop`, it will be safely
    /// destructed before exiting.
    ///
    /// No further callbacks will be given once this function has been called.
    pub fn terminate_loop(&mut self)
    {
        self.inner.terminate_loop()
    }

    /// Sets the window icon from the provided RGBA pixels.
    ///
    /// On Windows, the base icon size is 16x16, however a multiple of this
    /// (e.g. 32x32) should be provided for high-resolution displays.
    ///
    /// For `WebCanvas`, this function has no effect.
    pub fn set_icon_from_rgba_pixels<S>(
        &self,
        data: Vec<u8>,
        size: S
    ) -> Result<(), BacktraceError<ErrorMessage>>
    where
        S: Into<UVec2>
    {
        self.inner.set_icon_from_rgba_pixels(data, size.into())
    }

    /// Sets the visibility of the mouse cursor.
    pub fn set_cursor_visible(&self, visible: bool)
    {
        self.inner.set_cursor_visible(visible)
    }

    /// Grabs the cursor, preventing it from leaving the window.
    pub fn set_cursor_grab(
        &self,
        grabbed: bool
    ) -> Result<(), BacktraceError<ErrorMessage>>
    {
        self.inner.set_cursor_grab(grabbed)
    }

    /// Set to false to prevent the user from resizing the window.
    ///
    /// For `WebCanvas`, this function has no effect.
    pub fn set_resizable(&self, resizable: bool)
    {
        self.inner.set_resizable(resizable)
    }

    /// Request that the window is redrawn.
    ///
    /// This will cause the [WindowHandler::on_draw] callback to be invoked on
    /// the next frame.
    #[inline]
    pub fn request_redraw(&self)
    {
        self.inner.request_redraw()
    }

    /// Sets the window title.
    pub fn set_title<S: AsRef<str>>(&self, title: S)
    {
        self.inner.set_title(title.as_ref())
    }

    /// Sets the window fullscreen mode.
    ///
    /// When using a web canvas, permission for this operation may be denied,
    /// depending on where this is called from, and the user's browser settings.
    /// If the operation is successful, the
    /// [WindowHandler::on_fullscreen_status_changed] callback will be invoked.
    pub fn set_fullscreen_mode(&self, mode: WindowFullscreenMode)
    {
        self.inner.set_fullscreen_mode(mode)
    }

    /// Sets the window size in pixels. This is the window's inner size,
    /// excluding the border.
    ///
    /// For `WebCanvas`, this function has no effect.
    pub fn set_size_pixels<S: Into<UVec2>>(&self, size: S)
    {
        self.inner.set_size_pixels(size)
    }

    /// Sets the position of the window in pixels. If multiple monitors are in
    /// use, this will be the distance from the top left of the display
    /// area, spanning all the monitors.
    ///
    /// For `WebCanvas`, this function has no effect.
    pub fn set_position_pixels<P: Into<IVec2>>(&self, position: P)
    {
        self.inner.set_position_pixels(position)
    }

    /// Sets the window size in scaled device-independent pixels. This is the
    /// window's inner size, excluding the border.
    ///
    /// For `WebCanvas`, this function has no effect.
    pub fn set_size_scaled_pixels<S: Into<Vec2>>(&self, size: S)
    {
        self.inner.set_size_scaled_pixels(size)
    }

    /// Sets the position of the window in scaled device-independent pixels. If
    /// multiple monitors are in use, this will be the distance from the top
    /// left of the display area, spanning all the monitors.
    ///
    /// For `WebCanvas`, this function has no effect.
    pub fn set_position_scaled_pixels<P: Into<Vec2>>(&self, position: P)
    {
        self.inner.set_position_scaled_pixels(position)
    }

    /// Gets the window's scale factor.
    #[inline]
    #[must_use]
    pub fn get_scale_factor(&self) -> f64
    {
        self.inner.get_scale_factor()
    }

    /// Creates a [UserEventSender], which can be used to post custom events to
    /// this event loop from another thread.
    ///
    /// See [UserEventSender::send_event], [WindowHandler::on_user_event].
    pub fn create_user_event_sender(&self) -> UserEventSender<UserEventType>
    {
        self.inner.create_user_event_sender()
    }
}

#[cfg(any(doc, doctest, not(target_arch = "wasm32")))]
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[must_use]
pub(crate) enum WindowEventLoopAction
{
    /// Continue running the event loop.
    Continue,

    /// Stops the event loop. This will cause the entire process to end with
    /// error code 0, even if other threads are running.
    ///
    /// No further callbacks will be given once a handler has returned this
    /// value. The handler itself will be dropped before exiting.
    Exit
}

/// Information about the starting state of the window.
#[derive(Debug, PartialEq, Clone)]
pub struct WindowStartupInfo
{
    viewport_size_pixels: UVec2,
    scale_factor: f64
}

impl WindowStartupInfo
{
    pub(crate) fn new(viewport_size_pixels: UVec2, scale_factor: f64) -> Self
    {
        WindowStartupInfo {
            viewport_size_pixels,
            scale_factor
        }
    }

    /// The scale factor of the window. When a high-dpi display is in use,
    /// this will be greater than `1.0`.
    pub fn scale_factor(&self) -> f64
    {
        self.scale_factor
    }

    /// The size of the viewport in pixels.
    pub fn viewport_size_pixels(&self) -> &UVec2
    {
        &self.viewport_size_pixels
    }
}

/// Identifies a mouse button.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum MouseButton
{
    /// The left mouse button.
    Left,
    /// The middle mouse button.
    Middle,
    /// The right mouse button.
    Right,
    /// Another mouse button, identified by a number.
    Other(u16)
}

/// Describes a difference in the mouse scroll wheel position.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum MouseScrollDistance
{
    /// Number of lines or rows to scroll in each direction. The `y` field
    /// represents the vertical scroll direction on a typical mouse wheel.
    Lines
    {
        /// The horizontal scroll distance. Negative values indicate scrolling
        /// left, and positive values indicate scrolling right.
        x: f64,
        /// The vertical scroll distance. Negative values indicate scrolling
        /// down, and positive values indicate scrolling up.
        y: f64,
        /// The forward/backward scroll distance, supported on some 3D mice.
        z: f64
    },
    /// Number of pixels to scroll in each direction. Scroll events are
    /// expressed in pixels if supported by the device (eg. a touchpad) and
    /// platform. The `y` field represents the vertical scroll direction on a
    /// typical mouse wheel.
    Pixels
    {
        /// The horizontal scroll distance. Negative values indicate scrolling
        /// left, and positive values indicate scrolling right.
        x: f64,
        /// The vertical scroll distance. Negative values indicate scrolling
        /// down, and positive values indicate scrolling up.
        y: f64,
        /// The forward/backward scroll distance, supported on some 3D mice.
        z: f64
    },
    /// Number of pages to scroll in each direction (only supported for
    /// WebCanvas). The `y` field represents the vertical scroll direction on a
    /// typical mouse wheel.
    Pages
    {
        /// The horizontal scroll distance. Negative values indicate scrolling
        /// left, and positive values indicate scrolling right.
        x: f64,
        /// The vertical scroll distance. Negative values indicate scrolling
        /// down, and positive values indicate scrolling up.
        y: f64,
        /// The forward/backward scroll distance, supported on some 3D mice.
        z: f64
    }
}

/// A virtual key code.
#[allow(missing_docs)]
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
pub enum VirtualKeyCode
{
    Key1,
    Key2,
    Key3,
    Key4,
    Key5,
    Key6,
    Key7,
    Key8,
    Key9,
    Key0,

    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,

    Escape,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,

    PrintScreen,
    ScrollLock,
    PauseBreak,

    Insert,
    Home,
    Delete,
    End,
    PageDown,
    PageUp,

    Left,
    Up,
    Right,
    Down,

    Backspace,
    Return,
    Space,

    Compose,

    Caret,

    Numlock,
    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,
    NumpadAdd,
    NumpadDivide,
    NumpadDecimal,
    NumpadComma,
    NumpadEnter,
    NumpadEquals,
    NumpadMultiply,
    NumpadSubtract,

    AbntC1,
    AbntC2,
    Apostrophe,
    Apps,
    Asterisk,
    At,
    Ax,
    Backslash,
    Calculator,
    Capital,
    Colon,
    Comma,
    Convert,
    Equals,
    Grave,
    Kana,
    Kanji,
    LAlt,
    LBracket,
    LControl,
    LShift,
    LWin,
    Mail,
    MediaSelect,
    MediaStop,
    Minus,
    Mute,
    MyComputer,
    NavigateForward,
    NavigateBackward,
    NextTrack,
    NoConvert,
    OEM102,
    Period,
    PlayPause,
    Plus,
    Power,
    PrevTrack,
    RAlt,
    RBracket,
    RControl,
    RShift,
    RWin,
    Semicolon,
    Slash,
    Sleep,
    Stop,
    Sysrq,
    Tab,
    Underline,
    Unlabeled,
    VolumeDown,
    VolumeUp,
    Wake,
    WebBack,
    WebFavorites,
    WebForward,
    WebHome,
    WebRefresh,
    WebSearch,
    WebStop,
    Yen,
    Copy,
    Paste,
    Cut
}

/// The state of the modifier keys.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Default)]
pub struct ModifiersState
{
    pub(crate) ctrl: bool,
    pub(crate) alt: bool,
    pub(crate) shift: bool,
    pub(crate) logo: bool
}

impl ModifiersState
{
    /// This is true if the CTRL key is pressed.
    #[inline]
    #[must_use]
    pub fn ctrl(&self) -> bool
    {
        self.ctrl
    }

    /// This is true if the ALT key is pressed.
    #[inline]
    #[must_use]
    pub fn alt(&self) -> bool
    {
        self.alt
    }

    /// This is true if the SHIFT key is pressed.
    #[inline]
    #[must_use]
    pub fn shift(&self) -> bool
    {
        self.shift
    }

    /// This is true if the logo key is pressed (normally the Windows key).
    #[inline]
    #[must_use]
    pub fn logo(&self) -> bool
    {
        self.logo
    }
}

/// Configuration options about the mode in which the window should be created,
/// for example fullscreen or windowed.
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum WindowCreationMode
{
    /// Create the window in non-fullscreen mode.
    Windowed
    {
        /// The size of the window.
        size: WindowSize,

        /// The position of the window.
        position: Option<WindowPosition>
    },

    /// Create the window in fullscreen borderless mode.
    FullscreenBorderless
}

/// The size of the window to create.
#[derive(Debug, PartialEq, Clone)]
pub enum WindowSize
{
    /// Define the window size in pixels.
    PhysicalPixels(UVec2),
    /// Define the window size in device-independent scaled pixels.
    ScaledPixels(Vec2),
    /// Make the window fill the screen, except for a margin around the outer
    /// edges.
    MarginPhysicalPixels(u32),
    /// Make the window fill the screen, except for a margin around the outer
    /// edges.
    MarginScaledPixels(f32)
}

/// The position of the window to create.
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum WindowPosition
{
    /// Place the window in the center of the primary monitor.
    Center,
    /// Place the window at the specified pixel location from the top left of
    /// the primary monitor.
    PrimaryMonitorPixelsFromTopLeft(IVec2)
}

/// Whether or not the window is in fullscreen mode.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum WindowFullscreenMode
{
    /// Non-fullscreen mode.
    Windowed,
    /// Fullscreen borderless mode.
    FullscreenBorderless
}

/// Options used during the creation of a window.
#[derive(Debug, Clone, PartialEq)]
pub struct WindowCreationOptions
{
    pub(crate) mode: WindowCreationMode,
    pub(crate) multisampling: u16,
    pub(crate) vsync: bool,
    pub(crate) always_on_top: bool,
    pub(crate) resizable: bool,
    pub(crate) maximized: bool,
    pub(crate) transparent: bool,
    pub(crate) decorations: bool
}

impl WindowCreationOptions
{
    /// Instantiates a new `WindowCreationOptions` structure with the default
    /// options, in non-fullscreen mode.
    pub fn new_windowed(size: WindowSize, position: Option<WindowPosition>) -> Self
    {
        Self::new(WindowCreationMode::Windowed { size, position })
    }

    /// Instantiates a new `WindowCreationOptions` structure with the default
    /// options, in borderless fullscreen mode.
    #[inline]
    #[must_use]
    pub fn new_fullscreen_borderless() -> Self
    {
        Self::new(WindowCreationMode::FullscreenBorderless)
    }

    #[inline]
    #[must_use]
    fn new(mode: WindowCreationMode) -> Self
    {
        WindowCreationOptions {
            mode,
            multisampling: 16,
            vsync: true,
            always_on_top: false,
            resizable: true,
            maximized: false,
            decorations: true,
            transparent: false
        }
    }

    /// Sets the maximum level of multisampling which will be applied. By
    /// default this is set to `16`.
    ///
    /// Note that this depends on platform support, and setting this may have no
    /// effect.
    #[inline]
    #[must_use]
    pub fn with_multisampling(mut self, multisampling: u16) -> Self
    {
        self.multisampling = multisampling;
        self
    }

    /// Sets whether or not vsync should be enabled. This can increase latency,
    /// but should eliminate tearing. By default this is set to `true`.
    ///
    /// Note that this depends on platform support, and setting this may have no
    /// effect.
    #[inline]
    #[must_use]
    pub fn with_vsync(mut self, vsync: bool) -> Self
    {
        self.vsync = vsync;
        self
    }

    /// Sets whether or not the window can be resized by the user. The default
    /// is `true`.
    #[inline]
    #[must_use]
    pub fn with_resizable(mut self, resizable: bool) -> Self
    {
        self.resizable = resizable;
        self
    }

    /// If set to `true`, the window will be placed above other windows. The
    /// default is `false`.
    #[inline]
    #[must_use]
    pub fn with_always_on_top(mut self, always_on_top: bool) -> Self
    {
        self.always_on_top = always_on_top;
        self
    }

    /// If set to `true`, the window will be initially maximized. The default is
    /// `false`.
    #[inline]
    #[must_use]
    pub fn with_maximized(mut self, maximized: bool) -> Self
    {
        self.maximized = maximized;
        self
    }

    /// If set to `false`, the window will have no border.  The default is
    /// `true`.
    #[inline]
    #[must_use]
    pub fn with_decorations(mut self, decorations: bool) -> Self
    {
        self.decorations = decorations;
        self
    }

    /// Sets whether the background of the window should be transparent. The
    /// default is `false`.
    ///
    /// Note that this depends on platform support, and setting this may have no
    /// effect.
    #[inline]
    #[must_use]
    pub fn with_transparent(mut self, transparent: bool) -> Self
    {
        self.transparent = transparent;
        self
    }
}

/// Type representing a keyboard scancode.
pub type KeyScancode = u32;