glfw 0.13.0

GLFW3 bindings and idiomatic wrapper for Rust.
Documentation
// Copyright 2013-2016 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#![crate_type = "lib"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![crate_name = "glfw"]

#![allow(non_upper_case_globals)]

//! An idiomatic wrapper for the GLFW library.
//!
//! # Example
//!
//! ~~~no_run
//! extern crate glfw;
//!
//! use glfw::{Action, Context, Key};
//!
//! fn main() {
//!    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
//!
//!     // Create a windowed mode window and its OpenGL context
//!     let (mut window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::WindowMode::Windowed)
//!         .expect("Failed to create GLFW window.");
//!
//!     // Make the window's context current
//!     window.make_current();
//!     window.set_key_polling(true);
//!
//!     // Loop until the user closes the window
//!     while !window.should_close() {
//!         // Swap front and back buffers
//!         window.swap_buffers();
//!
//!         // Poll for and process events
//!         glfw.poll_events();
//!         for (_, event) in glfw::flush_messages(&events) {
//!             println!("{:?}", event);
//!             match event {
//!                 glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
//!                     window.set_should_close(true)
//!                 },
//!                 _ => {},
//!             }
//!         }
//!     }
//! }
//! ~~~
//!
//! # Cargo Features
//!
//! Use the `vulkan` feature flag to enable all Vulkan functions and types.
//!
//! Use the `image` feature flag to enable use of the [`image`](https://github.com/PistonDevelopers/image) library for cursors and icons.
//!
//! Use the `all` feature flag to enable both at the same time.
//!

// TODO: Document differences between GLFW and glfw-rs

extern crate semver;
extern crate libc;
#[cfg(feature = "vulkan")]
extern crate vk_sys;
#[macro_use]
extern crate log;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate enum_primitive;
extern crate num;
#[cfg(feature = "image")]
extern crate image;

use libc::{c_char, c_double, c_float, c_int};
use libc::{c_ushort, c_void, c_uchar};
#[cfg(feature = "vulkan")]
use libc::c_uint;
use std::ffi::{CStr, CString};
use std::mem;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::fmt;
use std::error;
use std::marker::Send;
use std::ptr;
use std::slice;
use std::path::PathBuf;
use semver::Version;

#[cfg(feature = "vulkan")]
use vk_sys::{
    self as vk,
    Instance as VkInstance,
    PhysicalDevice as VkPhysicalDevice
};

/// Alias to `MouseButton1`, supplied for improved clarity.
pub use self::MouseButton::Button1 as MouseButtonLeft;
/// Alias to `MouseButton2`, supplied for improved clarity.
pub use self::MouseButton::Button2 as MouseButtonRight;
/// Alias to `MouseButton3`, supplied for improved clarity.
pub use self::MouseButton::Button3 as MouseButtonMiddle;

pub mod ffi;
mod callbacks;

/// Input actions.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Action {
    Release                      = ffi::RELEASE,
    Press                        = ffi::PRESS,
    Repeat                       = ffi::REPEAT,
}

enum_from_primitive! {
/// Input keys.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Key {
    Space                    = ffi::KEY_SPACE,
    Apostrophe               = ffi::KEY_APOSTROPHE,
    Comma                    = ffi::KEY_COMMA,
    Minus                    = ffi::KEY_MINUS,
    Period                   = ffi::KEY_PERIOD,
    Slash                    = ffi::KEY_SLASH,
    Num0                     = ffi::KEY_0,
    Num1                     = ffi::KEY_1,
    Num2                     = ffi::KEY_2,
    Num3                     = ffi::KEY_3,
    Num4                     = ffi::KEY_4,
    Num5                     = ffi::KEY_5,
    Num6                     = ffi::KEY_6,
    Num7                     = ffi::KEY_7,
    Num8                     = ffi::KEY_8,
    Num9                     = ffi::KEY_9,
    Semicolon                = ffi::KEY_SEMICOLON,
    Equal                    = ffi::KEY_EQUAL,
    A                        = ffi::KEY_A,
    B                        = ffi::KEY_B,
    C                        = ffi::KEY_C,
    D                        = ffi::KEY_D,
    E                        = ffi::KEY_E,
    F                        = ffi::KEY_F,
    G                        = ffi::KEY_G,
    H                        = ffi::KEY_H,
    I                        = ffi::KEY_I,
    J                        = ffi::KEY_J,
    K                        = ffi::KEY_K,
    L                        = ffi::KEY_L,
    M                        = ffi::KEY_M,
    N                        = ffi::KEY_N,
    O                        = ffi::KEY_O,
    P                        = ffi::KEY_P,
    Q                        = ffi::KEY_Q,
    R                        = ffi::KEY_R,
    S                        = ffi::KEY_S,
    T                        = ffi::KEY_T,
    U                        = ffi::KEY_U,
    V                        = ffi::KEY_V,
    W                        = ffi::KEY_W,
    X                        = ffi::KEY_X,
    Y                        = ffi::KEY_Y,
    Z                        = ffi::KEY_Z,
    LeftBracket              = ffi::KEY_LEFT_BRACKET,
    Backslash                = ffi::KEY_BACKSLASH,
    RightBracket             = ffi::KEY_RIGHT_BRACKET,
    GraveAccent              = ffi::KEY_GRAVE_ACCENT,
    World1                   = ffi::KEY_WORLD_1,
    World2                   = ffi::KEY_WORLD_2,

    Escape                   = ffi::KEY_ESCAPE,
    Enter                    = ffi::KEY_ENTER,
    Tab                      = ffi::KEY_TAB,
    Backspace                = ffi::KEY_BACKSPACE,
    Insert                   = ffi::KEY_INSERT,
    Delete                   = ffi::KEY_DELETE,
    Right                    = ffi::KEY_RIGHT,
    Left                     = ffi::KEY_LEFT,
    Down                     = ffi::KEY_DOWN,
    Up                       = ffi::KEY_UP,
    PageUp                   = ffi::KEY_PAGE_UP,
    PageDown                 = ffi::KEY_PAGE_DOWN,
    Home                     = ffi::KEY_HOME,
    End                      = ffi::KEY_END,
    CapsLock                 = ffi::KEY_CAPS_LOCK,
    ScrollLock               = ffi::KEY_SCROLL_LOCK,
    NumLock                  = ffi::KEY_NUM_LOCK,
    PrintScreen              = ffi::KEY_PRINT_SCREEN,
    Pause                    = ffi::KEY_PAUSE,
    F1                       = ffi::KEY_F1,
    F2                       = ffi::KEY_F2,
    F3                       = ffi::KEY_F3,
    F4                       = ffi::KEY_F4,
    F5                       = ffi::KEY_F5,
    F6                       = ffi::KEY_F6,
    F7                       = ffi::KEY_F7,
    F8                       = ffi::KEY_F8,
    F9                       = ffi::KEY_F9,
    F10                      = ffi::KEY_F10,
    F11                      = ffi::KEY_F11,
    F12                      = ffi::KEY_F12,
    F13                      = ffi::KEY_F13,
    F14                      = ffi::KEY_F14,
    F15                      = ffi::KEY_F15,
    F16                      = ffi::KEY_F16,
    F17                      = ffi::KEY_F17,
    F18                      = ffi::KEY_F18,
    F19                      = ffi::KEY_F19,
    F20                      = ffi::KEY_F20,
    F21                      = ffi::KEY_F21,
    F22                      = ffi::KEY_F22,
    F23                      = ffi::KEY_F23,
    F24                      = ffi::KEY_F24,
    F25                      = ffi::KEY_F25,
    Kp0                      = ffi::KEY_KP_0,
    Kp1                      = ffi::KEY_KP_1,
    Kp2                      = ffi::KEY_KP_2,
    Kp3                      = ffi::KEY_KP_3,
    Kp4                      = ffi::KEY_KP_4,
    Kp5                      = ffi::KEY_KP_5,
    Kp6                      = ffi::KEY_KP_6,
    Kp7                      = ffi::KEY_KP_7,
    Kp8                      = ffi::KEY_KP_8,
    Kp9                      = ffi::KEY_KP_9,
    KpDecimal                = ffi::KEY_KP_DECIMAL,
    KpDivide                 = ffi::KEY_KP_DIVIDE,
    KpMultiply               = ffi::KEY_KP_MULTIPLY,
    KpSubtract               = ffi::KEY_KP_SUBTRACT,
    KpAdd                    = ffi::KEY_KP_ADD,
    KpEnter                  = ffi::KEY_KP_ENTER,
    KpEqual                  = ffi::KEY_KP_EQUAL,
    LeftShift                = ffi::KEY_LEFT_SHIFT,
    LeftControl              = ffi::KEY_LEFT_CONTROL,
    LeftAlt                  = ffi::KEY_LEFT_ALT,
    LeftSuper                = ffi::KEY_LEFT_SUPER,
    RightShift               = ffi::KEY_RIGHT_SHIFT,
    RightControl             = ffi::KEY_RIGHT_CONTROL,
    RightAlt                 = ffi::KEY_RIGHT_ALT,
    RightSuper               = ffi::KEY_RIGHT_SUPER,
    Menu                     = ffi::KEY_MENU,
    Unknown                  = ffi::KEY_UNKNOWN
}
}

/// Wrapper around 'glfwGetKeyName`
pub fn key_name(key: Option<Key>, scancode: Option<Scancode>) -> String {
    unsafe {
        string_from_c_str(ffi::glfwGetKeyName(match key {
            Some(k) => k as c_int,
            None => ffi::KEY_UNKNOWN
        }, scancode.unwrap_or(ffi::KEY_UNKNOWN)))
    }
}

impl Key {
    /// Wrapper around 'glfwGetKeyName` without scancode
    pub fn name(&self) -> String {
        key_name(Some(*self), None)
    }
}

/// Mouse buttons. The `MouseButtonLeft`, `MouseButtonRight`, and
/// `MouseButtonMiddle` aliases are supplied for convenience.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum MouseButton {
    /// The left mouse button. A `MouseButtonLeft` alias is provided to improve clarity.
    Button1                = ffi::MOUSE_BUTTON_1,
    /// The right mouse button. A `MouseButtonRight` alias is provided to improve clarity.
    Button2                = ffi::MOUSE_BUTTON_2,
    /// The middle mouse button. A `MouseButtonMiddle` alias is provided to improve clarity.
    Button3                = ffi::MOUSE_BUTTON_3,
    Button4                = ffi::MOUSE_BUTTON_4,
    Button5                = ffi::MOUSE_BUTTON_5,
    Button6                = ffi::MOUSE_BUTTON_6,
    Button7                = ffi::MOUSE_BUTTON_7,
    Button8                = ffi::MOUSE_BUTTON_8,
}

// We can't use `enum_from_primitive!` for MouseButton due to
// https://github.com/andersk/enum_primitive-rs/issues/2
// We implement FromPrimitive manually instead.
impl num::FromPrimitive for MouseButton {
    fn from_i64(n: i64) -> Option<MouseButton> {
        use MouseButton::*;
        match n {
            0 => Some(Button1),
            1 => Some(Button2),
            2 => Some(Button3),
            3 => Some(Button4),
            4 => Some(Button5),
            5 => Some(Button6),
            6 => Some(Button7),
            7 => Some(Button8),
            _ => None
        }
    }
    fn from_u64(n: u64) -> Option<MouseButton> {
        Self::from_i64(n as i64)
    }
}

/// Formats the type using aliases rather than the default variant names.
///
/// # Example
///
/// ~~~ignore
/// assert_eq(format!("{}", glfw::MouseButtonLeft), "MouseButton1");
/// assert_eq(format!("{}", glfw::DebugAliases(glfw::MouseButtonLeft)), "MouseButtonLeft");
/// ~~~
pub struct DebugAliases<T>(pub T);

impl fmt::Debug for DebugAliases<MouseButton> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let DebugAliases(button) = *self;
        match button {
            MouseButtonLeft     => write!(f, "MouseButtonLeft"),
            MouseButtonRight    => write!(f, "MouseButtonRight"),
            MouseButtonMiddle   => write!(f, "MouseButtonMiddle"),
            button              => button.fmt(f),
        }
    }
}

#[derive(Copy, Clone)]
pub struct Callback<Fn, UserData> {
    pub f: Fn,
    pub data: UserData,
}

/// Tokens corresponding to various error types.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Error {
    NotInitialized              = ffi::NOT_INITIALIZED,
    NoCurrentContext            = ffi::NO_CURRENT_CONTEXT,
    InvalidEnum                 = ffi::INVALID_ENUM,
    InvalidValue                = ffi::INVALID_VALUE,
    OutOfMemory                 = ffi::OUT_OF_MEMORY,
    ApiUnavailable              = ffi::API_UNAVAILABLE,
    VersionUnavailable          = ffi::VERSION_UNAVAILABLE,
    PlatformError               = ffi::PLATFORM_ERROR,
    FormatUnavailable           = ffi::FORMAT_UNAVAILABLE,
    NoWindowContext             = ffi::NO_WINDOW_CONTEXT,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;

        f.write_str(self.description())
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::NotInitialized => "NotInitialized",
            Error::NoCurrentContext => "NoCurrentContext",
            Error::InvalidEnum => "InvalidEnum",
            Error::InvalidValue => "InvalidValue",
            Error::OutOfMemory => "OutOfMemory",
            Error::ApiUnavailable => "ApiUnavailable",
            Error::VersionUnavailable => "VersionUnavailable",
            Error::PlatformError => "PlatformError",
            Error::FormatUnavailable => "FormatUnavailable",
            Error::NoWindowContext => "NoWindowContext",
        }
    }
}

/// An error callback. This can be supplied with some user data to be passed to
/// the callback function when it is triggered.
pub type ErrorCallback<UserData> = Callback<fn(Error, String, &UserData), UserData>;

/// The function to be used with the `FAIL_ON_ERRORS` callback.
pub fn fail_on_errors(_: Error, description: String, _: &()) {
    panic!("GLFW Error: {}", description);
}

/// A callback that triggers a task failure when an error is encountered.
pub static FAIL_ON_ERRORS: Option<ErrorCallback<()>> =
    Some(Callback { f: fail_on_errors as fn(Error, String, &()), data: () });

/// The function to be used with the `LOG_ERRORS` callback.
pub fn log_errors(_: Error, description: String, _: &()) {
    error!("GLFW Error: {}", description);
}

/// A callback that logs each error as it is encountered without triggering a
/// task failure.
pub static LOG_ERRORS: Option<ErrorCallback<()>> =
    Some(Callback { f: log_errors as fn(Error, String, &()), data: () });

/// When not using the `image` library, or if you just want to,
/// you can specify an image from its raw pixel data using this structure.
pub struct PixelImage {
    /// Width of the image in pixels
    pub width: u32,
    /// Height of the image in pixels
    pub height: u32,
    /// Pixels are 4 bytes each, one byte for each RGBA subpixel.
    pub pixels: Vec<u32>
}

/// Cursor modes.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum CursorMode {
    Normal                = ffi::CURSOR_NORMAL,
    Hidden                = ffi::CURSOR_HIDDEN,
    Disabled              = ffi::CURSOR_DISABLED,
}

/// Standard cursors provided by GLFW
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum StandardCursor {
    /// The regular arrow cursor shape.
    Arrow                 = ffi::ARROW_CURSOR,
    /// The text input I-beam cursor shape.
    IBeam                 = ffi::IBEAM_CURSOR,
    /// The crosshair shape.
    Crosshair             = ffi::CROSSHAIR_CURSOR,
    /// The hand shape.
    Hand                  = ffi::HAND_CURSOR,
    /// The horizontal resize arrow shape.
    HResize               = ffi::HRESIZE_CURSOR,
    /// The vertical resize arrow shape.
    VResize               = ffi::VRESIZE_CURSOR
}

/// Represents a window cursor that can be used to display any
/// of the standard cursors or load a custom cursor from an image.
///
/// Note that the cursor object has a lifetime and will not display
/// correctly after it has been dropped.
pub struct Cursor {
    ptr: *mut ffi::GLFWcursor
}

impl Drop for Cursor {
    fn drop(&mut self) {
        unsafe { ffi::glfwDestroyCursor(self.ptr) }
    }
}

impl Cursor {
    /// Create a new cursor using `glfwCreateStandardCursor`
    pub fn standard(cursor: StandardCursor) -> Cursor {
        Cursor {
            ptr: unsafe { ffi::glfwCreateStandardCursor(cursor as c_int) }
        }
    }

    /// Creates a new cursor from the image provided via `glfwCreateCursor`
    ///
    /// Note that the cursor image will be the same size as the image provided,
    /// so scaling it beforehand may be required.
    ///
    /// The cursor hotspot is specified in pixels, relative to the upper-left
    /// corner of the cursor image. Like all other coordinate systems in GLFW,
    /// the X-axis points to the right and the Y-axis points down.
    #[cfg(feature = "image")]
    pub fn create(image: image::RgbaImage, x_hotspot: u32, y_hotspot: u32) -> Cursor {
        let (width, height) = image.dimensions();

        let image_data = image.into_vec();

        let glfw_image = ffi::GLFWimage {
            width: width as c_int,
            height: height as c_int,
            pixels: image_data.as_ptr() as *const c_uchar
        };

        Cursor {
            ptr: unsafe { ffi::glfwCreateCursor(&glfw_image as *const ffi::GLFWimage, x_hotspot as c_int, y_hotspot as c_int) }
        }
    }

    /// Creates a new cursor from the `PixelImage` provided via `glfwCreateCursor`
    ///
    /// Note that the cursor image will be the same size as the image provided,
    /// so scaling it beforehand may be required.
    ///
    /// The cursor hotspot is specified in pixels, relative to the upper-left
    /// corner of the cursor image. Like all other coordinate systems in GLFW,
    /// the X-axis points to the right and the Y-axis points down.
    pub fn create_from_pixels(image: PixelImage, x_hotspot: u32, y_hotspot: u32) -> Cursor {
        let glfw_image = ffi::GLFWimage {
            width: image.width as c_int,
            height: image.height as c_int,
            pixels: image.pixels.as_ptr() as *const c_uchar
        };

        Cursor {
            ptr: unsafe { ffi::glfwCreateCursor(&glfw_image as *const ffi::GLFWimage, x_hotspot as c_int, y_hotspot as c_int) }
        }
    }
}

/// Describes a single video mode.
#[derive(Copy, Clone)]
pub struct VidMode {
    pub width:        u32,
    pub height:       u32,
    pub red_bits:     u32,
    pub green_bits:   u32,
    pub blue_bits:    u32,
    pub refresh_rate: u32,
}

/// Describes the gamma ramp of a monitor.
pub struct GammaRamp {
    pub red:    Vec<c_ushort>,
    pub green:  Vec<c_ushort>,
    pub blue:   Vec<c_ushort>,
}

/// `ContextReleaseBehavior` specifies the release behavior to be used by the context.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ContextReleaseBehavior {
    Any                   = ffi::ANY_RELEASE_BEHAVIOR,
    /// `Flush` tells the context to flush the pipeline whenever the context is released from being the current one.
    Flush                 = ffi::RELEASE_BEHAVIOR_FLUSH,
    /// `None` tells the context to NOT flush the pipeline on release
    None                  = ffi::RELEASE_BEHAVIOR_NONE
}

/// Specifies the API to use to create the context
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ContextCreationApi {
    Native                = ffi::NATIVE_CONTEXT_API,
    Egl                   = ffi::EGL_CONTEXT_API
}

/// Specifies how the context should handle swapping the buffers.
///
/// i.e. the number of screen updates to wait from the time
/// `glfwSwapBuffers`/`context.swap_buffers`
/// was called before swapping the buffers and returning.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum SwapInterval {
    /// Specifies no waits
    None,
    /// If either of the `WGL_EXT_swap_control_tear` and `GLX_EXT_swap_control_tear` extensions
    /// are enabled, allows the adaptively swap the frame. Sometimes called Adaptive V-sync
    Adaptive,
    /// Synchronizes the buffers every N frames. Set to 1 for V-sync
    Sync(u32)
}

/// An OpenGL process address.
pub type GLProc = ffi::GLFWglproc;

/// A Vulkan process address
#[cfg(feature = "vulkan")]
pub type VkProc = ffi::GLFWvkproc;

/// A token from which to call various GLFW functions. It can be obtained by
/// calling the `init` function. This cannot be sent to other tasks, and should
/// only be initialized on the main platform thread. Whilst this might make
/// performing some operations harder, this is to ensure thread safety is enforced
/// statically. The context can be safely cloned or implicitly copied if need be
/// for convenience.
#[derive(Copy, Clone)]
pub struct Glfw;

/// An error that might be returned when `glfw::init` is called.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum InitError {
    /// The library was already initialized.
    AlreadyInitialized,
    /// An internal error occurred when trying to initialize the library.
    Internal,
}

impl fmt::Display for InitError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;

        f.write_str(self.description())
    }
}

impl error::Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::AlreadyInitialized => "Already Initialized",
            InitError::Internal => "Internal Initialization Error",
        }
    }
}

/// Initializes the GLFW library. This must be called on the main platform
/// thread.
///
/// Wrapper for `glfwInit`.
///
/// # Error callback
///
/// An error callback can be set if desired. This allows for the handling of any
/// errors that occur during initialization. This can subsequently be changed
/// using the `Glfw::set_error_callback` function.
///
/// # Example
///
/// ~~~no_run
/// extern crate glfw;
///
/// fn main() {
///    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
/// }
/// ~~~
///
/// # Returns
///
/// - If initialization was successful a `Glfw` token will be returned along
///   with a `Receiver` from which errors can be intercepted.
/// - Subsequent calls to `init` will return `Err(AlreadyInitialized)`.
/// - If an initialization error occurred within the GLFW library
///   `Err(InternalInitError)` will be returned.
pub fn init<UserData: 'static>(mut callback: Option<ErrorCallback<UserData>>) -> Result<Glfw, InitError> {
    // Helper to convert unsafe extern "C" fn to (safe) extern "C" fn.
    extern "C" fn glfw_terminate() {
        unsafe {
            ffi::glfwTerminate();
        }
    }
    use std::sync::{Once, ONCE_INIT};
    static mut INIT: Once = ONCE_INIT;
    let mut result = Err(InitError::AlreadyInitialized);
    unsafe {
        INIT.call_once(|| {
            // Initialize the error callback if it was supplied. This is done
            // before `ffi::glfwInit` because errors could occur during
            // initialization.
            match callback.take() {
                Some(f) => callbacks::error::set(f),
                None    => callbacks::error::unset(),
            }
            if ffi::glfwInit() == ffi::TRUE {
                result = Ok(());
                // TODO: When (if?) std::rt::at_exit() stabilizes, prefer to use it.
                libc::atexit(glfw_terminate);
            } else {
                result = Err(InitError::Internal);
            }
        })
    }
    result.map(|_| Glfw)
}

impl Glfw {
    /// Sets the error callback, overwriting the previous one stored.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// use std::cell::Cell;
    ///
    /// fn error_callback(_: glfw::Error, description: String, error_count: &Cell<usize>) {
    ///     println!("GLFW error {}: {}", error_count.get(), description);
    ///     error_count.set(error_count.get() + 1);
    /// }
    ///
    /// // sets a new callback
    /// glfw.set_error_callback(Some(
    ///     glfw::Callback {
    ///         f: error_callback,
    ///         data: Cell::new(0),
    ///     }
    /// ));
    ///
    /// // removes the previously set callback
    /// glfw.set_error_callback(None);
    /// ~~~
    ///
    /// The `FAIL_ON_ERRORS` and `LOG_ERRORS` callbacks are provided for
    /// convenience. For example:
    ///
    /// ~~~ignore
    /// // triggers a task failure when a GLFW error is encountered.
    /// glfw.set_error_callback(glfw::FAIL_ON_ERRORS);
    /// ~~~
    pub fn set_error_callback<UserData: 'static>(&mut self, callback: Option<ErrorCallback<UserData>>) {
        match callback {
            Some(f) => callbacks::error::set(f),
            None    => callbacks::error::unset(),
        }
    }

    /// Sets the monitor callback, overwriting the previous one stored.
    pub fn set_monitor_callback<UserData: 'static>(&mut self, callback: Option<MonitorCallback<UserData>>) {
        match callback {
            Some(f) => callbacks::monitor::set(f),
            None    => callbacks::monitor::unset(),
        }
    }

    /// Sets the joystick callback, overwriting the previous one stored
    pub fn set_joystick_callback<UserData: 'static>(&mut self, callback: Option<JoystickCallback<UserData>>) {
        match callback {
            Some(f) => callbacks::joystick::set(f),
            None    => callbacks::joystick::unset(),
        }
    }

    /// Supplies the primary monitor to the closure provided, if it exists.
    /// This is usually the monitor where elements like the Windows task bar or
    /// the OS X menu bar is located.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// let (window, events) = glfw.with_primary_monitor(|m| {
    ///     glfw.create_window(300, 300, "Hello this is window",
    ///         m.map_or(glfw::WindowMode::Windowed, |m| glfw::FullScreen(m)))
    /// }).expect("Failed to create GLFW window.");
    /// ~~~
    pub fn with_primary_monitor<T, F>(&mut self, f: F) -> T where F: Fn(&mut Self, Option<&Monitor>) -> T {
        match unsafe { ffi::glfwGetPrimaryMonitor() } {
            ptr if ptr.is_null() => f(self, None),
            ptr => f(self, Some(&Monitor {
                ptr: ptr
            })),
        }
    }

    /// Supplies the primary monitor to the closure provided, if it exists.
    /// This is usually the monitor where elements like the Windows task bar or
    /// the OS X menu bar is located.
    ///
    /// Variant that can accept an `FnMut` closure.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// glfw.with_primary_monitor(|_: &mut _, m: Option<&glfw::Monitor>| {
    ///     let monitor = m.unwrap();
    ///
    ///     let mode: glfw::VidMode = monitor.get_video_mode().unwrap();
    ///
    ///     // Modifying `window` requires `FnMut`
    ///     window.set_monitor(glfw::WindowMode::FullScreen(&monitor), 0, 0, mode.width, mode.height, Some(mode.refresh_rate));
    /// });
    /// ~~~
    pub fn with_primary_monitor_mut<T, F>(&mut self, mut f: F) -> T where F: FnMut(&mut Self, Option<&Monitor>) -> T {
        match unsafe { ffi::glfwGetPrimaryMonitor() } {
            ptr if ptr.is_null() => f(self, None),
            ptr => f(self, Some(&Monitor {
                ptr: ptr
            })),
        }
    }

    /// Supplies a vector of the currently connected monitors to the closure
    /// provided.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// glfw.with_connected_monitors(|monitors| {
    ///     for monitor in monitors.iter() {
    ///         println!("{}: {}", monitor.get_name(), monitor.get_video_mode());
    ///     }
    /// });
    /// ~~~
    pub fn with_connected_monitors<T, F>(&mut self, f: F) -> T where F: Fn(&mut Self, &[Monitor]) -> T {
        unsafe {
            let mut count = 0;
            let ptr = ffi::glfwGetMonitors(&mut count);
            f(self,
              &slice::from_raw_parts(ptr as *const _, count as usize).iter().map(|&ptr| {
                Monitor {
                    ptr: ptr
                }
            }).collect::<Vec<Monitor>>())
        }
    }

    /// Supplies a vector of the currently connected monitors to the closure
    /// provided.
    ///
    /// Variant that can accept an `FnMut` closure.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// glfw.with_connected_monitors(|monitors| {
    ///     for monitor in monitors.iter() {
    ///         println!("{}: {}", monitor.get_name(), monitor.get_video_mode());
    ///     }
    /// });
    /// ~~~
    pub fn with_connected_monitors_mut<T, F>(&mut self, mut f: F) -> T where F: FnMut(&mut Self, &[Monitor]) -> T {
        unsafe {
            let mut count = 0;
            let ptr = ffi::glfwGetMonitors(&mut count);
            f(self,
              &slice::from_raw_parts(ptr as *const _, count as usize).iter().map(|&ptr| {
                  Monitor {
                      ptr: ptr
                  }
              }).collect::<Vec<Monitor>>())
        }
    }

    /// Queries Vulkan support via `glfwVulkanSupported`
    #[cfg(feature = "vulkan")]
    pub fn vulkan_supported(&self) -> bool {
        unsafe { ffi::glfwVulkanSupported() == ffi::TRUE }
    }

    /// This is used to set the window hints for the next call to
    /// `Glfw::create_window`. The hints can be reset to their default values
    /// using calling the `Glfw::default_window_hints` function.
    ///
    /// Wrapper for `glfwWindowHint`
    ///
    /// # OpenGL 3.x and 4.x on Mac OS X
    ///
    /// The only OpenGL 3.x and 4.x contexts supported by OS X are
    /// forward-compatible, core profile contexts.
    ///
    /// 10.7 and 10.8 support the following OpenGL versions:
    ///
    /// - `glfw::WindowHint::ContextVersion(3, 2)`
    ///
    /// 10.9 supports the following OpenGL versions
    ///
    /// - `glfw::WindowHint::ContextVersion(3, 2)`
    /// - `glfw::WindowHint::ContextVersion(3, 3)`
    /// - `glfw::WindowHint::ContextVersion(4, 1)`
    ///
    /// To create an OS X compatible context, the hints should be specified as
    /// follows:
    ///
    /// ~~~ignore
    /// glfw.window_hint(glfw::WindowHint::ContextVersion(3, 2));
    /// glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
    /// glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
    /// ~~~
    pub fn window_hint(&mut self, hint: WindowHint) {
        //This is just a simple function to unwrap the option and convert it to `c_int` or use `GLFW_DONT_CARE`,
        //then call `glfwWindowHint` with the result. It was required because `GLFW_DONT_CARE` is signed,
        //so `value.unwrap_or(ffi::DONT_CARE)` wouldn't work because of the type difference.
        #[inline(always)]
        unsafe fn dont_care_hint(hint: c_int, value: Option<u32>) {
            ffi::glfwWindowHint(hint, match value {
                Some(v) => v as c_int,
                None => ffi::DONT_CARE
            })
        }

        match hint {
            WindowHint::RedBits(bits)                    => unsafe { dont_care_hint(ffi::RED_BITS,                      bits) },
            WindowHint::GreenBits(bits)                  => unsafe { dont_care_hint(ffi::GREEN_BITS,                    bits) },
            WindowHint::BlueBits(bits)                   => unsafe { dont_care_hint(ffi::BLUE_BITS,                     bits) },
            WindowHint::AlphaBits(bits)                  => unsafe { dont_care_hint(ffi::ALPHA_BITS,                    bits) },
            WindowHint::DepthBits(bits)                  => unsafe { dont_care_hint(ffi::DEPTH_BITS,                    bits) },
            WindowHint::StencilBits(bits)                => unsafe { dont_care_hint(ffi::STENCIL_BITS,                  bits) },
            WindowHint::AccumRedBits(bits)               => unsafe { dont_care_hint(ffi::ACCUM_RED_BITS,                bits) },
            WindowHint::AccumGreenBits(bits)             => unsafe { dont_care_hint(ffi::ACCUM_GREEN_BITS,              bits) },
            WindowHint::AccumBlueBits(bits)              => unsafe { dont_care_hint(ffi::ACCUM_BLUE_BITS,               bits) },
            WindowHint::AccumAlphaBits(bits)             => unsafe { dont_care_hint(ffi::ACCUM_ALPHA_BITS,              bits) },
            WindowHint::AuxBuffers(num_buffers)          => unsafe { dont_care_hint(ffi::AUX_BUFFERS,                   num_buffers) },
            WindowHint::Samples(num_samples)             => unsafe { dont_care_hint(ffi::SAMPLES,                       num_samples) },
            WindowHint::RefreshRate(rate)                => unsafe { dont_care_hint(ffi::REFRESH_RATE,                  rate) },
            WindowHint::Stereo(is_stereo)                => unsafe { ffi::glfwWindowHint(ffi::STEREO,                   is_stereo as c_int) },
            WindowHint::SRgbCapable(is_capable)          => unsafe { ffi::glfwWindowHint(ffi::SRGB_CAPABLE,             is_capable as c_int) },
            WindowHint::ClientApi(api)                   => unsafe { ffi::glfwWindowHint(ffi::CLIENT_API,               api as c_int) },
            WindowHint::ContextVersionMajor(major)       => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR,    major as c_int) },
            WindowHint::ContextVersionMinor(minor)       => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR,    minor as c_int) },
            WindowHint::ContextVersion(major, minor)     => unsafe {
                ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int);
                ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int)
            },
            WindowHint::ContextRobustness(robustness)    => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_ROBUSTNESS,       robustness as c_int) },
            WindowHint::OpenGlForwardCompat(is_compat)   => unsafe { ffi::glfwWindowHint(ffi::OPENGL_FORWARD_COMPAT,    is_compat as c_int) },
            WindowHint::OpenGlDebugContext(is_debug)     => unsafe { ffi::glfwWindowHint(ffi::OPENGL_DEBUG_CONTEXT,     is_debug as c_int) },
            WindowHint::OpenGlProfile(profile)           => unsafe { ffi::glfwWindowHint(ffi::OPENGL_PROFILE,           profile as c_int) },
            WindowHint::Resizable(is_resizable)          => unsafe { ffi::glfwWindowHint(ffi::RESIZABLE,                is_resizable as c_int) },
            WindowHint::Visible(is_visible)              => unsafe { ffi::glfwWindowHint(ffi::VISIBLE,                  is_visible as c_int) },
            WindowHint::Decorated(is_decorated)          => unsafe { ffi::glfwWindowHint(ffi::DECORATED,                is_decorated as c_int) },
            WindowHint::AutoIconify(auto_iconify)        => unsafe { ffi::glfwWindowHint(ffi::AUTO_ICONIFY,             auto_iconify as c_int) },
            WindowHint::Floating(is_floating)            => unsafe { ffi::glfwWindowHint(ffi::FLOATING,                 is_floating as c_int) },
            WindowHint::ContextNoError(is_no_error)      => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_NO_ERROR,         is_no_error as c_int) },
            WindowHint::ContextCreationApi(api)          => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_CREATION_API,     api as c_int) },
            WindowHint::ContextReleaseBehavior(behavior) => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_RELEASE_BEHAVIOR, behavior as c_int) },
            WindowHint::DoubleBuffer(is_dbuffered)       => unsafe { ffi::glfwWindowHint(ffi::DOUBLEBUFFER,             is_dbuffered as c_int) },
        }
    }

    /// Resets the window hints previously set by the `window_hint` function to
    /// their default values.
    ///
    /// Wrapper for `glfwDefaultWindowHints`.
    pub fn default_window_hints(&mut self) {
        unsafe { ffi::glfwDefaultWindowHints(); }
    }

    /// Creates a new window.
    ///
    /// Wrapper for `glfwCreateWindow`.
    pub fn create_window(&self, width: u32, height: u32, title: &str, mode: WindowMode) -> Option<(Window, Receiver<(f64, WindowEvent)>)> {
        self.create_window_intern(width, height, title, mode, None)
    }

    /// Internal wrapper for `glfwCreateWindow`.
    fn create_window_intern(&self, width: u32, height: u32, title: &str, mode: WindowMode, share: Option<&Window>) -> Option<(Window, Receiver<(f64, WindowEvent)>)> {
        let ptr = unsafe {
            with_c_str(title, |title| {
                ffi::glfwCreateWindow(
                    width as c_int,
                    height as c_int,
                    title,
                    mode.to_ptr(),
                    match share { Some(w) => w.ptr, None => ptr::null_mut() }
                )
            })
        };
        if ptr.is_null() {
            None
        } else {
            let (drop_sender, drop_receiver) = channel();
            let (sender, receiver) = channel();
            unsafe { ffi::glfwSetWindowUserPointer(ptr, mem::transmute(Box::new(sender))); }
            Some((
                Window {
                    ptr: ptr,
                    glfw: self.clone(),
                    is_shared: share.is_some(),
                    drop_sender: Some(drop_sender),
                    drop_receiver: drop_receiver,
                    current_cursor: None
                },
                receiver,
            ))
        }
    }

    /// Makes the context of the specified window current. If no window is given
    /// then the current context is detached.
    ///
    /// Wrapper for `glfwMakeContextCurrent`.
    pub fn make_context_current(&mut self, context: Option<&Window>) {
        match context {
            Some(window) => unsafe { ffi::glfwMakeContextCurrent(window.ptr) },
            None         => unsafe { ffi::glfwMakeContextCurrent(ptr::null_mut()) },
        }
    }

    /// Wrapper for `glfwGetX11Display`
    #[cfg(target_os="linux")]
    pub fn get_x11_display(&self) -> *mut c_void {
        unsafe { ffi::glfwGetX11Display() }
    }

    /// Immediately process the received events.
    ///
    /// Wrapper for `glfwPollEvents`.
    pub fn poll_events(&mut self) {
        unsafe { ffi::glfwPollEvents(); }
    }

    /// Sleep until at least one event has been received, and then perform the
    /// equivalent of `Glfw::poll_events`.
    ///
    /// Wrapper for `glfwWaitEvents`.
    pub fn wait_events(&mut self) {
        unsafe { ffi::glfwWaitEvents(); }
    }

    /// Sleep until at least one event has been received, or until the specified
    /// timeout is reached, and then perform the equivalent of `Glfw::poll_events`.
    /// Timeout is specified in seconds.
    ///
    /// Wrapper for `glfwWaitEventsTimeout`.
    pub fn wait_events_timeout(&mut self, timeout: f64) {
        unsafe { ffi::glfwWaitEventsTimeout(timeout); }
    }

    /// Posts an empty event from the current thread to the event queue, causing
    /// `wait_events` or `wait_events_timeout` to return.
    /// If no windows exist, this function returns immediately.
    ///
    /// Wrapper for `glfwPostEmptyEvent`.
    pub fn post_empty_event(&mut self) {
        unsafe { ffi::glfwPostEmptyEvent(); }
    }

    /// Returns the current value of the GLFW timer. Unless the timer has been
    /// set using `glfw::set_time`, the timer measures time elapsed since GLFW
    /// was initialized.
    ///
    /// Wrapper for `glfwGetTime`.
    pub fn get_time(&self) -> f64 {
        unsafe { ffi::glfwGetTime() as f64 }
    }

    /// Sets the value of the GLFW timer.
    ///
    /// Wrapper for `glfwSetTime`.
    pub fn set_time(&mut self, time: f64) {
        unsafe { ffi::glfwSetTime(time as c_double); }
    }

    /// Wrapper for `glfwGetTimerValue`.
    pub fn get_timer_value() -> u64 {
        unsafe { ffi::glfwGetTimerValue() as u64 }
    }

    /// Wrapper for `glfwGetTimerFrequency`
    pub fn get_timer_frquency() -> u64 {
        unsafe { ffi::glfwGetTimerFrequency() as u64 }
    }

    /// Sets the number of screen updates to wait before swapping the buffers of
    /// the current context and returning from `Window::swap_buffers`.
    ///
    /// Wrapper for `glfwSwapInterval`.
    pub fn set_swap_interval(&mut self, interval: SwapInterval) {
        unsafe {
            ffi::glfwSwapInterval(match interval {
                SwapInterval::None           =>  0       as c_int,
                SwapInterval::Adaptive       => -1       as c_int,
                SwapInterval::Sync(interval) => interval as c_int
            })
        }
    }

    /// Returns `true` if the specified OpenGL or context creation API extension
    /// is supported by the current context.
    ///
    /// Wrapper for `glfwExtensionSupported`.
    pub fn extension_supported(&self, extension: &str) -> bool {
        unsafe {
            with_c_str(extension, |extension| {
                ffi::glfwExtensionSupported(extension) == ffi::TRUE
            })
        }
    }

    /// Wrapper for `glfwGetRequiredInstanceExtensions`
    ///
    /// This function returns a Vector of names of Vulkan instance extensions
    /// required by GLFW for creating Vulkan surfaces for GLFW windows. If successful,
    /// the list will always contains `VK_KHR_surface`, so if you don't require any
    /// additional extensions you can pass this list directly to the `VkInstanceCreateInfo` struct.
    ///
    /// Will return `None` if the API is unavailable.
    #[cfg(feature = "vulkan")]
    pub fn get_required_instance_extensions(&self) -> Option<Vec<String>> {
        let mut len: c_uint = 0;

        unsafe {
            let raw_extensions: *const *const c_char = ffi::glfwGetRequiredInstanceExtensions(&mut len as *mut c_uint);

            if !raw_extensions.is_null() {
                return Some(slice::from_raw_parts(raw_extensions, len as usize)
                    .iter()
                    .map(|extensions| string_from_c_str(*extensions))
                    .collect());
            }
        }

        None
    }

    /// Returns the address of the specified client API or extension function if
    /// it is supported by the current context, NULL otherwise.
    ///
    /// Wrapper for `glfwGetProcAddress`.
    pub fn get_proc_address_raw(&self, procname: &str) -> GLProc {
        debug_assert!(unsafe { ffi::glfwGetCurrentContext() } != std::ptr::null_mut());
        with_c_str(procname, |procname| {
            unsafe { ffi::glfwGetProcAddress(procname) }
        })
    }

    /// This function returns the address of the specified Vulkan core or extension function
    /// for the specified instance. If instance is set to NULL it can return any function
    /// exported from the Vulkan loader, including at least the following functions:
    ///
    /// * `vkEnumerateInstanceExtensionProperties`
    /// * `vkEnumerateInstanceLayerProperties`
    /// * `vkCreateInstance`
    /// * `vkGetInstanceProcAddr`
    ///
    /// If Vulkan is not available on the machine, this function returns `NULL`
    ///
    /// Wrapper for `glfwGetInstanceProcAddress`
    #[cfg(feature = "vulkan")]
    pub fn get_instance_proc_address_raw(&self, instance: VkInstance, procname: &str) -> VkProc {
        //TODO: Determine if this assertion is required? It doesn't seem to be required for vkCreateInstance,
        //TODO: but it might be needed for other pointers.
        debug_assert!(unsafe { ffi::glfwGetCurrentContext() } != std::ptr::null_mut());
        with_c_str(procname, |procname| {
            unsafe { ffi::glfwGetInstanceProcAddress(instance, procname) }
        })
    }

    /// This function returns whether the specified queue family of the specified
    /// physical device supports presentation to the platform GLFW was built for.
    ///
    /// Wrapper for `glfwGetPhysicalDevicePresentationSupport`
    #[cfg(feature = "vulkan")]
    pub fn get_physical_device_presentation_support_raw(&self, instance: VkInstance, device: VkPhysicalDevice, queue_family: u32) -> bool {
        vk::TRUE == unsafe { ffi::glfwGetPhysicalDevicePresentationSupport(instance, device, queue_family as c_uint) as u32 }
    }

    /// Constructs a `Joystick` handle corresponding to the supplied `JoystickId`.
    pub fn get_joystick(&self, id: JoystickId) -> Joystick {
        Joystick { id: id, glfw: self.clone() }
    }
}

/// Wrapper for `glfwGetVersion`.
pub fn get_version() -> Version {
    unsafe {
        let mut major = 0;
        let mut minor = 0;
        let mut patch = 0;
        ffi::glfwGetVersion(&mut major, &mut minor, &mut patch);
        Version {
            major: major as u64,
            minor: minor as u64,
            patch: patch as u64,
            pre:   Vec::new(),
            build: Vec::new(),
        }
    }
}

/// Replacement for `String::from_raw_buf`
pub unsafe fn string_from_c_str(c_str: *const c_char) -> String {
    String::from_utf8_lossy(CStr::from_ptr(c_str).to_bytes()).into_owned()
}

/// Replacement for `ToCStr::with_c_str`
pub fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const c_char) -> T {
    let c_str = CString::new(s.as_bytes());
    f(c_str.unwrap().as_bytes_with_nul().as_ptr() as *const _)
}

/// Wrapper for `glfwGetVersionString`.
pub fn get_version_string() -> String {
    unsafe { string_from_c_str(ffi::glfwGetVersionString()) }
}

/// An monitor callback. This can be supplied with some user data to be passed
/// to the callback function when it is triggered.
pub type MonitorCallback<UserData> = Callback<fn(Monitor, MonitorEvent, &UserData), UserData>;

/// A struct that wraps a `*GLFWmonitor` handle.
#[allow(missing_copy_implementations)]
pub struct Monitor {
    ptr: *mut ffi::GLFWmonitor
}

impl std::fmt::Debug for Monitor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Monitor({:p})", self.ptr)
    }
}

impl Monitor {
    /// Wrapper for `glfwGetMonitorPos`.
    pub fn get_pos(&self) -> (i32, i32) {
        unsafe {
            let mut xpos = 0;
            let mut ypos = 0;
            ffi::glfwGetMonitorPos(self.ptr, &mut xpos, &mut ypos);
            (xpos as i32, ypos as i32)
        }
    }

    /// Wrapper for `glfwGetMonitorPhysicalSize`.
    pub fn get_physical_size(&self) -> (i32, i32) {
        unsafe {
            let mut width = 0;
            let mut height = 0;
            ffi::glfwGetMonitorPhysicalSize(self.ptr, &mut width, &mut height);
            (width as i32, height as i32)
        }
    }

    /// Wrapper for `glfwGetMonitorName`.
    pub fn get_name(&self) -> String {
        unsafe { string_from_c_str(ffi::glfwGetMonitorName(self.ptr)) }
    }

    /// Wrapper for `glfwGetVideoModes`.
    pub fn get_video_modes(&self) -> Vec<VidMode> {
        unsafe {
            let mut count = 0;
            let ptr = ffi::glfwGetVideoModes(self.ptr, &mut count);
            slice::from_raw_parts(ptr, count as usize).iter().map(VidMode::from_glfw_vid_mode).collect()
        }
    }

    /// Wrapper for `glfwGetVideoMode`.
    pub fn get_video_mode(&self) -> Option<VidMode> {
        unsafe {
            // TODO: Can be returned to as_ref + map as in previous commit when (if?) as_ref stabilizes.
            let ptr = ffi::glfwGetVideoMode(self.ptr);
            if ptr.is_null() {
                None
            } else {
                Some(VidMode::from_glfw_vid_mode(&*ptr))
            }
        }
    }

    /// Wrapper for `glfwSetGamma`.
    pub fn set_gamma(&mut self, gamma: f32) {
        unsafe { ffi::glfwSetGamma(self.ptr, gamma as c_float); }
    }

    /// Wrapper for `glfwGetGammaRamp`.
    pub fn get_gamma_ramp(&self) -> GammaRamp {
        unsafe {
            let llramp = *ffi::glfwGetGammaRamp(self.ptr);
            GammaRamp {
                red:    slice::from_raw_parts(llramp.red as *const c_ushort, llramp.size as usize)
                              .iter().map(|&x| x).collect(),
                green:  slice::from_raw_parts(llramp.green as *const c_ushort, llramp.size as usize)
                              .iter().map(|&x| x).collect(),
                blue:   slice::from_raw_parts(llramp.blue as *const c_ushort, llramp.size as usize)
                              .iter().map(|&x| x).collect(),
            }
        }
    }

    /// Wrapper for `glfwSetGammaRamp`.
    pub fn set_gamma_ramp(&mut self, ramp: &mut GammaRamp) {
        unsafe {
            ffi::glfwSetGammaRamp(
                self.ptr,
                &ffi::GLFWgammaramp {
                    red:    ramp.red.as_mut_ptr(),
                    green:  ramp.green.as_mut_ptr(),
                    blue:   ramp.blue.as_mut_ptr(),
                    size:   ramp.red.len() as u32,
                }
            );
        }
    }
}

/// Monitor events.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum MonitorEvent {
    Connected                   = ffi::CONNECTED,
    Disconnected                = ffi::DISCONNECTED,
}

impl VidMode {
    fn from_glfw_vid_mode(mode: &ffi::GLFWvidmode) -> VidMode {
        VidMode {
            width:        mode.width as u32,
            height:       mode.height as u32,
            red_bits:     mode.redBits as u32,
            green_bits:   mode.greenBits as u32,
            blue_bits:    mode.blueBits as u32,
            refresh_rate: mode.refreshRate as u32,
        }
    }
}

impl fmt::Debug for VidMode {
    /// Returns a string representation of the video mode.
    ///
    /// # Returns
    ///
    /// A string in the form:
    ///
    /// ~~~ignore
    /// ~"[width] x [height], [total_bits] ([red_bits] [green_bits] [blue_bits]) [refresh_rate] Hz"
    /// ~~~
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} x {}, {} = {} + {} + {}, {} Hz",
            self.width, self.height,
            self.red_bits + self.green_bits + self.blue_bits,
            self.red_bits, self.green_bits, self.blue_bits,
            self.refresh_rate)
    }
}

/// Window hints that can be set using the `window_hint` function.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum WindowHint {
    /// Specifies the desired bit depth of the red component of the default framebuffer.
    RedBits(Option<u32>),
    /// Specifies the desired bit depth of the green component of the default framebuffer.
    GreenBits(Option<u32>),
    /// Specifies the desired bit depth of the blue component of the default framebuffer.
    BlueBits(Option<u32>),
    /// Specifies the desired bit depth of the alpha component of the default framebuffer.
    AlphaBits(Option<u32>),
    /// Specifies the desired bit depth of the depth component of the default framebuffer.
    DepthBits(Option<u32>),
    /// Specifies the desired bit depth of the stencil component of the default framebuffer.
    StencilBits(Option<u32>),
    /// Specifies the desired bit depth of the red component of the accumulation framebuffer.
    AccumRedBits(Option<u32>),
    /// Specifies the desired bit depth of the green component of the accumulation framebuffer.
    AccumGreenBits(Option<u32>),
    /// Specifies the desired bit depth of the blue component of the accumulation framebuffer.
    AccumBlueBits(Option<u32>),
    /// Specifies the desired bit depth of the alpha component of the accumulation framebuffer.
    AccumAlphaBits(Option<u32>),
    /// Specifies the desired number of auxiliary buffers.
    AuxBuffers(Option<u32>),
    /// Specifies whether to use stereoscopic rendering.
    Stereo(bool),
    /// Specifies the desired number of samples to use for multisampling. Zero
    /// disables multisampling.
    Samples(Option<u32>),
    /// Specifies whether the framebuffer should be sRGB capable.
    SRgbCapable(bool),
    /// Specifies the desired refresh rate for full screen windows. If set to `None`,
    /// the highest available refresh rate will be used.
    ///
    /// This hint is ignored for windowed mode windows.
    RefreshRate(Option<u32>),
    /// Specifies which `ClientApi` to create the context for.
    ClientApi(ClientApiHint),
    /// Specifies the major client API version that the created context must be
    /// compatible with.
    ///
    /// Window creation will fail if the resulting OpenGL version is less than
    /// the one requested.
    ContextVersionMajor(u32),
    /// Specifies the minor client API version that the created context must be
    /// compatible with.
    ///
    /// Window creation will fail if the resulting OpenGL version is less than
    /// the one requested.
    ContextVersionMinor(u32),
    /// Specifies the client API version that the created context must be
    /// compatible with. This is the same as successive calls to `window_hint`
    /// function with the `ContextVersionMajor` and `ContextVersionMinor` hints.
    ///
    /// Window creation will fail if the resulting OpenGL version is less than
    /// the one requested.
    ///
    /// If `ContextVersion(1, 0)` is requested, _most_ drivers will provide the
    /// highest available context.
    ContextVersion(u32, u32),
    /// Specifies the `ContextRobustness` strategy to be used.
    ContextRobustness(ContextRobustnessHint),
    /// Specifies whether the OpenGL context should be forward-compatible, i.e.
    /// one where all functionality deprecated in the requested version of
    /// OpenGL is removed. This may only be used if the requested OpenGL version
    /// is 3.0 or above.
    ///
    /// If another client API is requested, this hint is ignored.
    OpenGlForwardCompat(bool),
    /// Specifies whether to create a debug OpenGL context, which may have
    /// additional error and performance issue reporting functionality.
    ///
    /// If another client API is requested, this hint is ignored.
    OpenGlDebugContext(bool),
    /// Specifies which OpenGL profile to create the context for. If requesting
    /// an OpenGL version below 3.2, `OpenGlAnyProfile` must be used.
    ///
    /// If another client API is requested, this hint is ignored.
    OpenGlProfile(OpenGlProfileHint),
    /// Specifies whether the window will be resizable by the user. Even if this
    /// is set to `false`, the window can still be resized using the
    /// `Window::set_size` function.
    ///
    /// This hint is ignored for fullscreen windows.
    Resizable(bool),
    /// Specifies whether the window will be visible on creation.
    ///
    /// This hint is ignored for fullscreen windows.
    Visible(bool),
    /// Specifies whether the window will have platform-specific decorations
    /// such as a border, a close widget, etc.
    ///
    /// This hint is ignored for full screen windows.
    Decorated(bool),
    /// Specifies whether the (full screen) window will automatically iconify
    /// and restore the previous video mode on input focus loss.
    ///
    /// This hint is ignored for windowed mode windows.
    AutoIconify(bool),
    /// Specifies whether the window will be floating above other regular
    /// windows, also called topmost or always-on-top.
    ///
    /// This hint is ignored for full screen windows.
    Floating(bool),
    /// Specifies whether the OpenGL or OpenGL ES contexts do not emit errors,
    /// allowing for better performance in some situations.
    ContextNoError(bool),
    /// Specifies which context creation API to use to create the context.
    ContextCreationApi(ContextCreationApi),
    /// Specifies the behavior of the OpenGL pipeline when a context is transferred between threads
    ContextReleaseBehavior(ContextReleaseBehavior),
    /// Specifies whether the framebuffer should be double buffered.
    ///
    /// You nearly always want to use double buffering.
    ///
    /// Note that setting this to false will make `swap_buffers` do nothing useful,
    /// and your scene will have to be displayed some other way.
    DoubleBuffer(bool)
}

/// Client API tokens.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ClientApiHint {
    NoApi                    = ffi::NO_API,
    OpenGl                   = ffi::OPENGL_API,
    OpenGlEs                 = ffi::OPENGL_ES_API,
}

/// Context robustness tokens.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ContextRobustnessHint {
    NoRobustness                = ffi::NO_ROBUSTNESS,
    NoResetNotification         = ffi::NO_RESET_NOTIFICATION,
    LoseContextOnReset          = ffi::LOSE_CONTEXT_ON_RESET,
}

/// OpenGL profile tokens.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum OpenGlProfileHint {
    Any            = ffi::OPENGL_ANY_PROFILE,
    Core           = ffi::OPENGL_CORE_PROFILE,
    Compat         = ffi::OPENGL_COMPAT_PROFILE,
}

/// Describes the mode of a window
#[derive(Copy, Clone, Debug)]
pub enum WindowMode<'a> {
    /// Full screen mode. Contains the monitor on which the window is displayed.
    FullScreen(&'a Monitor),

    /// Windowed mode.
    Windowed,
}

/// Private conversion methods for `glfw::WindowMode`
impl<'a> WindowMode<'a> {
    /// Returns a pointer to a monitor if the window is fullscreen, otherwise
    /// it returns a null pointer (if it is in windowed mode).
    fn to_ptr(&self) -> *mut ffi::GLFWmonitor {
        match *self {
            WindowMode::FullScreen(ref monitor) => monitor.ptr,
            WindowMode::Windowed                => ptr::null_mut(),
        }
    }
}

/// Key modifiers (e.g., Shift, Control, Alt, Super)
pub mod modifiers {
    bitflags! {
        #[doc = "Key modifiers"]
        pub flags Modifiers: ::libc::c_int {
            const Shift       = ::ffi::MOD_SHIFT,
            const Control     = ::ffi::MOD_CONTROL,
            const Alt         = ::ffi::MOD_ALT,
            const Super       = ::ffi::MOD_SUPER
        }
    }
}

/// Keyboard code returned by the OS
pub type Scancode = c_int;

/// Window event messages.
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum WindowEvent {
    Pos(i32, i32),
    Size(i32, i32),
    Close,
    Refresh,
    Focus(bool),
    Iconify(bool),
    FramebufferSize(i32, i32),
    MouseButton(MouseButton, Action, modifiers::Modifiers),
    CursorPos(f64, f64),
    CursorEnter(bool),
    Scroll(f64, f64),
    Key(Key, Scancode, Action, modifiers::Modifiers),
    Char(char),
    CharModifiers(char, modifiers::Modifiers),
    FileDrop(Vec<PathBuf>),
}

/// Returns an iterator that yields until no more messages are contained in the
/// `Receiver`'s queue. This is useful for event handling where the blocking
/// behaviour of `Receiver::iter` is undesirable.
///
/// # Example
///
/// ~~~ignore
/// for event in glfw::flush_messages(&events) {
///     // handle event
/// }
/// ~~~
pub fn flush_messages<'a, Message: Send>(receiver: &'a Receiver<Message>) -> FlushedMessages<'a, Message> {
    FlushedMessages(receiver)
}

/// An iterator that yields until no more messages are contained in the
/// `Receiver`'s queue.
pub struct FlushedMessages<'a, Message: 'a + Send>(&'a Receiver<Message>);

unsafe impl<'a, Message: 'a + Send> Send for FlushedMessages<'a, Message> {
}

impl<'a, Message: 'static + Send> Iterator for FlushedMessages<'a, Message> {
    type Item = Message;

    fn next(&mut self) -> Option<Message> {
        let FlushedMessages(receiver) = *self;
        match receiver.try_recv() {
            Ok(message) => Some(message),
            _ => None,
        }
    }
}

/// A struct that wraps a `*GLFWwindow` handle.
pub struct Window {
    ptr: *mut ffi::GLFWwindow,
    pub glfw: Glfw,
    pub is_shared: bool,
    /// A `Sender` that can be cloned out to child `RenderContext`s.
    drop_sender: Option<Sender<()>>,
    /// Once all  child`RenderContext`s have been dropped, calling `try_recv()`
    /// on the `drop_receiver` will result in an `Err(std::comm::Disconnected)`,
    /// indicating that it is safe to drop the `Window`.
    drop_receiver: Receiver<()>,
    /// This is here to allow owning the current Cursor object instead
    /// of forcing the user to take care of its lifetime.
    current_cursor: Option<Cursor>
}

macro_rules! set_window_callback {
    ($window:ident, $should_poll:expr, $ll_fn:ident, $callback:ident) => ({
        if $should_poll {
            unsafe { ffi::$ll_fn($window.ptr, Some(callbacks::$callback)); }
        } else {
            unsafe { ffi::$ll_fn($window.ptr, None); }
        }
    })
}

impl Window {
    /// Returns the address of the specified client API or extension function if
    /// it is supported by the context associated with this Window. If this Window is not the
    /// current context, it will make it the current context.
    ///
    /// Wrapper for `glfwGetProcAddress`.
    pub fn get_proc_address(&mut self, procname: &str) -> GLProc {
        if self.ptr != unsafe { ffi::glfwGetCurrentContext() } {
            self.make_current();
        }

        self.glfw.get_proc_address_raw(procname)
    }

    /// This function returns the address of the specified Vulkan core or extension function
    /// for the specified instance. If instance is set to NULL it can return any function
    /// exported from the Vulkan loader, including at least the following functions:
    ///
    /// * `vkEnumerateInstanceExtensionProperties`
    /// * `vkEnumerateInstanceLayerProperties`
    /// * `vkCreateInstance`
    /// * `vkGetInstanceProcAddr`
    ///
    /// If Vulkan is not available on the machine, this function returns `NULL`
    ///
    /// Wrapper for `glfwGetInstanceProcAddress`
    #[cfg(feature = "vulkan")]
    pub fn get_instance_proc_address(&mut self, instance: VkInstance, procname: &str) -> VkProc {
        //TODO: Determine if setting this context as current is required? It doesn't seem to be required for vkCreateInstance,
        //TODO: but it might be needed for other pointers.
        if self.ptr != unsafe { ffi::glfwGetCurrentContext() } {
            self.make_current();
        }

        self.glfw.get_instance_proc_address_raw(instance, procname)
    }

    /// This function returns whether the specified queue family of the specified
    /// physical device supports presentation to the platform GLFW was built for.
    ///
    /// Wrapper for `glfwGetPhysicalDevicePresentationSupport`
    #[cfg(feature = "vulkan")]
    pub fn get_physical_device_presentation_support(&self, instance: VkInstance, device: VkPhysicalDevice, queue_family: u32) -> bool {
        self.glfw.get_physical_device_presentation_support_raw(instance, device, queue_family)
    }

    /// Wrapper for `glfwCreateWindow`.
    pub fn create_shared(&self, width: u32, height: u32, title: &str, mode: WindowMode) -> Option<(Window, Receiver<(f64, WindowEvent)>)> {
        self.glfw.create_window_intern(width, height, title, mode, Some(self))
    }

    /// Calling this method forces the destructor to be called, closing the
    /// window.
    pub fn close(self) {}

    /// Returns a render context that can be shared between tasks, allowing
    /// for concurrent rendering.
    pub fn render_context(&mut self) -> RenderContext {
        RenderContext {
            ptr: self.ptr,
            // this will only be None after dropping so this is safe
            drop_sender: self.drop_sender.as_ref().unwrap().clone()
        }
    }

    /// Wrapper for `glfwWindowShouldClose`.
    pub fn should_close(&self) -> bool {
        unsafe { ffi::glfwWindowShouldClose(self.ptr) == ffi::TRUE }
    }

    /// Wrapper for `glfwSetWindowShouldClose`.
    pub fn set_should_close(&mut self, value: bool) {
        unsafe { ffi::glfwSetWindowShouldClose(self.ptr, value as c_int) }
    }

    /// Sets the title of the window.
    ///
    /// Wrapper for `glfwSetWindowTitle`.
    pub fn set_title(&mut self, title: &str) {
        unsafe {
            with_c_str(title, |title| {
                ffi::glfwSetWindowTitle(self.ptr, title);
            });
        }
    }

    /// Wrapper for `glfwGetWindowPos`.
    pub fn get_pos(&self) -> (i32, i32) {
        unsafe {
            let mut xpos = 0;
            let mut ypos = 0;
            ffi::glfwGetWindowPos(self.ptr, &mut xpos, &mut ypos);
            (xpos as i32, ypos as i32)
        }
    }

    /// Wrapper for `glfwSetWindowPos`.
    pub fn set_pos(&mut self, xpos: i32, ypos: i32) {
        unsafe { ffi::glfwSetWindowPos(self.ptr, xpos as c_int, ypos as c_int); }
    }

    /// Wrapper for `glfwGetWindowSize`.
    pub fn get_size(&self) -> (i32, i32) {
        unsafe {
            let mut width = 0;
            let mut height = 0;
            ffi::glfwGetWindowSize(self.ptr, &mut width, &mut height);
            (width as i32, height as i32)
        }
    }

    /// Wrapper for `glfwSetWindowSize`.
    pub fn set_size(&mut self, width: i32, height: i32) {
        unsafe { ffi::glfwSetWindowSize(self.ptr, width as c_int, height as c_int); }
    }

    /// Wrapper for `glfwGetWindowFrameSize`
    ///
    /// Returns `(left, top, right, bottom)` edge window frame sizes, in screen coordinates.
    pub fn get_frame_size(&self) -> (i32, i32, i32, i32) {
        let (mut left, mut top, mut right, mut bottom): (i32, i32, i32, i32) = (0, 0, 0, 0);

        unsafe {
            ffi::glfwGetWindowFrameSize(self.ptr, &mut left as *mut c_int, &mut top as *mut c_int, &mut right as *mut c_int, &mut bottom as *mut c_int);
        }

        (left, top, right, bottom)
    }

    /// Wrapper for `glfwGetFramebufferSize`.
    pub fn get_framebuffer_size(&self) -> (i32, i32) {
        unsafe {
            let mut width = 0;
            let mut height = 0;
            ffi::glfwGetFramebufferSize(self.ptr, &mut width, &mut height);
            (width as i32, height as i32)
        }
    }

    /// Wrapper for `glfwSetWindowAspectRatio`.
    pub fn set_aspect_ratio(&mut self, numer: u32, denum: u32) {
        unsafe { ffi::glfwSetWindowAspectRatio(self.ptr, numer as c_int, denum as c_int) }
    }

    /// Wrapper for `glfwSetWindowSizeLimits`.
    pub fn set_size_limits(&mut self, minwidth: u32, minheight: u32, maxwidth: u32, maxheight: u32) {
        unsafe { ffi::glfwSetWindowSizeLimits(self.ptr , minwidth as c_int, minheight as c_int, maxwidth as c_int, maxheight as c_int) }
    }

    /// Wrapper for `glfwIconifyWindow`.
    pub fn iconify(&mut self) {
        unsafe { ffi::glfwIconifyWindow(self.ptr); }
    }

    /// Wrapper for `glfwRestoreWindow`.
    pub fn restore(&mut self) {
        unsafe { ffi::glfwRestoreWindow(self.ptr); }
    }

    /// Wrapper for `glfwMaximizeWindow`
    pub fn maximize(&mut self) {
        unsafe { ffi::glfwMaximizeWindow(self.ptr) }
    }

    /// Wrapper for `glfwShowWindow`.
    pub fn show(&mut self) {
        unsafe { ffi::glfwShowWindow(self.ptr); }
    }

    /// Wrapper for `glfwHideWindow`.
    pub fn hide(&mut self) {
        unsafe { ffi::glfwHideWindow(self.ptr); }
    }

    /// Returns whether the window is fullscreen or windowed.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// window.with_window_mode(|mode| {
    ///     match mode {
    ///         glfw::Windowed => println!("Windowed"),
    ///         glfw::FullScreen(m) => println!("FullScreen({})", m.get_name()),
    ///     }
    /// });
    /// ~~~
    pub fn with_window_mode<T, F>(&self, f: F) -> T where F: Fn(WindowMode) -> T {
        let ptr = unsafe { ffi::glfwGetWindowMonitor(self.ptr) };
        if ptr.is_null() {
            f(WindowMode::Windowed)
        } else {
            f(WindowMode::FullScreen(&Monitor {
                ptr: ptr
            }))
        }
    }

    /// Returns whether the window is fullscreen or windowed.
    ///
    /// Variant that can accept an `FnMut` closure.
    ///
    /// # Example
    ///
    /// ~~~ignore
    /// window.with_window_mode(|mode| {
    ///     match mode {
    ///         glfw::Windowed => println!("Windowed"),
    ///         glfw::FullScreen(m) => println!("FullScreen({})", m.get_name()),
    ///     }
    /// });
    /// ~~~
    pub fn with_window_mode_mut<T, F>(&self, mut f: F) -> T where F: FnMut(WindowMode) -> T {
        let ptr = unsafe { ffi::glfwGetWindowMonitor(self.ptr) };
        if ptr.is_null() {
            f(WindowMode::Windowed)
        } else {
            f(WindowMode::FullScreen(&Monitor {
                ptr: ptr
            }))
        }
    }

    /// Wrapper for `glfwSetWindowMonitor`
    pub fn set_monitor(&mut self, mode: WindowMode, xpos: i32, ypos: i32, width: u32, height: u32, refresh_rate: Option<u32>) {
        let monitor_ptr = if let WindowMode::FullScreen(ref monitor) = mode { monitor.ptr } else { ptr::null_mut() };

        unsafe {
            ffi::glfwSetWindowMonitor(self.ptr, monitor_ptr, xpos as c_int, ypos as c_int, width as c_int, height as c_int, match refresh_rate {
                Some(value) => value as c_int,
                None => ffi::DONT_CARE
            })
        }
    }

    /// Wrapper for `glfwFocusWindow`
    ///
    /// It is NOT recommended to use this function, as it steals focus from other applications
    /// and can be extremely disruptive to the user.
    pub fn focus(&mut self) {
        unsafe { ffi::glfwFocusWindow(self.ptr) }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `FOCUSED`.
    pub fn is_focused(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::FOCUSED) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `ICONIFIED`.
    pub fn is_iconified(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::ICONIFIED) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowattrib` called with `MAXIMIZED`.
    pub fn is_maximized(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::MAXIMIZED) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `CLIENT_API`.
    pub fn get_client_api(&self) -> c_int {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::CLIENT_API) }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with
    /// `CONTEXT_VERSION_MAJOR`, `CONTEXT_VERSION_MINOR` and `CONTEXT_REVISION`.
    ///
    /// # Returns
    ///
    /// The client API version of the window's context in a version struct.
    pub fn get_context_version(&self) -> Version {
        unsafe {
            Version {
                major: ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_VERSION_MAJOR) as u64,
                minor: ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_VERSION_MINOR) as u64,
                patch: ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_REVISION) as u64,
                pre:   Vec::new(),
                build: Vec::new(),
            }
        }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `CONTEXT_ROBUSTNESS`.
    pub fn get_context_robustness(&self) -> c_int {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::CONTEXT_ROBUSTNESS) }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `OPENGL_FORWARD_COMPAT`.
    pub fn is_opengl_forward_compat(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::OPENGL_FORWARD_COMPAT) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `OPENGL_DEBUG_CONTEXT`.
    pub fn is_opengl_debug_context(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::OPENGL_DEBUG_CONTEXT) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `OPENGL_PROFILE`.
    pub fn get_opengl_profile(&self) -> c_int {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::OPENGL_PROFILE) }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `RESIZABLE`.
    pub fn is_resizable(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::RESIZABLE) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `VISIBLE`.
    pub fn is_visible(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::VISIBLE) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetWindowAttrib` called with `DECORATED`.
    pub fn is_decorated(&self) -> bool {
        unsafe { ffi::glfwGetWindowAttrib(self.ptr, ffi::DECORATED) == ffi::TRUE }
    }

    /// Wrapper for `glfwSetWindowPosCallback`.
    pub fn set_pos_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetWindowPosCallback, window_pos_callback);
    }

    /// Starts or stops polling for all available events
    pub fn set_all_polling(&mut self, should_poll: bool) {
        self.set_pos_polling(should_poll);
        self.set_size_polling(should_poll);
        self.set_close_polling(should_poll);
        self.set_refresh_polling(should_poll);
        self.set_focus_polling(should_poll);
        self.set_iconify_polling(should_poll);
        self.set_framebuffer_size_polling(should_poll);
        self.set_key_polling(should_poll);
        self.set_char_polling(should_poll);
        self.set_char_mods_polling(should_poll);
        self.set_mouse_button_polling(should_poll);
        self.set_cursor_pos_polling(should_poll);
        self.set_cursor_enter_polling(should_poll);
        self.set_scroll_polling(should_poll);
        self.set_drag_and_drop_polling(should_poll);
    }

    /// Wrapper for `glfwSetWindowSizeCallback`.
    pub fn set_size_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetWindowSizeCallback, window_size_callback);
    }

    /// Wrapper for `glfwSetWindowCloseCallback`.
    pub fn set_close_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetWindowCloseCallback, window_close_callback);
    }

    /// Wrapper for `glfwSetWindowRefreshCallback`.
    pub fn set_refresh_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetWindowRefreshCallback, window_refresh_callback);
    }

    /// Wrapper for `glfwSetWindowFocusCallback`.
    pub fn set_focus_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetWindowFocusCallback, window_focus_callback);
    }

    /// Wrapper for `glfwSetWindowIconifyCallback`.
    pub fn set_iconify_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetWindowIconifyCallback, window_iconify_callback);
    }

    /// Wrapper for `glfwSetFramebufferSizeCallback`.
    pub fn set_framebuffer_size_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetFramebufferSizeCallback, framebuffer_size_callback);
    }

    /// Wrapper for `glfwSetFramebufferSizeCallback`.
    pub fn set_drag_and_drop_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetDropCallback, drop_callback);
    }

    /// Wrapper for `glfwGetInputMode` called with `CURSOR`.
    pub fn get_cursor_mode(&self) -> CursorMode {
        unsafe { mem::transmute(ffi::glfwGetInputMode(self.ptr, ffi::CURSOR)) }
    }

    /// Wrapper for `glfwSetInputMode` called with `CURSOR`.
    pub fn set_cursor_mode(&mut self, mode: CursorMode) {
        unsafe { ffi::glfwSetInputMode(self.ptr, ffi::CURSOR, mode as c_int); }
    }

    /// Wrapper for `glfwSetCursor` using `Cursor`
    ///
    /// The window will take ownership of the cursor, and will not Drop it
    /// until it is replaced or the window itself is destroyed.
    ///
    /// Returns the previously set Cursor or None if no cursor was set.
    pub fn set_cursor(&mut self, cursor: Option<Cursor>) -> Option<Cursor> {
        let previous = mem::replace(&mut self.current_cursor, cursor);

        unsafe {
            ffi::glfwSetCursor(self.ptr, match self.current_cursor {
                Some(ref cursor) => cursor.ptr,
                None => ptr::null_mut()
            })
        }

        previous
    }

    /// Sets the window icon from the given images by called `glfwSetWindowIcon`
    ///
    /// Multiple images can be specified for allowing the OS to choose the best size where necessary.
    ///
    /// Example:
    ///
    /// ```ignore
    ///if let DynamicImage::ImageRgba8(icon) = image::open("examples/icon.png").unwrap() {
    ///    window.set_icon(vec![
    ///        imageops::resize(&icon, 16, 16, image::imageops::Lanczos3),
    ///        imageops::resize(&icon, 32, 32, image::imageops::Lanczos3),
    ///        imageops::resize(&icon, 48, 48, image::imageops::Lanczos3)
    ///    ]);
    ///}
    /// ```
    #[cfg(feature = "image")]
    pub fn set_icon(&mut self, images: Vec<image::RgbaImage>) {
        // When the images are turned into Vecs, the lifetimes of them go into the Vec lifetime
        // So they need to be kept until the function ends.
        let image_data : Vec<(Vec<_>, u32, u32)> = images.into_iter().map(|image| {
            let (width, height) = image.dimensions();

            (image.into_vec(), width, height)
        }).collect();

        let glfw_images: Vec<ffi::GLFWimage> = image_data.iter().map(|ref data| {
            ffi::GLFWimage {
                width:  data.1 as c_int,
                height: data.2 as c_int,
                pixels: data.0.as_ptr() as *const c_uchar
            }
        }).collect();

        unsafe {
            ffi::glfwSetWindowIcon(self.ptr, glfw_images.len() as c_int, glfw_images.as_ptr() as *const ffi::GLFWimage)
        }
    }

    /// Sets the window icon via `glfwSetWindowIcon` from a set a set of vectors
    /// containing pixels in RGBA format (one pixel per 32-bit integer)
    pub fn set_icon_from_pixels(&mut self, images: Vec<PixelImage>) {
        let glfw_images: Vec<ffi::GLFWimage> = images.iter().map(|image: &PixelImage| {
            ffi::GLFWimage {
                width: image.width as c_int,
                height: image.height as c_int,
                pixels: image.pixels.as_ptr() as *const c_uchar
            }
        }).collect();

        unsafe {
            ffi::glfwSetWindowIcon(self.ptr, glfw_images.len() as c_int, glfw_images.as_ptr() as *const ffi::GLFWimage)
        }
    }

    /// Wrapper for `glfwGetInputMode` called with `STICKY_KEYS`.
    pub fn has_sticky_keys(&self) -> bool {
        unsafe { ffi::glfwGetInputMode(self.ptr, ffi::STICKY_KEYS) == ffi::TRUE }
    }

    /// Wrapper for `glfwSetInputMode` called with `STICKY_KEYS`.
    pub fn set_sticky_keys(&mut self, value: bool) {
        unsafe { ffi::glfwSetInputMode(self.ptr, ffi::STICKY_KEYS, value as c_int); }
    }

    /// Wrapper for `glfwGetInputMode` called with `STICKY_MOUSE_BUTTONS`.
    pub fn has_sticky_mouse_buttons(&self) -> bool {
        unsafe { ffi::glfwGetInputMode(self.ptr, ffi::STICKY_MOUSE_BUTTONS) == ffi::TRUE }
    }

    /// Wrapper for `glfwSetInputMode` called with `STICKY_MOUSE_BUTTONS`.
    pub fn set_sticky_mouse_buttons(&mut self, value: bool) {
        unsafe { ffi::glfwSetInputMode(self.ptr, ffi::STICKY_MOUSE_BUTTONS, value as c_int); }
    }

    /// Wrapper for `glfwGetKey`.
    pub fn get_key(&self, key: Key) -> Action {
        unsafe { mem::transmute(ffi::glfwGetKey(self.ptr, key as c_int)) }
    }

    /// Wrapper for `glfwGetMouseButton`.
    pub fn get_mouse_button(&self, button: MouseButton) -> Action {
        unsafe { mem::transmute(ffi::glfwGetMouseButton(self.ptr, button as c_int)) }
    }

    /// Wrapper for `glfwGetCursorPos`.
    pub fn get_cursor_pos(&self) -> (f64, f64) {
        unsafe {
            let mut xpos = 0.0;
            let mut ypos = 0.0;
            ffi::glfwGetCursorPos(self.ptr, &mut xpos, &mut ypos);
            (xpos as f64, ypos as f64)
        }
    }

    /// Wrapper for `glfwSetCursorPos`.
    pub fn set_cursor_pos(&mut self, xpos: f64, ypos: f64) {
        unsafe { ffi::glfwSetCursorPos(self.ptr, xpos as c_double, ypos as c_double); }
    }

    /// Wrapper for `glfwSetKeyCallback`.
    pub fn set_key_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetKeyCallback, key_callback);
    }

    /// Wrapper for `glfwSetCharCallback`.
    pub fn set_char_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetCharCallback, char_callback);
    }

    /// Wrapper for `glfwSetCharModsCallback`
    pub fn set_char_mods_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetCharModsCallback, char_mods_callback);
    }

    /// Wrapper for `glfwSetMouseButtonCallback`.
    pub fn set_mouse_button_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetMouseButtonCallback, mouse_button_callback);
    }

    /// Wrapper for `glfwSetCursorPosCallback`.
    pub fn set_cursor_pos_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetCursorPosCallback, cursor_pos_callback);
    }

    /// Wrapper for `glfwSetCursorEnterCallback`.
    pub fn set_cursor_enter_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetCursorEnterCallback, cursor_enter_callback);
    }

    /// Wrapper for `glfwSetScrollCallback`.
    pub fn set_scroll_polling(&mut self, should_poll: bool) {
        set_window_callback!(self, should_poll, glfwSetScrollCallback, scroll_callback);
    }

    /// Wrapper for `glfwGetClipboardString`.
    pub fn set_clipboard_string(&mut self, string: &str) {
        unsafe {
            with_c_str(string, |string| {
                ffi::glfwSetClipboardString(self.ptr, string);
            });
        }
    }

    /// Wrapper for `glfwGetClipboardString`.
    pub fn get_clipboard_string(&self) -> String {
        unsafe { string_from_c_str(ffi::glfwGetClipboardString(self.ptr)) }
    }

    /// Wrapper for `glfwGetWin32Window`
    #[cfg(target_os="windows")]
    pub fn get_win32_window(&self) -> *mut c_void {
        unsafe { ffi::glfwGetWin32Window(self.ptr) }
    }

    /// Wrapper for `glfwGetWGLContext`
    #[cfg(target_os="windows")]
    pub fn get_wgl_context(&self) -> *mut c_void {
        unsafe { ffi::glfwGetWGLContext(self.ptr) }
    }

    /// Wrapper for `glfwGetCocoaWindow`
    #[cfg(target_os="macos")]
    pub fn get_cocoa_window(&self) -> *mut c_void {
        unsafe { ffi::glfwGetCocoaWindow(self.ptr) }
    }

    /// Wrapper for `glfwGetNSGLContext`
    #[cfg(target_os="macos")]
    pub fn get_nsgl_context(&self) -> *mut c_void {
        unsafe { ffi::glfwGetNSGLContext(self.ptr) }
    }

    /// Wrapper for `glfwGetX11Window`
    #[cfg(target_os="linux")]
    pub fn get_x11_window(&self) -> *mut c_void {
        unsafe { ffi::glfwGetX11Window(self.ptr) }
    }

    /// Wrapper for `glfwGetGLXContext`
    #[cfg(target_os="linux")]
    pub fn get_glx_context(&self) -> *mut c_void {
        unsafe { ffi::glfwGetGLXContext(self.ptr) }
    }
}

impl Drop for Window {
    /// Closes the window and performs the necessary cleanups. This will block
    /// until all associated `RenderContext`s were also dropped, and emit a
    /// `debug!` message to that effect.
    ///
    /// Wrapper for `glfwDestroyWindow`.
    fn drop(&mut self) {
        drop(self.drop_sender.take());

        // Check if all senders from the child `RenderContext`s have hung up.
        if self.drop_receiver.try_recv() != Err(std::sync::mpsc::TryRecvError::Disconnected) {
            debug!("Attempted to drop a Window before the `RenderContext` was dropped.");
            debug!("Blocking until the `RenderContext` was dropped.");
            let _ = self.drop_receiver.recv();
        }

        if !self.ptr.is_null() {
            unsafe {
                let _: Box<Sender<(f64, WindowEvent)>> = mem::transmute(ffi::glfwGetWindowUserPointer(self.ptr));
            }
        }

        if !self.is_shared {
            unsafe { ffi::glfwDestroyWindow(self.ptr); }
        }
    }
}

/// A rendering context that can be shared between tasks.
pub struct RenderContext {
    ptr: *mut ffi::GLFWwindow,
    /// As long as this sender is alive, it is not safe to drop the parent
    /// `Window`.
    #[allow(dead_code)]
    drop_sender: Sender<()>,
}

unsafe impl Send for RenderContext {}

/// Methods common to renderable contexts
pub trait Context {
    /// Returns the pointer to the underlying `GLFWwindow`.
    fn window_ptr(&self) -> *mut ffi::GLFWwindow;

    /// Swaps the front and back buffers of the window. If the swap interval is
    /// greater than zero, the GPU driver waits the specified number of screen
    /// updates before swapping the buffers.
    ///
    /// Wrapper for `glfwSwapBuffers`.
    fn swap_buffers(&mut self) {
        let ptr = self.window_ptr();
        unsafe { ffi::glfwSwapBuffers(ptr); }
    }

    /// Returns `true` if the window is the current context.
    fn is_current(&self) -> bool {
        self.window_ptr() == unsafe { ffi::glfwGetCurrentContext() }
    }

    /// Wrapper for `glfwMakeContextCurrent`
    fn make_current(&mut self) {
        let ptr = self.window_ptr();
        unsafe { ffi::glfwMakeContextCurrent(ptr); }
    }
}

impl Context for Window {
    fn window_ptr(&self) -> *mut ffi::GLFWwindow { self.ptr }
}

impl Context for RenderContext {
    fn window_ptr(&self) -> *mut ffi::GLFWwindow { self.ptr }
}

/// Wrapper for `glfwMakeContextCurrent`.
pub fn make_context_current(context: Option<&Context>) {
    match context {
        Some(ctx) => unsafe { ffi::glfwMakeContextCurrent(ctx.window_ptr()) },
        None      => unsafe { ffi::glfwMakeContextCurrent(ptr::null_mut()) },
    }
}

enum_from_primitive! {
    /// Joystick identifier tokens.
    #[repr(i32)]
    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
    pub enum JoystickId {
        Joystick1       = ffi::JOYSTICK_1,
        Joystick2       = ffi::JOYSTICK_2,
        Joystick3       = ffi::JOYSTICK_3,
        Joystick4       = ffi::JOYSTICK_4,
        Joystick5       = ffi::JOYSTICK_5,
        Joystick6       = ffi::JOYSTICK_6,
        Joystick7       = ffi::JOYSTICK_7,
        Joystick8       = ffi::JOYSTICK_8,
        Joystick9       = ffi::JOYSTICK_9,
        Joystick10      = ffi::JOYSTICK_10,
        Joystick11      = ffi::JOYSTICK_11,
        Joystick12      = ffi::JOYSTICK_12,
        Joystick13      = ffi::JOYSTICK_13,
        Joystick14      = ffi::JOYSTICK_14,
        Joystick15      = ffi::JOYSTICK_15,
        Joystick16      = ffi::JOYSTICK_16,
    }
}

/// A joystick handle.
#[derive(Copy, Clone)]
pub struct Joystick {
    pub id: JoystickId,
    pub glfw: Glfw,
}

/// Joystick events.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum JoystickEvent {
    Connected                   = ffi::CONNECTED,
    Disconnected                = ffi::DISCONNECTED,
}

/// An joystick callback. This can be supplied with some user data to be passed
/// to the callback function when it is triggered.
pub type JoystickCallback<UserData> = Callback<fn(JoystickId, JoystickEvent, &UserData), UserData>;

impl Joystick {
    /// Wrapper for `glfwJoystickPresent`.
    pub fn is_present(&self) -> bool {
        unsafe { ffi::glfwJoystickPresent(self.id as c_int) == ffi::TRUE }
    }

    /// Wrapper for `glfwGetJoystickAxes`.
    pub fn get_axes(&self) -> Vec<f32> {
        unsafe {
            let mut count = 0;
            let ptr = ffi::glfwGetJoystickAxes(self.id as c_int, &mut count);
            slice::from_raw_parts(ptr, count as usize).iter().map(|&a| a as f32).collect()
        }
    }

    /// Wrapper for `glfwGetJoystickButtons`.
    pub fn get_buttons(&self) -> Vec<c_int> {
        unsafe {
            let mut count = 0;
            let ptr = ffi::glfwGetJoystickButtons(self.id as c_int, &mut count);
            slice::from_raw_parts(ptr, count as usize).iter().map(|&b| b as c_int).collect()
        }
    }

    /// Wrapper for `glfwGetJoystickName`.
    pub fn get_name(&self) -> String {
        unsafe { string_from_c_str(ffi::glfwGetJoystickName(self.id as c_int)) }
    }
}