diff --git a/GPipe-GLFW.cabal b/GPipe-GLFW.cabal
--- a/GPipe-GLFW.cabal
+++ b/GPipe-GLFW.cabal
@@ -1,5 +1,5 @@
 name:                GPipe-GLFW
-version:             1.2.3
+version:             1.3.0
 cabal-version:       >=1.10
 build-type:          Simple
 author:              Patrick Redmond
@@ -56,4 +56,4 @@
   type:     git
   location: https://github.com/plredmond/GPipe-GLFW.git
   subdir:   GPipe-GLFW
-  tag:      v1.2.3
+  tag:      v1.3.0
diff --git a/src/Graphics/GPipe/Context/GLFW/Input.hs b/src/Graphics/GPipe/Context/GLFW/Input.hs
--- a/src/Graphics/GPipe/Context/GLFW/Input.hs
+++ b/src/Graphics/GPipe/Context/GLFW/Input.hs
@@ -1,42 +1,280 @@
--- | Window actions, mostly corresponding to user input. Analogous to those from
--- 'Graphics.UI.GLFW', but in the GPipe 'ContextT' monad.
-module Graphics.GPipe.Context.GLFW.Input
-(
-  -- * Exposed actions
-  getCursorPos,
-  getMouseButton,
-  getKey,
-  registerScrollCallback,
-  windowShouldClose,
-  -- * Re-exported from GLFW
-  MouseButtonState(..), MouseButton(..), KeyState(..), Key(..), ScrollCallback
-) where
+-- | This module exposes much of the functionality that GLFW the __Input guide__ documents:
+-- <http://www.glfw.org/docs/latest/input_guide.html>.
+-- Actions are in the GPipe 'ContextT' monad when a window handle is required,
+-- otherwise they are bare IO actions.
+-- It is recommended to read about GLFW input handling as it pertains to your domain,
+-- especially the __Event processing__ section:
+-- <http://www.glfw.org/docs/latest/input_guide.html#events>.
 
+module Graphics.GPipe.Context.GLFW.Input (
+
+ -- * Event processing
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#events
+ GLFW.pollEvents,
+ -- | Process received events and return; for applications which continually render.
+ GLFW.waitEvents,
+ -- | Sleep until an event is received; for applications which update in response to user input.
+ GLFW.postEmptyEvent,
+ -- | Force wake from 'waitEvents' with a dummy event.
+
+ -- * Keyboard input
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#input_keyboard
+
+ -- ** Key input
+ setKeyCallback,
+ getKey,
+ setStickyKeysInputMode,
+ getStickyKeysInputMode,
+
+ -- ** Text input
+ setCharCallback,
+
+ -- * Mouse input
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#input_mouse
+
+ -- ** Cursor position
+ setCursorPosCallback,
+ getCursorPos,
+
+ -- ** Cursor modes
+ setCursorInputMode,
+ getCursorInputMode,
+
+ -- ** Cursor objects
+
+ -- *** Custom cursor creation
+ GLFW.createCursor,
+
+ -- *** Standard cursor creation
+ GLFW.createStandardCursor,
+
+ -- *** Cursor destruction
+ GLFW.destroyCursor,
+
+ -- *** Cursor setting
+ setCursor,
+
+ -- ** Cursor enter/leave events
+ setCursorEnterCallback,
+
+ -- ** Mouse button input
+ setMouseButtonCallback,
+ getMouseButton,
+ setStickyMouseButtonsInputMode,
+ getStickyMouseButtonsInputMode,
+
+ -- ** Scroll input
+ setScrollCallback,
+
+ -- * Joystick input
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#joystick
+ GLFW.joystickPresent,
+ -- | Is the specified 'Joystick' currently connected?
+
+ -- ** Joystick axis states
+ GLFW.getJoystickAxes,
+ -- | Poll for the positions of each axis on the 'Joystick'. Positions are on the range `-1.0..1.0`.
+
+ -- ** Joystick button states
+ GLFW.getJoystickButtons,
+ -- | Poll for the 'JoystickButtonState' of each button on the 'Joystick'.
+
+ -- ** Joystick name
+ GLFW.getJoystickName,
+ -- | Retrieve a non-unique string identifier for the 'Joystick'.
+ -- This might be the make & model name of the device.
+
+ -- * Time input
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#time
+ GLFW.getTime,
+ -- | Poll for the number of seconds since GLFW was initialized by GPipe.
+ GLFW.setTime,
+ -- | Manually set the timer to a specified value.
+
+ -- * Clipboard input and output
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#clipboard
+ getClipboardString,
+ setClipboardString,
+
+ -- * Path drop input
+ -- | Learn more: http://www.glfw.org/docs/latest/input_guide.html#path_drop
+ setDropCallback,
+
+ -- * Reexported datatypes
+
+ -- ** Keyboard
+ Key(..),
+ KeyState(..),
+ ModifierKeys(..),
+ StickyKeysInputMode(..),
+
+ -- ** Mouse
+ CursorInputMode(..),
+ StandardCursorShape(..),
+ CursorState(..),
+ StickyMouseButtonsInputMode(..),
+ MouseButton(..),
+ MouseButtonState(..),
+
+ -- ** Joystick
+ Joystick(..),
+ JoystickButtonState(..),
+
+ -- * Not supported
+ -- | Some GLFW functionality isn't currently exposed by "GLFW-b".
+ --
+ --     * `glfwWaitEventsTimeout`
+ --     * `glfwSetCharModsCallback`
+ --     * `glfwGetKeyName`
+ --     * `glfwSetJoystickCallback`
+ --     * `glfwGetTimerValue`
+ --     * `glfwGetTimerFrequency`
+
+ -- * Deprecated
+ GLFW.ScrollCallback,
+ registerScrollCallback,
+ windowShouldClose,
+ ) where
+
 import Graphics.GPipe.Context.GLFW.Unsafe (GLFWWindow(..))
 
 import Control.Monad.IO.Class (MonadIO)
 import Graphics.GPipe.Context (ContextT, withContextWindow)
-import qualified Graphics.UI.GLFW as GLFW (getCursorPos, getMouseButton, getKey, windowShouldClose, setScrollCallback)
-import Graphics.UI.GLFW (MouseButtonState(..), MouseButton(..), KeyState(..), Key(..), ScrollCallback)
+import qualified Graphics.UI.GLFW as GLFW
+import Graphics.UI.GLFW (
+ Key(..),
+ KeyState(..),
+ ModifierKeys(..),
+ StickyKeysInputMode(..),
+ CursorInputMode(..),
+ Cursor(..),
+ StandardCursorShape(..),
+ CursorState(..),
+ StickyMouseButtonsInputMode(..),
+ MouseButton(..),
+ MouseButtonState(..),
+ Joystick(..),
+ JoystickButtonState(..),
+ )
 
--- | Gets the current cursor position, in pixels relative to the top-left corner
--- of the window.
+{- Util -}
+
+-- | Convenience function to access the unwrapped GLFW window.
+withWindow :: MonadIO m => (GLFW.Window -> IO a) -> ContextT GLFWWindow os f m a
+withWindow f = withContextWindow $ f . getGLFWWindow
+
+-- | Convenience function to quickly wrap two argument functions taking window and something else.
+-- Wrapped functions don't take window.
+wrapWindowFun :: MonadIO m => (GLFW.Window -> a -> IO b) -> a -> ContextT GLFWWindow os f m b
+wrapWindowFun f x = withWindow $ \w -> f w x
+
+-- | Convenience function to quickly wrap callback setters taking window and passing window to the callback.
+-- Wrapped functions don't take window.
+-- Callbacks don't receive window /it is eaten by `const`/.
+wrapCallbackSetter :: (MonadIO m, Functor g) => (GLFW.Window -> g (GLFW.Window -> b) -> IO a) -> g b -> ContextT GLFWWindow os f m a
+wrapCallbackSetter setter cb = withWindow $ \w -> setter w (const <$> cb)
+
+{- Keyboard -}
+
+-- | Register or unregister a callback to receive 'KeyState' changes to any 'Key'.
+setKeyCallback :: MonadIO m => Maybe (Key -> Int -> KeyState -> ModifierKeys -> IO ()) -> ContextT GLFWWindow os f m ()
+setKeyCallback = wrapCallbackSetter GLFW.setKeyCallback
+
+-- | Poll for the 'KeyState' of a 'Key'.
+getKey :: MonadIO m => Key -> ContextT GLFWWindow os f m KeyState
+getKey = wrapWindowFun GLFW.getKey
+
+-- | Polling a 'Key' for 'KeyState' may sometimes miss state transitions.
+-- If you use cannot use a callback to receive 'KeyState' changes,
+-- use 'getKey' in combination with GLFW's sticky-keys feature:
+-- <http://www.glfw.org/docs/latest/input_guide.html#input_key>.
+setStickyKeysInputMode :: MonadIO m => StickyKeysInputMode -> ContextT GLFWWindow os f m ()
+setStickyKeysInputMode = wrapWindowFun GLFW.setStickyKeysInputMode
+
+getStickyKeysInputMode :: MonadIO m => ContextT GLFWWindow os f m StickyKeysInputMode
+getStickyKeysInputMode = withWindow GLFW.getStickyKeysInputMode
+
+-- | Register or unregister a callback to receive character input obeying keyboard layouts and modifier effects.
+setCharCallback :: MonadIO m => Maybe (Char -> IO ()) -> ContextT GLFWWindow os f m ()
+setCharCallback = wrapCallbackSetter GLFW.setCharCallback
+
+{- Mouse -}
+
+-- | Register or unregister a callback to receive mouse location changes.
+-- Callback receives `x` and `y` position measured in screen-coordinates relative to the top left of the GLFW window.
+setCursorPosCallback :: MonadIO m => Maybe (Double -> Double -> IO ()) -> ContextT GLFWWindow os f m ()
+setCursorPosCallback = wrapCallbackSetter GLFW.setCursorPosCallback
+
+-- | Poll for the location of the mouse.
 getCursorPos :: MonadIO m => ContextT GLFWWindow os f m (Double, Double)
-getCursorPos = withContextWindow (GLFW.getCursorPos . getGLFWWindow)
+getCursorPos = withWindow GLFW.getCursorPos
 
--- | Gets the state of the specified 'MouseButton'.
+-- | GLFW supports setting cursor mode to support mouselook and other advanced uses of the mouse:
+-- <http://www.glfw.org/docs/latest/input_guide.html#cursor_mode>.
+setCursorInputMode :: MonadIO m => CursorInputMode -> ContextT GLFWWindow os f m ()
+setCursorInputMode = wrapWindowFun GLFW.setCursorInputMode
+
+getCursorInputMode :: MonadIO m => ContextT GLFWWindow os f m CursorInputMode
+getCursorInputMode = withWindow GLFW.getCursorInputMode
+
+-- | Set the cursor to be displayed over the window while 'CursorInputMode' is `Normal`.
+setCursor :: MonadIO m => Cursor -> ContextT GLFWWindow os f m ()
+setCursor = wrapWindowFun GLFW.setCursor
+
+-- | Register or unregister a callback to receive 'CursorState' changes when the cursor enters or exits the window.
+setCursorEnterCallback :: MonadIO m => Maybe (CursorState -> IO ()) -> ContextT GLFWWindow os f m ()
+setCursorEnterCallback = wrapCallbackSetter GLFW.setCursorEnterCallback
+
+-- | Register or unregister a callback to receive 'MouseButtonState' changes to a 'MouseButton'.
+setMouseButtonCallback :: MonadIO m => Maybe (MouseButton -> MouseButtonState -> ModifierKeys -> IO ()) -> ContextT GLFWWindow os f m ()
+setMouseButtonCallback = wrapCallbackSetter GLFW.setMouseButtonCallback
+
+-- | Poll for the 'MouseButtonState' of a 'MouseButton'.
 getMouseButton :: MonadIO m => MouseButton -> ContextT GLFWWindow os f m MouseButtonState
-getMouseButton mb = withContextWindow (\(GLFWWindow w) -> GLFW.getMouseButton w mb)
+getMouseButton = wrapWindowFun GLFW.getMouseButton
 
--- | Gets the state of the specified 'Key'.
-getKey :: MonadIO m => Key -> ContextT GLFWWindow os f m KeyState
-getKey k = withContextWindow (\(GLFWWindow w) -> GLFW.getKey w k)
+-- | Polling a 'MouseButton' for 'MouseButtonState' may sometimes miss state transitions.
+-- If you use cannot use a callback to receive 'MouseButtonState' changes,
+-- use 'getMouseButton' in combination with GLFW's sticky-mouse-buttons feature:
+-- <http://www.glfw.org/docs/latest/input_guide.html#input_mouse_button>.
+setStickyMouseButtonsInputMode :: MonadIO m => StickyMouseButtonsInputMode -> ContextT GLFWWindow os f m ()
+setStickyMouseButtonsInputMode = wrapWindowFun GLFW.setStickyMouseButtonsInputMode
 
--- | Registers the specified 'ScrollCallback'.
-registerScrollCallback :: MonadIO m => Maybe ScrollCallback -> ContextT GLFWWindow os f m ()
-registerScrollCallback callback = withContextWindow (\win -> GLFW.setScrollCallback (getGLFWWindow win) callback)
+getStickyMouseButtonsInputMode :: MonadIO m => ContextT GLFWWindow os f m StickyMouseButtonsInputMode
+getStickyMouseButtonsInputMode = withWindow GLFW.getStickyMouseButtonsInputMode
 
--- | Returns 'True' if the window should close (e.g. because the user pressed
--- the \'x\' button).
+-- | Register or unregister a callback to receive scroll offset changes.
+setScrollCallback :: MonadIO m => Maybe (Double -> Double -> IO ()) -> ContextT GLFWWindow os f m ()
+setScrollCallback = wrapCallbackSetter GLFW.setScrollCallback
+
+{- Joystick -}
+
+{- Time -}
+
+{- Clipboard -}
+
+-- | Poll the system clipboard for a UTF-8 encoded string, if one can be extracted.
+getClipboardString :: MonadIO m => ContextT GLFWWindow os f m (Maybe String)
+getClipboardString = withWindow GLFW.getClipboardString
+
+-- | Store a UTF-8 encoded string in the system clipboard.
+setClipboardString :: MonadIO m => String -> ContextT GLFWWindow os f m ()
+setClipboardString = wrapWindowFun GLFW.setClipboardString
+
+{- Pathdrop -}
+
+-- | Register or unregister a callback to receive file paths when files are dropped onto the window.
+setDropCallback :: MonadIO m => Maybe ([String] -> IO ()) -> ContextT GLFWWindow os f m ()
+setDropCallback = wrapCallbackSetter GLFW.setDropCallback
+
+{- Deprecated -}
+
+-- | Register or unregister a 'ScrollCallback' to receive scroll events.
+-- Deprecated and will be removed. Prefer to use 'setScrollCallback'.
+registerScrollCallback :: MonadIO m => Maybe GLFW.ScrollCallback -> ContextT GLFWWindow os f m ()
+registerScrollCallback callback = withWindow $ \w -> GLFW.setScrollCallback w callback
+
+-- | Returns 'True' if the window should close (e.g. because the user pressed the \'x\' button).
+-- Deprecated and will be moved to a different module.
 windowShouldClose :: MonadIO m => ContextT GLFWWindow os f m Bool
-windowShouldClose = withContextWindow (GLFW.windowShouldClose . getGLFWWindow)
+windowShouldClose = withWindow GLFW.windowShouldClose
