-- {-# OPTIONS_HADDOCK hide #-} {- | GLUT-based keyboard/mouse handling. Sven Panne 2000 This provides a "still down" event in addition to GLUT's key/mouse button up/down events, and manages bindings from input events to actions. -} module Graphics.UI.GLUT.Input ( Key(..), KeyEvent(..), KeyBinder, InputHandler, StillDownHandler, glutInitInput ) where import Data.IORef(IORef, newIORef, readIORef, modifyIORef) import Data.List(deleteBy) import Graphics.UI.GLUT --------------------------------------------------------------------------- data KeyEvent = Press | StillDown | Release deriving Eq --------------------------------------------------------------------------- -- | A mutable list of keys (or mouse buttons), along with modifier -- state and mouse position. type KeyTable = IORef [(Key, Modifiers, Position)] newKeyTable :: IO KeyTable newKeyTable = newIORef [] getKeys :: KeyTable -> IO [(Key, Modifiers, Position)] getKeys = readIORef insertIntoKeyTable :: KeyTable -> Key -> Modifiers -> Position -> IO () insertIntoKeyTable keyTab key mods pos = modifyIORef keyTab ((key,mods,pos):) deleteFromKeyTable :: KeyTable -> Key -> IO () deleteFromKeyTable keyTab key = modifyIORef keyTab (deleteBy (\(k,_,_) (l,_,_) -> k==l) (key, nullmods, nullpos)) where nullmods = Modifiers Up Up Up nullpos = Position 0 0 --------------------------------------------------------------------------- type InputHandler = Modifiers -> Position -> IO () type KeyBinder = Key -> KeyEvent -> Maybe InputHandler -> IO () -- TODO: Improve type -- | A mutable list of mappings from key/mousebutton up/down/stilldown -- events to IO actions. type BindingTable = IORef [((Key,KeyEvent), InputHandler)] newBindingTable :: IO BindingTable newBindingTable = newIORef [] bindKey :: BindingTable -> KeyBinder bindKey bindingTable key event Nothing = modifyIORef bindingTable (\t -> [ e | e@(b,_) <- t, b /= (key, event)]) bindKey bindingTable key event (Just action) = do bindKey bindingTable key event Nothing modifyIORef bindingTable (((key, event), action) :) execAction :: BindingTable -> Key -> KeyEvent -> Modifiers -> Position -> IO () execAction bindingTable key event mods pos = readIORef bindingTable >>= (maybe (return ()) (\a -> a mods pos) . lookup (key, event)) --------------------------------------------------------------------------- type StillDownHandler = IO () stillDown :: BindingTable -> KeyTable -> StillDownHandler stillDown bindingTable pressedKeys = getKeys pressedKeys >>= mapM_ (\(k,mods,pos) -> execAction bindingTable k StillDown mods pos) --------------------------------------------------------------------------- -- | Initialise the input system, which keeps a list of input event to -- action bindings and executes the the proper actions automatically. -- Returns a function for adding bindings, and another which should be -- called periodically (eg from refresh) to trigger still-down actions. glutInitInput :: IO (KeyBinder, StillDownHandler) glutInitInput = do -- globalKeyRepeat would be a little bit more efficient, but it has -- two disadvantages: it is not yet implemented for MS windows and -- it changes the global state of X11. perWindowKeyRepeat $= PerWindowKeyRepeatOff bindingTable <- newBindingTable pressedKeys <- newKeyTable let keyPress k mods pos = do insertIntoKeyTable pressedKeys k mods pos execAction bindingTable k Press mods pos keyRelease k mods pos = do deleteFromKeyTable pressedKeys k execAction bindingTable k Release mods pos keyboardMouse k Down mods pos = keyPress k mods pos keyboardMouse k Up mods pos = keyRelease k mods pos keyboardMouseCallback $= Just keyboardMouse return (bindKey bindingTable, stillDown bindingTable pressedKeys)