Repository: PistonDevelopers/glfw-rs (language: rust)

Issue:
Enforce thread safety at the type level.
According to the GLFW docs, the following functions are thread-safe:
- `glfwGetVersion`
- `glfwGetVersionString`
- `glfwWindowShouldClose`
- `glfwSetWindowShouldClose`
- `glfwSetWindowUserPointer`
- `glfwGetWindowUserPointer`
- `glfwPostEmptyEvent`
- `glfwGetTime`
- `glfwMakeContextCurrent`
- `glfwGetCurrentContext`
- `glfwSwapBuffers`
- `glfwSwapInterval`
- `glfwExtensionSupported`
- `glfwGetProcAddress`

and the following are thread-unsafe:
- `glfwInit`
- `glfwTerminate`
- `glfwSetErrorCallback`
- `glfwGetMonitors`
- `glfwGetPrimaryMonitor`
- `glfwGetMonitorPos`
- `glfwGetMonitorPhysicalSize`
- `glfwGetMonitorName`
- `glfwSetMonitorCallback`
- `glfwGetVideoModes`
- `glfwGetVideoMode`
- `glfwSetGamma`
- `glfwGetGammaRamp`
- `glfwSetGammaRamp`
- `glfwDefaultWindowHints`
- `glfwWindowHint`
- `glfwCreateWindow`
- `glfwDestroyWindow`
- `glfwSetWindowTitle`
- `glfwGetWindowPos`
- `glfwSetWindowPos`
- `glfwGetWindowSize`
- `glfwSetWindowSize`
- `glfwGetFramebufferSize`
- `glfwIconifyWindow`
- `glfwRestoreWindow`
- `glfwShowWindow`
- `glfwHideWindow`
- `glfwGetWindowMonitor`
- `glfwGetWindowAttrib`
- `glfwSetWindowPosCallback`
- `glfwSetWindowSizeCallback`
- `glfwSetWindowCloseCallback`
- `glfwSetWindowRefreshCallback`
- `glfwSetWindowFocusCallback`
- `glfwSetWindowIconifyCallback`
- `glfwSetFramebufferSizeCallback`
- `glfwPollEvents`
- `glfwWaitEvents`
- `glfwGetInputMode`
- `glfwSetInputMode`
- `glfwGetKey`
- `glfwGetMouseButton`
- `glfwGetCursorPos`
- `glfwSetCursorPos`
- `glfwCreateCursor`
- `glfwDestroyCursor`
- `glfwSetCursor`
- `glfwSetKeyCallback`
- `glfwSetCharCallback`
- `glfwSetMouseButtonCallback`
- `glfwSetCursorPosCallback`
- `glfwSetCursorEnterCallback`
- `glfwSetScrollCallback`
- `glfwSetDropCallback`
- `glfwJoystickPresent`
- `glfwGetJoystickAxes`
- `glfwGetJoystickButtons`
- `glfwGetJoystickName`
- `glfwSetClipboardString`
- `glfwGetClipboardString`
- `glfwSetTime`

The rest of the functions are safe, as far as I know.

A possible API:

``` rust
/// A type that lives on the main thread
#[non_sendable]
pub enum Glfw {}

impl Glfw {
    pub fn init() -> Option<Glfw> { ... }
    pub fn poll_events() { ... }
    pub fn create_window(...) -> Option<Window> { ... }
}
```

Client code:

``` rust
let glfw = Glfw::init().unwrap();
let window = glfw.create_window(...);
```



Discussion / hints:
`glfwDestroyWindow` is going to be annoying, since it pretty much makes `drop` need to be done on the creator's thread.

Ugh, indeed. :[

``` rust
#[non_sendable]
pub struct Glfw {
    priv drop_port: Port<*ffi::GLFWwindow>,
}
```

?

The problem with adding a port is that it is a leaky abstraction, the user has to `recv()` on it for it to work... unless you hide the drop_port check inside of the poll_events.

I don't think there is going to be a nice way to abstract this otherwise glfw would already be doing it. If you look at libsdl2 or sfml they have identical limitations, since it is a limitation created by the underlying os apis and not the library.

I think this change is a good idea. The worst case is forcing the user to destroy the window.

Looking at the docs, There is lots of limitations that we can't easily abstract away anyway.

```
Note
    This function may only be called from the main thread.
    This function may not be called from a callback.
    If the window's context is current on the main thread, it is detached before being destroyed.

Warning
    The window's context must not be current on any other thread.
```

@larsbergstrom Thoughts?

I agree with @csherratt that this is a good change. 

The only other thing we've done in Servo is add dynamic checks to certain functions (e.g., those that access _native_ thread-locals) to ensure that they are in the proper thread configuration. That has the advantage of not requiring an extra object, but I think I like your solution better for this particular situation.

> That has the advantage of not requiring an extra object, but I think I like your solution better for this particular situation.

I guess I just like static checks :D. We could make the `Glfw` object cloneable too - just not sendable.

@csherratt at least glfw actually documents this stuff. You get no such information with SDL, which is a massive pain.

@bjz I'm glad it is well documented. The abstraction problems are created by platform vendors not glfw or sdl.

I'm wondering if we should just force windows to exist on the main thread, then have users build their own abstractions on top of it.

@bjz, How can that be enforced? Do we fail!() if the window is dropped without being cleaned up first?

Well the windows would be marked as non-sharable, and because they could only be constructed using the glfw context thing, they would have to live on that same thread.

Ok, updated the issue with the clarified list of thread safe/unsafe functions after seeing this recent commit: glfw/glfw@0e20577af4ce63b33199471325c62daa0c39e92a

I think we could probably enforce threading with the NoShare, NoSend traits. Windows can only be created by a glfw object. Windows are marked with NoShare and NoSend. If they want to access a window from another thread we create a culled version of the Window called WindowShareable that can only access the thread safe functions.


Fix patch:
diff --git a/README.md b/README.md
index 051096ad..5361bf37 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
 
 GLFW bindings and wrapper for The Rust Programming Language.
 
-## Example code
+## Example
 
 ~~~rust
 extern crate native;
@@ -32,31 +32,41 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    // Set up an error callback
-    let errors = glfw::get_errors().unwrap();
-
-    // Initialize the library
-    glfw::start(proc() {
-        // Create a windowed mode window and its OpenGL context
-        let window = glfw::Window::create(300, 300, "Hello this is window", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
-
-        // Make the window's context current
-        window.make_context_current();
-
-        // 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();
-            glfw::fail_on_error(&errors);
+    let glfw = glfw::init().unwrap();
+    let errors = glfw.get_errors().unwrap();
+
+    // Create a windowed mode window and its OpenGL context
+    let window = glfw.create_window(300, 300, "Hello this is window", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
+
+    // Make the window's context current
+    window.make_context_current();
+
+    // 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();
+        glfw::fail_on_error(&errors);
+        for (_, event) in glfw::flush_messages(&events) {
+            println!("{}", event);
+            match event {
+                glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _) => {
+                    window.set_should_close(true)
+                },
+                _ => {},
+            }
         }
-    });
+    }
 }
 ~~~
 
+## Documentation
+
+The [API docs](http://rust-ci.org/bjz/glfw-rs/doc/glfw/) are hosted on Rust CI.
+
 ## Prerequisites
 
 To build glfw-rs you will need to [build glfw](http://www.glfw.org/docs/latest/compile.html) using the [BUILD_SHARED_LIBS](http://www.glfw.org/docs/latest/compile.html#compile_options) option.
diff --git a/src/examples/clipboard.rs b/src/examples/clipboard.rs
index fb712724..3c87ac07 100644
--- a/src/examples/clipboard.rs
+++ b/src/examples/clipboard.rs
@@ -22,24 +22,23 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    let errors = glfw::get_errors().unwrap();
+    let glfw = glfw::init().unwrap();
+    let errors = glfw.get_errors().unwrap();
 
-    glfw::start(proc() {
-        let (window, events) = glfw::Window::create(300, 300, "Clipboard Test", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
+    let (window, events) = glfw.create_window(300, 300, "Clipboard Test", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
 
-        window.set_key_polling(true);
-        window.make_context_current();
-        glfw::set_swap_interval(1);
+    window.set_key_polling(true);
+    window.make_context_current();
+    glfw.set_swap_interval(1);
 
-        while !window.should_close() {
-            glfw::poll_events();
-            glfw::fail_on_error(&errors);
-            for (_, event) in events.flush_events() {
-                handle_window_event(&window, event);
-            }
+    while !window.should_close() {
+        glfw.poll_events();
+        glfw::fail_on_error(&errors);
+        for (_, event) in events.flush_events() {
+            handle_window_event(&window, event);
         }
-    });
+    }
 }
 
 #[cfg(target_os = "macos")]
diff --git a/src/examples/cursor.rs b/src/examples/cursor.rs
index 1dd559d8..ff345a87 100644
--- a/src/examples/cursor.rs
+++ b/src/examples/cursor.rs
@@ -22,26 +22,25 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-   let errors = glfw::get_errors().unwrap();
+    let glfw = glfw::init().unwrap();
+    let errors = glfw.get_errors().unwrap();
 
-    glfw::start(proc() {
-        let (window, events) = glfw::Window::create(800, 600, "Hello, I am a window.", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
+    let (window, events) = glfw.create_window(800, 600, "Hello, I am a window.", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
 
-        window.set_cursor_mode(glfw::CursorDisabled);
-        window.make_context_current();
+    window.set_cursor_mode(glfw::CursorDisabled);
+    window.make_context_current();
 
-        window.set_cursor_pos_polling(true);
-        window.set_key_polling(true);
+    window.set_cursor_pos_polling(true);
+    window.set_key_polling(true);
 
-        while !window.should_close() {
-            glfw::poll_events();
-            glfw::fail_on_error(&errors);
-            for (_, event) in events.flush_events() {
-                handle_window_event(&window, event);
-            }
+    while !window.should_close() {
+        glfw.poll_events();
+        glfw::fail_on_error(&errors);
+        for (_, event) in events.flush_events() {
+            handle_window_event(&window, event);
         }
-    });
+    }
 }
 
 fn handle_window_event(window: &glfw::Window, event: glfw::WindowEvent) {
diff --git a/src/examples/defaults.rs b/src/examples/defaults.rs
index f50f65e3..f33ac458 100644
--- a/src/examples/defaults.rs
+++ b/src/examples/defaults.rs
@@ -22,47 +22,46 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    glfw::start(proc() {
-        glfw::window_hint(glfw::Visible(true));
-
-        let (window, _) = glfw::Window::create(640, 480, "Defaults", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
-
-        window.make_context_current();
-
-        let (width, height) = window.get_size();
-        println!("window size: ({}, {})", width, height);
-
-        println!("Context version: {:s}",         window.get_context_version().to_str());
-        println!("OpenGL forward compatible: {}", window.is_opengl_forward_compat());
-        println!("OpenGL debug context: {}",      window.is_opengl_debug_context());
-        println!("OpenGL profile: {}",            window.get_opengl_profile());
-
-        let gl_params = [
-            (gl::RED_BITS,          None,   "red bits"          ),
-            (gl::GREEN_BITS,        None,   "green bits"        ),
-            (gl::BLUE_BITS,         None,   "blue bits"         ),
-            (gl::ALPHA_BITS,        None,   "alpha bits"        ),
-            (gl::DEPTH_BITS,        None,   "depth bits"        ),
-            (gl::STENCIL_BITS,      None,   "stencil bits"      ),
-            (gl::ACCUM_RED_BITS,    None,   "accum red bits"    ),
-            (gl::ACCUM_GREEN_BITS,  None,   "accum green bits"  ),
-            (gl::ACCUM_BLUE_BITS,   None,   "accum blue bits"   ),
-            (gl::ACCUM_ALPHA_BITS,  None,   "accum alpha bits"  ),
-            (gl::STEREO,            None,   "stereo"            ),
-            (gl::SAMPLES_ARB,       Some("GL_ARB_multisample"), "FSAA samples" ),
-        ];
-
-        for &(param, ext, name) in gl_params.iter() {
-            if ext.map_or(true, |s| {
-                glfw::extension_supported(s)
-            }) {
-                let value = 0;
-                unsafe { gl::GetIntegerv(param, &value) };
-                println!("OpenGL {:s}: {}", name, value);
-            };
-        }
-    });
+    let glfw = glfw::init().unwrap();
+    glfw.window_hint(glfw::Visible(true));
+
+    let (window, _) = glfw.create_window(640, 480, "Defaults", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
+
+    window.make_context_current();
+
+    let (width, height) = window.get_size();
+    println!("window size: ({}, {})", width, height);
+
+    println!("Context version: {:s}",         window.get_context_version().to_str());
+    println!("OpenGL forward compatible: {}", window.is_opengl_forward_compat());
+    println!("OpenGL debug context: {}",      window.is_opengl_debug_context());
+    println!("OpenGL profile: {}",            window.get_opengl_profile());
+
+    let gl_params = [
+        (gl::RED_BITS,          None,   "red bits"          ),
+        (gl::GREEN_BITS,        None,   "green bits"        ),
+        (gl::BLUE_BITS,         None,   "blue bits"         ),
+        (gl::ALPHA_BITS,        None,   "alpha bits"        ),
+        (gl::DEPTH_BITS,        None,   "depth bits"        ),
+        (gl::STENCIL_BITS,      None,   "stencil bits"      ),
+        (gl::ACCUM_RED_BITS,    None,   "accum red bits"    ),
+        (gl::ACCUM_GREEN_BITS,  None,   "accum green bits"  ),
+        (gl::ACCUM_BLUE_BITS,   None,   "accum blue bits"   ),
+        (gl::ACCUM_ALPHA_BITS,  None,   "accum alpha bits"  ),
+        (gl::STEREO,            None,   "stereo"            ),
+        (gl::SAMPLES_ARB,       Some("GL_ARB_multisample"), "FSAA samples" ),
+    ];
+
+    for &(param, ext, name) in gl_params.iter() {
+        if ext.map_or(true, |s| {
+            glfw.extension_supported(s)
+        }) {
+            let value = 0;
+            unsafe { gl::GetIntegerv(param, &value) };
+            println!("OpenGL {:s}: {}", name, value);
+        };
+    }
 }
 
 mod gl {
diff --git a/src/examples/events.rs b/src/examples/events.rs
index 891f62b2..fb90fa21 100644
--- a/src/examples/events.rs
+++ b/src/examples/events.rs
@@ -22,47 +22,46 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    let errors = glfw::get_errors().unwrap();
+    let glfw = glfw::init().unwrap();
+    let errors = glfw.get_errors().unwrap();
 
-    glfw::start(proc() {
-        glfw::window_hint(glfw::Resizable(true));
+    glfw.window_hint(glfw::Resizable(true));
 
-        let (window, events) = glfw::Window::create(800, 600, "Hello, I am a window.", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
+    let (window, events) = glfw.create_window(800, 600, "Hello, I am a window.", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
 
-        window.set_sticky_keys(true);
+    window.set_sticky_keys(true);
 
-        // Polling of events can be turned on and off by the specific event type
-        window.set_pos_polling(true);
-        window.set_all_polling(true);
-        window.set_size_polling(true);
-        window.set_close_polling(true);
-        window.set_refresh_polling(true);
-        window.set_focus_polling(true);
-        window.set_iconify_polling(true);
-        window.set_framebuffer_size_polling(true);
-        window.set_key_polling(true);
-        window.set_char_polling(true);
-        window.set_mouse_button_polling(true);
-        window.set_cursor_pos_polling(true);
-        window.set_cursor_enter_polling(true);
-        window.set_scroll_polling(true);
+    // Polling of events can be turned on and off by the specific event type
+    window.set_pos_polling(true);
+    window.set_all_polling(true);
+    window.set_size_polling(true);
+    window.set_close_polling(true);
+    window.set_refresh_polling(true);
+    window.set_focus_polling(true);
+    window.set_iconify_polling(true);
+    window.set_framebuffer_size_polling(true);
+    window.set_key_polling(true);
+    window.set_char_polling(true);
+    window.set_mouse_button_polling(true);
+    window.set_cursor_pos_polling(true);
+    window.set_cursor_enter_polling(true);
+    window.set_scroll_polling(true);
 
-        // Alternatively, all event types may be set to poll at once. Note that
-        // in this example, this call is redundant as all events have been set
-        // to poll in the above code.
-        window.set_all_polling(true);
+    // Alternatively, all event types may be set to poll at once. Note that
+    // in this example, this call is redundant as all events have been set
+    // to poll in the above code.
+    window.set_all_polling(true);
 
-        window.make_context_current();
+    window.make_context_current();
 
-        while !window.should_close() {
-            glfw::poll_events();
-            glfw::fail_on_error(&errors);
-            for event in events.flush_events() {
-                handle_window_event(&window, event);
-            }
+    while !window.should_close() {
+        glfw.poll_events();
+        glfw::fail_on_error(&errors);
+        for event in events.flush_events() {
+            handle_window_event(&window, event);
         }
-    });
+    }
 }
 
 fn handle_window_event(window: &glfw::Window, (time, event): (f64, glfw::WindowEvent)) {
diff --git a/src/examples/manual_init.rs b/src/examples/manual_init.rs
deleted file mode 100644
index f0dcd2f7..00000000
--- a/src/examples/manual_init.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2013 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.
-
-extern crate native;
-extern crate glfw;
-
-#[start]
-fn start(argc: int, argv: **u8) -> int {
-    // GLFW must run on the main platform thread
-    native::start(argc, argv, main)
-}
-
-fn main() {
-    let errors = glfw::get_errors().unwrap();
-
-    if glfw::init().is_err() {
-        fail!(~"Failed to initialize GLFW");
-    } else {
-        let (window, events) = glfw::Window::create(300, 300, "Hello this is window", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
-
-        window.set_key_polling(true);
-        window.make_context_current();
-
-        while !window.should_close() {
-            glfw::poll_events();
-            glfw::fail_on_error(&errors);
-            for (_, event) in events.flush_events() {
-                handle_window_event(&window, event);
-            }
-        }
-    }
-}
-
-fn handle_window_event(window: &glfw::Window, event: glfw::WindowEvent) {
-    match event {
-        glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _) => {
-            window.set_should_close(true)
-        }
-        _ => {}
-    }
-}
diff --git a/src/examples/modes.rs b/src/examples/modes.rs
index f6f8dd71..46dfb3a2 100644
--- a/src/examples/modes.rs
+++ b/src/examples/modes.rs
@@ -22,19 +22,19 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    glfw::start(proc() {
-        let _ = glfw::Monitor::get_primary().map(|monitor| {
-            println!("{}:", monitor.get_name());
-            println!("    {}\n", monitor.get_video_mode().unwrap());
-        });
+    let glfw = glfw::init().unwrap();
 
-        println!("Available monitors\n\
-                  ------------------");
-        for monitor in glfw::Monitor::get_connected().iter() {
-            println!("{}:", monitor.get_name());
-            for mode in monitor.get_video_modes().iter() {
-                println!("  {}", *mode);
-            }
-        }
+    let _ = glfw.get_primary_monitor().map(|monitor| {
+        println!("{}:", monitor.get_name());
+        println!("    {}\n", monitor.get_video_mode().unwrap());
     });
+
+    println!("Available monitors\n\
+              ------------------");
+    for monitor in glfw.get_connected_monitors().iter() {
+        println!("{}:", monitor.get_name());
+        for mode in monitor.get_video_modes().iter() {
+            println!("  {}", *mode);
+        }
+    }
 }
diff --git a/src/examples/title.rs b/src/examples/title.rs
index dbc8ee72..b79d5070 100644
--- a/src/examples/title.rs
+++ b/src/examples/title.rs
@@ -22,24 +22,23 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    let errors = glfw::get_errors().unwrap();
+    let glfw = glfw::init().unwrap();
+    let errors = glfw.get_errors().unwrap();
 
-    glfw::start(proc() {
-        let (window, events) = glfw::Window::create(400, 400, "English 日本語 русский язык 官話", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
+    let (window, events) = glfw.create_window(400, 400, "English 日本語 русский язык 官話", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
 
-        window.set_key_polling(true);
-        window.make_context_current();
-        glfw::set_swap_interval(1);
+    window.set_key_polling(true);
+    window.make_context_current();
+    glfw.set_swap_interval(1);
 
-        while !window.should_close() {
-            glfw::poll_events();
-            glfw::fail_on_error(&errors);
-            for (_, event) in events.flush_events() {
-                handle_window_event(&window, event);
-            }
+    while !window.should_close() {
+        glfw.poll_events();
+        glfw::fail_on_error(&errors);
+        for (_, event) in events.flush_events() {
+            handle_window_event(&window, event);
         }
-    });
+    }
 }
 
 fn handle_window_event(window: &glfw::Window, event: glfw::WindowEvent) {
diff --git a/src/examples/window.rs b/src/examples/window.rs
index 3dfce2e4..39acf11f 100644
--- a/src/examples/window.rs
+++ b/src/examples/window.rs
@@ -22,23 +22,22 @@ fn start(argc: int, argv: **u8) -> int {
 }
 
 fn main() {
-    let errors = glfw::get_errors().unwrap();
+    let glfw = glfw::init().unwrap();
+    let errors = glfw.get_errors().unwrap();
 
-    glfw::start(proc() {
-        let (window, events) = glfw::Window::create(300, 300, "Hello this is window", glfw::Windowed)
-            .expect("Failed to create GLFW window.");
+    let (window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::Windowed)
+        .expect("Failed to create GLFW window.");
 
-        window.set_key_polling(true);
-        window.make_context_current();
+    window.set_key_polling(true);
+    window.make_context_current();
 
-        while !window.should_close() {
-            glfw::poll_events();
-            glfw::fail_on_error(&errors);
-            for (_, event) in events.flush_events() {
-                handle_window_event(&window, event);
-            }
+    while !window.should_close() {
+        glfw.poll_events();
+        glfw::fail_on_error(&errors);
+        for (_, event) in events.flush_events() {
+            handle_window_event(&window, event);
         }
-    });
+    }
 }
 
 fn handle_window_event(window: &glfw::Window, event: glfw::WindowEvent) {
diff --git a/src/lib/callbacks.rs b/src/lib/callbacks.rs
index a4b7d334..765d2143 100644
--- a/src/lib/callbacks.rs
+++ b/src/lib/callbacks.rs
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Private functions and items to be used with the high-level library wrapper.
+//! Private callback support functions.
 
 use std::cast;
 use std::libc::*;
@@ -32,7 +32,7 @@ pub extern "C" fn error_callback(error: c_int, description: *c_char) {
     unsafe {
         local_data::get(ERROR_SENDER, |sender| {
             sender.as_ref().expect("ERROR_SENDER not initialized")
-                  .send((get_time(), cast::transmute(error), str::raw::from_c_str(description)))
+                  .send((ffi::glfwGetTime() as f64, cast::transmute(error), str::raw::from_c_str(description)))
         });
     }
 }
@@ -70,14 +70,14 @@ unsafe fn get_sender<'a>(window: &'a *ffi::GLFWwindow) -> &'a Sender<(f64, Windo
 
 macro_rules! window_callback(
     (fn $name:ident () => $event:ident) => (
-         pub extern "C" fn $name(window: *ffi::GLFWwindow) {
-            unsafe { get_sender(&window).send((get_time(), $event)); }
-         }
+        pub extern "C" fn $name(window: *ffi::GLFWwindow) {
+            unsafe { get_sender(&window).send((ffi::glfwGetTime() as f64, $event)); }
+        }
      );
     (fn $name:ident ($($ext_arg:ident: $ext_arg_ty:ty),*) => $event:ident($($arg_conv:expr),*)) => (
-         pub extern "C" fn $name(window: *ffi::GLFWwindow $(, $ext_arg: $ext_arg_ty)*) {
-            unsafe { get_sender(&window).send((get_time(), $event($($arg_conv),*))); }
-         }
+        pub extern "C" fn $name(window: *ffi::GLFWwindow $(, $ext_arg: $ext_arg_ty)*) {
+            unsafe { get_sender(&window).send((ffi::glfwGetTime() as f64, $event($($arg_conv),*))); }
+        }
      );
 )
 
diff --git a/src/lib/ffi.rs b/src/lib/ffi.rs
index 5bd9c6ab..973d677f 100644
--- a/src/lib/ffi.rs
+++ b/src/lib/ffi.rs
@@ -13,7 +13,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Low-level glfw function bindings and constants.
+//! Low-level function bindings and constants pertaining to the underlying
+//! GLFW library.
 
 use std::libc::*;
 
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index 13435719..3da1b4b3 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -22,6 +22,52 @@
 #![feature(globs)]
 #![feature(macro_rules)]
 
+//! An ideomatic wrapper for the GLFW library.
+//!
+//! # Example
+//!
+//! ~~~rust
+//! extern crate native;
+//! extern crate glfw;
+//! 
+//! #[start]
+//! fn start(argc: int, argv: **u8) -> int {
+//!     // Run GLFW on the main thread
+//!     native::start(argc, argv, main)
+//! }
+//! 
+//! fn main() {
+//!     let glfw = glfw::init().unwrap();
+//!     let errors = glfw.get_errors().unwrap();
+//! 
+//!     // Create a windowed mode window and its OpenGL context
+//!     let window = glfw.create_window(300, 300, "Hello this is window", glfw::Windowed)
+//!         .expect("Failed to create GLFW window.");
+//! 
+//!     // Make the window's context current
+//!     window.make_context_current();
+//! 
+//!     // 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();
+//!         glfw::fail_on_error(&errors);
+//!         for (_, event) in glfw::flush_messages(&events) {
+//!             println!("{}", event);
+//!             match event {
+//!                 glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _) => {
+//!                     window.set_should_close(true)
+//!                 },
+//!                 _ => {},
+//!             }
+//!         }
+//!     }
+//! }
+//! ~~~
+
 // TODO: Document differences between GLFW and glfw-rs
 
 extern crate semver;
@@ -30,20 +76,29 @@ extern crate sync;
 use std::cast;
 use std::comm::{channel, Receiver, Sender, Data};
 use std::fmt;
+use std::kinds::marker;
 use std::libc::*;
 use std::ptr;
 use std::str;
 use std::slice;
 use semver::Version;
 
+/// Alias to `MouseButton1`, supplied for improved clarity.
 pub use MouseButtonLeft     = self::MouseButton1;
+/// Alias to `MouseButton2`, supplied for improved clarity.
 pub use MouseButtonRight    = self::MouseButton2;
+/// Alias to `MouseButton3`, supplied for improved clarity.
 pub use MouseButtonMiddle   = self::MouseButton3;
 
 pub mod ffi;
+
 mod callbacks;
+
+/// Platform-specific linking. This module is automatically generated when
+/// glfw-rs is compiled.
 mod link;
 
+/// Input actions.
 #[repr(C)]
 #[deriving(Clone, Eq, Hash, Show)]
 pub enum Action {
@@ -52,6 +107,7 @@ pub enum Action {
     Repeat                       = ffi::REPEAT,
 }
 
+/// Input keys.
 #[repr(C)]
 #[deriving(Clone, Eq, Hash, Show)]
 pub enum Key {
@@ -178,11 +234,16 @@ pub enum Key {
     KeyMenu                     = ffi::KEY_MENU,
 }
 
+/// Mouse buttons. The `MouseButtonLeft`, `MouseButtonRight`, and
+/// `MouseButtonMiddle` aliases are supplied for convenience.
 #[repr(C)]
 #[deriving(Clone, Eq, Hash)]
 pub enum MouseButton {
+    /// The left mouse button. A `MouseButtonLeft` alias is provided to improve clarity.
     MouseButton1                = ffi::MOUSE_BUTTON_1,
+    /// The right mouse button. A `MouseButtonRight` alias is provided to improve clarity.
     MouseButton2                = ffi::MOUSE_BUTTON_2,
+    /// The middle mouse button. A `MouseButtonMiddle` alias is provided to improve clarity.
     MouseButton3                = ffi::MOUSE_BUTTON_3,
     MouseButton4                = ffi::MOUSE_BUTTON_4,
     MouseButton5                = ffi::MOUSE_BUTTON_5,
@@ -206,6 +267,7 @@ impl fmt::Show for MouseButton {
     }
 }
 
+/// Tokens corresponding to various error types.
 #[repr(C)]
 #[deriving(Clone, Eq, Hash, Show)]
 pub enum Error {
@@ -220,6 +282,7 @@ pub enum Error {
     FormatUnavailable           = ffi::FORMAT_UNAVAILABLE,
 }
 
+/// Cursor modes.
 #[repr(C)]
 #[deriving(Clone, Eq, Hash, Show)]
 pub enum CursorMode {
@@ -245,7 +308,20 @@ pub struct GammaRamp {
     blue:   ~[c_ushort],
 }
 
+/// An OpenGL process address.
 pub type GLProc = ffi::GLFWglproc;
+ 
+/// 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 initialised 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.
+#[deriving(Clone)]
+pub struct Glfw {
+    priv no_send: marker::NoSend,
+    priv no_share: marker::NoShare,
+}
 
 /// An error that might be returned when `glfw::init` is called.
 #[deriving(Eq, Show)]
@@ -256,12 +332,34 @@ pub enum InitError {
     InternalInitError,
 }
 
-/// Initialise glfw. This must be called on the main platform thread. Subsequent
-/// calls will return `Err(AlreadyInitialized)`. If an error occured within the
-/// glfw library, `Err(InternalInitError)` will be returned.
+/// Initializes the GLFW library. This must be called on the main platform thread.
 ///
 /// Wrapper for `glfwInit`.
-pub fn init() -> Result<(), InitError> {
+///
+/// # Example
+///
+/// ~~~rust
+/// extern crate native;
+/// extern crate glfw;
+/// 
+/// #[start]
+/// fn start(argc: int, argv: **u8) -> int {
+///     // Run GLFW on the main thread
+///     native::start(argc, argv, main)
+/// }
+/// 
+/// fn main() {
+///     let glfw = glfw::init().unwrap();
+/// }
+/// ~~~
+///
+/// # Returns
+///
+/// - If initialization was successful, a `Glfw` token will be returned.
+/// - Subsequent calls to `init` will return `Err(AlreadyInitialized)`.
+/// - If an initialization error occured within the glfw library,
+///   `Err(InternalInitError)` will be returned. 
+pub fn init() -> Result<Glfw, InitError> {
     use sync::one::{Once, ONCE_INIT};
     static mut INIT: Once = ONCE_INIT;
     let mut result = Err(AlreadyInitialized);
@@ -277,18 +375,223 @@ pub fn init() -> Result<(), InitError> {
             }
         })
     }
-    result
+    result.map(|_| Glfw {
+        no_send: marker::NoSend,
+        no_share: marker::NoShare,
+    })
 }
 
-/// Initialises GLFW, failing if the initialisation was unsuccessful.
-///
-/// # Parameters
-///
-/// - `f`: to be called after the GLFW is initialised.
-pub fn start(f: proc()) {
-    match init() {
-        Ok(_)   => f(),
-        Err(e)  => fail!("Failed to initialize GLFW: {}", e),
+impl Glfw {
+    /// Returns a `Reciever` for intercepting errors from GLFW. This will only
+    /// return the `Reciever` once – after that `None` will be returned.
+    ///
+    /// # Example
+    ///
+    /// ~~~rust
+    /// let glfw = glfw::init().unwrap();
+    /// let errors = glfw.get_errors().unwrap();
+    ///
+    /// // ...
+    ///
+    /// match errors.try_recv() {
+    ///     std::comm::Data((_, _, description)) => {
+    ///         fail!("GLFW Error: {}", description),
+    ///     },
+    ///     _ => {},
+    /// }
+    /// ~~~
+    pub fn get_errors(&self) -> Option<Receiver<(f64, Error, ~str)>> {
+        callbacks::init_error_receiver()
+    }
+
+    /// Returns the primary monitor. This is usually the monitor where elements
+    /// like the Windows task bar or the OS X menu bar is located.
+    ///
+    /// Wrapper for `glfwGetPrimaryMonitor`.
+    pub fn get_primary_monitor(&self) -> Result<Monitor,()> {
+        unsafe {
+            ffi::glfwGetPrimaryMonitor()
+             .to_option()
+             .map_or(Err(()),
+                |ptr| Ok(Monitor { ptr: ptr }))
+        }
+    }
+
+    /// Returns a vector of the currently connected monitors.
+    ///
+    /// Wrapper for `glfwGetMonitors`.
+    pub fn get_connected_monitors(&self) -> Vec<Monitor> {
+        unsafe {
+            let mut count = 0;
+            let ptr = ffi::glfwGetMonitors(&mut count);
+            slice::from_buf(ptr, count as uint).iter().map(|&m| Monitor { ptr: m }).collect()
+        }
+    }
+
+    /// This is used to set the window hints for the next call to `Window::create`.
+    /// The hints can be reset to their default values using the
+    /// `default_window_hints` function.
+    ///
+    /// Wrapper for `glfwWindowHint`
+    pub fn window_hint(&self, hint: WindowHint) {
+        match hint {
+            RedBits(bits)                   => unsafe { ffi::glfwWindowHint(ffi::RED_BITS,              bits as c_int) },
+            GreenBits(bits)                 => unsafe { ffi::glfwWindowHint(ffi::GREEN_BITS,            bits as c_int) },
+            BlueBits(bits)                  => unsafe { ffi::glfwWindowHint(ffi::BLUE_BITS,             bits as c_int) },
+            AlphaBits(bits)                 => unsafe { ffi::glfwWindowHint(ffi::ALPHA_BITS,            bits as c_int) },
+            DepthBits(bits)                 => unsafe { ffi::glfwWindowHint(ffi::DEPTH_BITS,            bits as c_int) },
+            StencilBits(bits)               => unsafe { ffi::glfwWindowHint(ffi::STENCIL_BITS,          bits as c_int) },
+            AccumRedBits(bits)              => unsafe { ffi::glfwWindowHint(ffi::ACCUM_RED_BITS,        bits as c_int) },
+            AccumGreenBits(bits)            => unsafe { ffi::glfwWindowHint(ffi::ACCUM_GREEN_BITS,      bits as c_int) },
+            AccumBlueBits(bits)             => unsafe { ffi::glfwWindowHint(ffi::ACCUM_BLUE_BITS,       bits as c_int) },
+            AccumAlphaBits(bits)            => unsafe { ffi::glfwWindowHint(ffi::ACCUM_ALPHA_BITS,      bits as c_int) },
+            AuxBuffers(num_buffers)         => unsafe { ffi::glfwWindowHint(ffi::AUX_BUFFERS,           num_buffers as c_int) },
+            Stereo(is_stereo)               => unsafe { ffi::glfwWindowHint(ffi::STEREO,                is_stereo as c_int) },
+            Samples(num_samples)            => unsafe { ffi::glfwWindowHint(ffi::SAMPLES,               num_samples as c_int) },
+            SRgbCapable(is_capable)         => unsafe { ffi::glfwWindowHint(ffi::SRGB_CAPABLE,          is_capable as c_int) },
+            RefreshRate(rate)               => unsafe { ffi::glfwWindowHint(ffi::REFRESH_RATE,          rate as c_int) },
+            ClientApi(api)                  => unsafe { ffi::glfwWindowHint(ffi::CLIENT_API,            api as c_int) },
+            ContextVersionMajor(minor)      => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int) },
+            ContextVersionMinor(major)      => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int) },
+            ContextVersion(major, minor)    => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int);
+                                                        ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int) },
+            ContextRobustness(robustness)   => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_ROBUSTNESS,    robustness as c_int) },
+            OpenglForwardCompat(is_compat)  => unsafe { ffi::glfwWindowHint(ffi::OPENGL_FORWARD_COMPAT, is_compat as c_int) },
+            OpenglDebugContext(is_debug)    => unsafe { ffi::glfwWindowHint(ffi::OPENGL_DEBUG_CONTEXT,  is_debug as c_int) },
+            OpenglProfile(profile)          => unsafe { ffi::glfwWindowHint(ffi::OPENGL_PROFILE,        profile as c_int) },
+            Resizable(is_resizable)         => unsafe { ffi::glfwWindowHint(ffi::RESIZABLE,             is_resizable as c_int) },
+            Visible(is_visible)             => unsafe { ffi::glfwWindowHint(ffi::VISIBLE,               is_visible as c_int) },
+            Decorated(is_decorated)         => unsafe { ffi::glfwWindowHint(ffi::DECORATED,             is_decorated 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(&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, EventReceiver)> {
+        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, EventReceiver)> {
+        let ptr = unsafe {
+            title.with_c_str(|title| {
+                ffi::glfwCreateWindow(
+                    width as c_int,
+                    height as c_int,
+                    title,
+                    mode.to_ptr(),
+                    match share { Some(w) => w.ptr, None => ptr::null() }
+                )
+            })
+        };
+        if ptr.is_null() {
+            None
+        } else {
+            let (sender, receiver) = channel();
+            unsafe { ffi::glfwSetWindowUserPointer(ptr, cast::transmute(~sender)); }
+            Some((
+                Window {
+                    ptr: ptr,
+                    glfw: self.clone(),
+                    is_shared: share.is_none(),
+                },
+                EventReceiver(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(&self, context: Option<&Window>) {
+        match context {
+            Some(window) => unsafe { ffi::glfwMakeContextCurrent(window.ptr) },
+            None         => unsafe { ffi::glfwMakeContextCurrent(ptr::null()) },
+        }
+    }
+
+    /// Wrapper for `glfwGetX11Display`
+    #[cfg(target_os="linux")]
+    pub fn get_x11_display(&self) -> *c_void {
+        unsafe { ffi::glfwGetX11Display() }
+    }
+
+    /// Immediately process the received events.
+    ///
+    /// Wrapper for `glfwPollEvents`.
+    pub fn poll_events(&self) {
+        unsafe { ffi::glfwPollEvents(); }
+    }
+
+    /// Sleep until at least one event has been recieved, and then perform the
+    /// equivalent of `Glfw::poll_events`.
+    ///
+    /// Wrapper for `glfwWaitEvents`.
+    pub fn wait_events(&self) {
+        unsafe { ffi::glfwWaitEvents(); }
+    }
+
+    /// 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(&self, time: f64) {
+        unsafe { ffi::glfwSetTime(time as c_double); }
+    }
+
+    /// 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(&self, interval: u32) {
+        unsafe { ffi::glfwSwapInterval(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 {
+            extension.with_c_str(|extension| {
+                ffi::glfwExtensionSupported(extension) == ffi::TRUE
+            })
+        }
+    }
+
+    /// Returns the address of the specified client API or extension function
+    /// if it is supported by the current context.
+    ///
+    /// Wrapper for `glfwGetProcAddress`.
+    pub fn get_proc_address(&self, procname: &str) -> Option<GLProc> {
+        unsafe {
+            procname.with_c_str(|procname| {
+                ffi::glfwGetProcAddress(procname)
+            })
+        }
+    }
+
+    /// Constructs a `Joystick` handle corresponding to the supplied `JoystickId`.
+    pub fn get_joystick(&self, id: JoystickId) -> Joystick {
+        Joystick { id: id, glfw: self.clone() }
     }
 }
 
@@ -314,27 +617,6 @@ pub fn get_version_string() -> ~str {
     unsafe { str::raw::from_c_str(ffi::glfwGetVersionString()) }
 }
 
-/// Returns a `Reciever` for intercepting errors from GLFW. This will only
-/// return `Some` once, otherwise `None` will be returned.
-///
-/// # Example
-///
-/// ~~~rust
-/// let errors = glfw::get_errors().unwrap();
-///
-/// // ...
-///
-/// match errors.try_recv() {
-///     std::comm::Data((_, _, description)) => {
-///         fail!("GLFW Error: {}", description),
-///     },
-///     _ => {},
-/// }
-/// ~~~
-pub fn get_errors() -> Option<Receiver<(f64, Error, ~str)>> {
-    callbacks::init_error_receiver()
-}
-
 /// Fails if an error has been received.
 pub fn fail_on_error(errors: &Receiver<(f64, Error, ~str)>) {
     match errors.try_recv() {
@@ -345,7 +627,10 @@ pub fn fail_on_error(errors: &Receiver<(f64, Error, ~str)>) {
     }
 }
 
-pub trait MonitorCallback { fn call(&self, monitor: &Monitor, event: MonitorEvent); }
+/// A monitor callback trait.
+pub trait MonitorCallback {
+    fn call(&self, monitor: &Monitor, event: MonitorEvent);
+}
 
 /// A struct that wraps a `*GLFWmonitor` handle.
 #[deriving(Eq)]
@@ -354,25 +639,6 @@ pub struct Monitor {
 }
 
 impl Monitor {
-    /// Wrapper for `glfwGetPrimaryMonitor`.
-    pub fn get_primary() -> Result<Monitor,()> {
-        unsafe {
-            ffi::glfwGetPrimaryMonitor()
-             .to_option()
-             .map_or(Err(()),
-                |ptr| Ok(Monitor { ptr: ptr }))
-        }
-    }
-
-    /// Wrapper for `glfwGetMonitors`.
-    pub fn get_connected() -> Vec<Monitor> {
-        unsafe {
-            let mut count = 0;
-            let ptr = ffi::glfwGetMonitors(&mut count);
-            slice::from_buf(ptr, count as uint).iter().map(|&m| Monitor { ptr: m }).collect()
-        }
-    }
-
     /// Wrapper for `glfwGetMonitorPos`.
     pub fn get_pos(&self) -> (i32, i32) {
         unsafe {
@@ -454,6 +720,7 @@ impl Monitor {
     }
 }
 
+/// Monitor events.
 #[repr(C)]
 pub enum MonitorEvent {
     Connected                   = ffi::CONNECTED,
@@ -588,45 +855,7 @@ pub enum WindowHint {
     Decorated(bool),
 }
 
-/// This is used to set the window hints for the next call to `Window::create`.
-/// The hints can be reset to their default values using the
-/// `default_window_hints` function.
-///
-/// This function must only be called from the main platform thread.
-///
-/// Wrapper for `glfwWindowHint`
-pub fn window_hint(hint: WindowHint) {
-    match hint {
-        RedBits(bits)                   => unsafe { ffi::glfwWindowHint(ffi::RED_BITS,              bits as c_int) },
-        GreenBits(bits)                 => unsafe { ffi::glfwWindowHint(ffi::GREEN_BITS,            bits as c_int) },
-        BlueBits(bits)                  => unsafe { ffi::glfwWindowHint(ffi::BLUE_BITS,             bits as c_int) },
-        AlphaBits(bits)                 => unsafe { ffi::glfwWindowHint(ffi::ALPHA_BITS,            bits as c_int) },
-        DepthBits(bits)                 => unsafe { ffi::glfwWindowHint(ffi::DEPTH_BITS,            bits as c_int) },
-        StencilBits(bits)               => unsafe { ffi::glfwWindowHint(ffi::STENCIL_BITS,          bits as c_int) },
-        AccumRedBits(bits)              => unsafe { ffi::glfwWindowHint(ffi::ACCUM_RED_BITS,        bits as c_int) },
-        AccumGreenBits(bits)            => unsafe { ffi::glfwWindowHint(ffi::ACCUM_GREEN_BITS,      bits as c_int) },
-        AccumBlueBits(bits)             => unsafe { ffi::glfwWindowHint(ffi::ACCUM_BLUE_BITS,       bits as c_int) },
-        AccumAlphaBits(bits)            => unsafe { ffi::glfwWindowHint(ffi::ACCUM_ALPHA_BITS,      bits as c_int) },
-        AuxBuffers(num_buffers)         => unsafe { ffi::glfwWindowHint(ffi::AUX_BUFFERS,           num_buffers as c_int) },
-        Stereo(is_stereo)               => unsafe { ffi::glfwWindowHint(ffi::STEREO,                is_stereo as c_int) },
-        Samples(num_samples)            => unsafe { ffi::glfwWindowHint(ffi::SAMPLES,               num_samples as c_int) },
-        SRgbCapable(is_capable)         => unsafe { ffi::glfwWindowHint(ffi::SRGB_CAPABLE,          is_capable as c_int) },
-        RefreshRate(rate)               => unsafe { ffi::glfwWindowHint(ffi::REFRESH_RATE,          rate as c_int) },
-        ClientApi(api)                  => unsafe { ffi::glfwWindowHint(ffi::CLIENT_API,            api as c_int) },
-        ContextVersionMajor(minor)      => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int) },
-        ContextVersionMinor(major)      => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int) },
-        ContextVersion(major, minor)    => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MAJOR, major as c_int);
-                                                    ffi::glfwWindowHint(ffi::CONTEXT_VERSION_MINOR, minor as c_int) },
-        ContextRobustness(robustness)   => unsafe { ffi::glfwWindowHint(ffi::CONTEXT_ROBUSTNESS,    robustness as c_int) },
-        OpenglForwardCompat(is_compat)  => unsafe { ffi::glfwWindowHint(ffi::OPENGL_FORWARD_COMPAT, is_compat as c_int) },
-        OpenglDebugContext(is_debug)    => unsafe { ffi::glfwWindowHint(ffi::OPENGL_DEBUG_CONTEXT,  is_debug as c_int) },
-        OpenglProfile(profile)          => unsafe { ffi::glfwWindowHint(ffi::OPENGL_PROFILE,        profile as c_int) },
-        Resizable(is_resizable)         => unsafe { ffi::glfwWindowHint(ffi::RESIZABLE,             is_resizable as c_int) },
-        Visible(is_visible)             => unsafe { ffi::glfwWindowHint(ffi::VISIBLE,               is_visible as c_int) },
-        Decorated(is_decorated)         => unsafe { ffi::glfwWindowHint(ffi::DECORATED,             is_decorated as c_int) },
-    }
-}
-
+/// Client API tokens.
 #[repr(C)]
 #[deriving(Clone, Eq, Show)]
 pub enum ClientApi {
@@ -634,6 +863,7 @@ pub enum ClientApi {
     OpenGlEsApi                 = ffi::OPENGL_ES_API,
 }
 
+/// Context robustness tokens.
 #[repr(C)]
 #[deriving(Clone, Eq, Show)]
 pub enum ContextRobustness {
@@ -642,6 +872,7 @@ pub enum ContextRobustness {
     LoseContextOnReset          = ffi::LOSE_CONTEXT_ON_RESET,
 }
 
+/// OpenGL profile tokens.
 #[repr(C)]
 #[deriving(Clone, Eq, Show)]
 pub enum OpenGlProfile {
@@ -650,16 +881,6 @@ pub enum OpenGlProfile {
     OpenGlCompatProfile         = ffi::OPENGL_COMPAT_PROFILE,
 }
 
-/// Resets the window hints previously set by the `window_hint` function to
-/// their default values.
-///
-/// This function must only be called from the main platform thread.
-///
-/// Wrapper for `glfwDefaultWindowHints`.
-pub fn default_window_hints() {
-    unsafe { ffi::glfwDefaultWindowHints(); }
-}
-
 /// Describes the mode of a window
 pub enum WindowMode {
     /// Full screen mode. Contains the monitor on which the window is displayed.
@@ -736,6 +957,7 @@ impl fmt::Show for Modifiers {
 
 pub type Scancode = c_int;
 
+/// Window event messages.
 pub enum WindowEvent {
     PosEvent(i32, i32),
     SizeEvent(i32, i32),
@@ -752,6 +974,7 @@ pub enum WindowEvent {
     CharEvent(char),
 }
 
+/// An iterator over the flushed events received from an `EventReceiver`.
 pub struct FlushedWindowEvents<'a> {
     priv event_receiver: &'a Receiver<(f64, WindowEvent)>,
 }
@@ -765,6 +988,8 @@ impl<'a> Iterator<(f64, WindowEvent)> for FlushedWindowEvents<'a> {
     }
 }
 
+/// A `Receiver` of window events that can be flushed using the
+/// `EventReceiver::flush_events` function.
 pub struct EventReceiver(Receiver<(f64, WindowEvent)>);
 
 impl Deref<Receiver<(f64, WindowEvent)>> for EventReceiver {
@@ -786,6 +1011,7 @@ impl EventReceiver {
 /// A struct that wraps a `*GLFWwindow` handle.
 pub struct Window {
     ptr: *ffi::GLFWwindow,
+    glfw: Glfw,
     is_shared: bool,
 }
 
@@ -800,44 +1026,14 @@ macro_rules! set_window_callback(
 )
 
 impl Window {
-    /// Wrapper for `glfwCreateWindow`.
-    pub fn create(width: u32, height: u32, title: &str, mode: WindowMode) -> Option<(Window, EventReceiver)> {
-        Window::create_intern(width, height, title, mode, None)
-    }
-
     /// Wrapper for `glfwCreateWindow`.
     pub fn create_shared(&self, width: u32, height: u32, title: &str, mode: WindowMode) -> Option<(Window, EventReceiver)> {
-        Window::create_intern(width, height, title, mode, Some(self))
+        self.glfw.create_window_intern(width, height, title, mode, Some(self))
     }
 
-    /// Internal wrapper for `glfwCreateWindow`.
-    fn create_intern(width: u32, height: u32, title: &str, mode: WindowMode, share: Option<&Window>) -> Option<(Window, EventReceiver)> {
-        let ptr = unsafe {
-            title.with_c_str(|title| {
-                ffi::glfwCreateWindow(
-                    width as c_int,
-                    height as c_int,
-                    title,
-                    mode.to_ptr(),
-                    match share { Some(w) => w.ptr, None => ptr::null() }
-                )
-            })
-        };
-        if ptr.is_null() {
-            None
-        } else {
-            let (sender, receiver) = channel();
-            unsafe { ffi::glfwSetWindowUserPointer(ptr, cast::transmute(~sender)); }
-            Some(
-                (Window {ptr: ptr, is_shared: share.is_none()},
-                EventReceiver(receiver)
-            ))
-        }
-    }
-
-    pub fn close(self) {
-        // Calling this method forces the destructor to be called, closing the window
-    }
+    /// Calling this method forces the destructor to be called, closing the
+    /// window.
+    pub fn close(self) {}
 
     /// Wrapper for `glfwWindowShouldClose`.
     pub fn should_close(&self) -> bool {
@@ -849,6 +1045,8 @@ impl Window {
         unsafe { ffi::glfwSetWindowShouldClose(self.ptr, value as c_int) }
     }
 
+    /// Sets the title of the window.
+    ///
     /// Wrapper for `glfwSetWindowTitle`.
     pub fn set_title(&self, title: &str) {
         unsafe {
@@ -918,11 +1116,9 @@ impl Window {
         unsafe { ffi::glfwHideWindow(self.ptr); }
     }
 
-    /// Wrapper for `glfwGetWindowMonitor`.
+    /// Returns whether the window is fullscreen or windowed.
     ///
-    /// # Returns
-    ///
-    /// The window mode; either glfw::FullScreen or glfw::Windowed
+    /// Wrapper for `glfwGetWindowMonitor`.
     pub fn get_window_mode(&self) -> WindowMode {
         WindowMode::from_ptr(
             unsafe { ffi::glfwGetWindowMonitor(self.ptr) }
@@ -1149,14 +1345,18 @@ impl Window {
 
     /// Wrapper for `glfwMakeContextCurrent`.
     pub fn make_context_current(&self) {
-        make_context_current(Some(self));
+        self.glfw.make_context_current(Some(self));
     }
 
-    /// Wrapper for `glfwGetCurrentContext`
+    /// Returns `true` if the window is the current context.
     pub fn is_current_context(&self) -> bool {
         self.ptr == unsafe { ffi::glfwGetCurrentContext() }
     }
 
+    /// 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`.
     pub fn swap_buffers(&self) {
         unsafe { ffi::glfwSwapBuffers(self.ptr); }
@@ -1199,23 +1399,9 @@ impl Window {
     }
 }
 
-/// Wrapper for `glfwMakeContextCurrent`.
-pub fn make_context_current(context: Option<&Window>) {
-    match context {
-        Some(window) => unsafe { ffi::glfwMakeContextCurrent(window.ptr) },
-        None         => unsafe { ffi::glfwMakeContextCurrent(ptr::null()) },
-    }
-}
-
-/// Wrapper for `glfwGetX11Display`
-#[cfg(target_os="linux")]
-pub fn get_x11_display() -> *c_void {
-    unsafe { ffi::glfwGetX11Display() }
-}
-
 #[unsafe_destructor]
 impl Drop for Window {
-    /// Closes the window and removes all associated callbacks.
+    /// Closes the window and performs the necessary cleanups.
     ///
     /// Wrapper for `glfwDestroyWindow`.
     fn drop(&mut self) {
@@ -1230,19 +1416,10 @@ impl Drop for Window {
     }
 }
 
-/// Wrapper for `glfwPollEvents`.
-pub fn poll_events() {
-    unsafe { ffi::glfwPollEvents(); }
-}
-
-/// Wrapper for `glfwWaitEvents`.
-pub fn wait_events() {
-    unsafe { ffi::glfwWaitEvents(); }
-}
-
+/// Joystick identifier tokens.
 #[repr(C)]
 #[deriving(Clone, Eq, Hash, Show)]
-pub enum Joystick {
+pub enum JoystickId {
     Joystick1       = ffi::JOYSTICK_1,
     Joystick2       = ffi::JOYSTICK_2,
     Joystick3       = ffi::JOYSTICK_3,
@@ -1261,17 +1438,23 @@ pub enum Joystick {
     Joystick16      = ffi::JOYSTICK_16,
 }
 
+/// A joystick handle.
+pub struct Joystick {
+    id: JoystickId,
+    glfw: Glfw,
+}
+
 impl Joystick {
     /// Wrapper for `glfwJoystickPresent`.
     pub fn is_present(&self) -> bool {
-        unsafe { ffi::glfwJoystickPresent(*self as c_int) == ffi::TRUE }
+        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 as c_int, &mut count);
+            let ptr = ffi::glfwGetJoystickAxes(self.id as c_int, &mut count);
             slice::from_buf(ptr, count as uint).iter().map(|&a| a as f32).collect()
         }
     }
@@ -1280,46 +1463,13 @@ impl Joystick {
     pub fn get_buttons(&self) -> Vec<c_int> {
         unsafe {
             let mut count = 0;
-            let ptr = ffi::glfwGetJoystickButtons(*self as c_int, &mut count);
+            let ptr = ffi::glfwGetJoystickButtons(self.id as c_int, &mut count);
             slice::from_buf(ptr, count as uint).iter().map(|&b| b as c_int).collect()
         }
     }
 
     /// Wrapper for `glfwGetJoystickName`.
     pub fn get_name(&self) -> ~str {
-        unsafe { str::raw::from_c_str(ffi::glfwGetJoystickName(*self as c_int)) }
-    }
-}
-
-/// Wrapper for `glfwGetTime`.
-pub fn get_time() -> f64 {
-    unsafe { ffi::glfwGetTime() as f64 }
-}
-
-/// Wrapper for `glfwSetTime`.
-pub fn set_time(time: f64) {
-    unsafe { ffi::glfwSetTime(time as c_double); }
-}
-
-/// Wrapper for `glfwSwapInterval`.
-pub fn set_swap_interval(interval: u32) {
-    unsafe { ffi::glfwSwapInterval(interval as c_int); }
-}
-
-/// Wrapper for `glfwExtensionSupported`.
-pub fn extension_supported(extension: &str) -> bool {
-    unsafe {
-        extension.with_c_str(|extension| {
-            ffi::glfwExtensionSupported(extension) == ffi::TRUE
-        })
-    }
-}
-
-/// Wrapper for `glfwGetProcAddress`.
-pub fn get_proc_address(procname: &str) -> Option<GLProc> {
-    unsafe {
-        procname.with_c_str(|procname| {
-            ffi::glfwGetProcAddress(procname)
-        })
+        unsafe { str::raw::from_c_str(ffi::glfwGetJoystickName(self.id as c_int)) }
     }
 }


Test patch: