packages feed

GLFW-task (empty) → 0.1.0

raw patch · 6 files changed

+266/−0 lines, 6 filesdep +GLFWdep +OpenGLdep +basesetup-changed

Dependencies added: GLFW, OpenGL, base, monad-task, transformers

Files

+ GLFW-task.cabal view
@@ -0,0 +1,36 @@+name:          GLFW-task+version:       0.1.0+homepage:      http://github.com/ninegua/GLFW-task+author:        Paul Liu +maintainer:    Paul Liu <paul@thev.net>+stability:     experimental+category:      Graphics+cabal-version: >= 1.6+build-type:    Simple+synopsis:      GLFW utility functions for use with monad-task.+description:   GLFW (http://hackage.haskell.org/package/GLFW) is a Haskell +               binding to the GLFW C library for writing OpenGL programs. +               This package provides some utility functions for writing GLFW +               programs using the monad-task library +               (http://hackage.haskell.org/package/GLFW-task). +license:       BSD3+license-file:  LICENSE++extra-source-files:+  README.md+  example/example.lhs++library+  build-depends:+    base       >= 3     && < 5,+    OpenGL     >= 2.1   && < 3,+    GLFW       == 0.5.*,+    monad-task == 0.*,+    transformers < 4+    +  exposed-modules:+    Graphics.UI.GLFW.Task++source-repository head+  type:     git+  location: git://github.com/ninegua/GLFW-task
+ Graphics/UI/GLFW/Task.hs view
@@ -0,0 +1,109 @@+{- | GLFW helper functions for use with a @`TaskT`@ monad transformer (from monad-task package).+-}+{-# LANGUAGE FlexibleContexts, TupleSections #-}+module Graphics.UI.GLFW.Task +  (+    Event(..)+  , or+  , onKey+  , onChar+  , onButton+  , onPos+  , onWheel+  , onSize+  , onClose+  , onRefresh+  , isKey+  , isChar+  , isButton+  , isPress+  , isRelease+  , registerTaskCallbacks+  ) where++import Control.Concurrent+import Graphics.UI.GLFW +import qualified Graphics.Rendering.OpenGL as GL+import Graphics.Rendering.OpenGL (($=))+import Control.Monad.IO.Class +import Control.Monad.Task.Class ++-- | @Event@ is a unified data type for all GLFW events.+data Event +  = KeyEvent Key KeyButtonState+  | CharEvent Char KeyButtonState+  | MouseButtonEvent MouseButton KeyButtonState+  | MousePosEvent GL.Position +  | MouseWheelEvent Int+  | WindowSizeEvent GL.Size +  | WindowCloseEvent +  | WindowRefreshEvent+  deriving (Eq, Show)+++onKey     :: Event -> Maybe (Key, KeyButtonState)+onKey     (KeyEvent k s)         = Just (k, s)+onKey     _                      = Nothing+onChar    :: Event -> Maybe (Char, KeyButtonState)+onChar    (CharEvent ch s)       = Just (ch, s)+onChar    _                      = Nothing+onButton  :: Event -> Maybe (MouseButton, KeyButtonState)+onButton  (MouseButtonEvent b s) = Just (b, s)+onButton  _                      = Nothing+onPos     :: Event -> Maybe GL.Position+onPos     (MousePosEvent p)      = Just p+onPos     _                      = Nothing+onWheel   :: Event -> Maybe Int+onWheel   (MouseWheelEvent w)    = Just w+onWheel   _                      = Nothing+onSize    :: Event -> Maybe GL.Size+onSize    (WindowSizeEvent s)    = Just s+onSize    _                      = Nothing+onClose   :: Event -> Maybe ()+onClose   WindowCloseEvent       = Just ()+onClose   _                      = Nothing+onRefresh :: Event -> Maybe ()+onRefresh WindowRefreshEvent     = Just ()+onRefresh _                      = Nothing++isKey :: Enum a => a -> Key -> Maybe ()+isKey key k | fromEnum k == fromEnum key = Just ()+            | otherwise                  = Nothing++isChar :: Char -> Char -> Maybe ()+isChar ch c | ch == c   = Just ()+            | otherwise = Nothing++isButton :: MouseButton -> (MouseButton, KeyButtonState) -> Maybe KeyButtonState+isButton but (b, s) | b == but  = Just s+                    | otherwise = Nothing++isPress, isRelease :: (a, KeyButtonState) -> Maybe a+isPress   (b, s) | s == Press   = Just b +                 | otherwise    = Nothing++isRelease (b, s) | s == Release = Just b +                 | otherwise    = Nothing++-- | @registerTaskCallbacks@ sets up all event callbacks, and returns a +--   'Graphics.UI.GLFW.waitEvent' equivalent function for task monad,+--   which must be called repeatedly in order to pump events to other +--   task co-routines.+--+--   These task co-routines should use 'Control.Monad.Task.Class.watch' +--   to select event of interest, and they should be forked prior to+--   the waitEvent call.+registerTaskCallbacks :: (MonadIO m, MonadTask Event m) => IO (m ())+registerTaskCallbacks = do+  q <- newMVar []+  let enqueue = modifyMVar_ q . (return .) . (:)+      dequeue = modifyMVar q $ return . ([],)+  windowRefreshCallback $= enqueue WindowRefreshEvent+  windowSizeCallback    $= enqueue . WindowSizeEvent+  windowCloseCallback   $= (enqueue WindowCloseEvent >> return True)+  keyCallback           $= (enqueue .) . KeyEvent+  charCallback          $= (enqueue .) . CharEvent+  mouseButtonCallback   $= (enqueue .) . MouseButtonEvent+  mousePosCallback      $= enqueue . MousePosEvent+  mouseWheelCallback    $= enqueue . MouseWheelEvent+  return (liftIO (waitEvents >> dequeue) >>= mapM_ signal . reverse)
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2012 Paul H. Liu <paul@thev.net>++This software is provided 'as-is', without any express or implied+warranty. In no event will the authors be held liable for any damages+arising from the use of this software.++Permission is granted to anyone to use this software for any purpose,+including commercial applications, and to alter it and redistribute it+freely, subject to the following restrictions:++1. The origin of this software must not be misrepresented; you must not+   claim that you wrote the original software. If you use this software+   in a product, an acknowledgment in the product documentation would+   be appreciated but is not required.++2. Altered source versions must be plainly marked as such, and must not+   be misrepresented as being the original software.++3. This notice may not be removed or altered from any source+   distribution.
+ README.md view
@@ -0,0 +1,8 @@+GLFW-task+=========++GLFW utility functions for use with monad-task.++See [Invert the Inversion of Control](http://www.thev.net/PaulLiu/invert-inversion.html)+for a tutorial on writing a GLFW/OpenGL application using task monad.+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ example/example.lhs view
@@ -0,0 +1,91 @@+This program is adapted from GLFW/example/example.hs to use monad-control library, and is identital to the code from the tutorial [Invert the Inversion of Control](http://www.thev.net/PaulLiu/invert-inversion.html), which has more details. ++To demonstrate the usage of GLFW for OpenGL based Haskell applications, here is a sample program that allows user to draw lines by holding the left mouse button and move the mouse.++\begin{code}+import qualified Graphics.Rendering.OpenGL as GL+import qualified Graphics.UI.GLFW as GLFW+import Graphics.UI.GLFW.Task+import Graphics.Rendering.OpenGL (($=))+import Control.Monad.Task+import Control.Monad.IO.Class+import Control.Monad.State+import Control.Monad+import Prelude hiding (lines)++main = do+  GLFW.initialize+  -- open window+  GLFW.openWindow (GL.Size 400 400) [GLFW.DisplayAlphaBits 8] GLFW.Window+  GLFW.windowTitle $= "GLFW Demo"+  GL.shadeModel    $= GL.Smooth+  -- enable antialiasing+  GL.lineSmooth $= GL.Enabled+  GL.blend      $= GL.Enabled+  GL.blendFunc  $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)+  GL.lineWidth  $= 1.5+  -- set the color to clear background+  GL.clearColor $= GL.Color4 0 0 0 0+  -- run the main app+  lineTask+  -- finish up+  GLFW.closeWindow+  GLFW.terminate++set2DViewport size@(GL.Size w h) = do+  GL.viewport   $= (GL.Position 0 0, size)+  GL.matrixMode $= GL.Projection+  GL.loadIdentity+  GL.ortho2D 0 (realToFrac w) (realToFrac h) 0++type Lines = [(GL.GLint, GL.GLint)]++drawLines :: Lines -> IO ()+drawLines lines = do+  GL.clear [GL.ColorBuffer]+  GL.color $ GL.Color3 1 0 (0::GL.GLdouble)+  GL.renderPrimitive GL.Lines $ mapM_ +    (\ (x, y) -> GL.vertex (GL.Vertex3 (fromIntegral x) +                                       (fromIntegral y) +                                       (0::GL.GLdouble))) lines++data S = S { lines :: Lines, dirty :: Bool }+type M a = TaskT Event (StateT S IO) a++getLines      = fmap lines get+modifyLines f = modify $ \x -> x { lines = f (lines x), dirty = True }+getDirty      = fmap dirty get+putDirty y    = modify $ \x -> x { dirty = y }++repeatUntil f m = loop+  where loop = m >> watch Just >>= maybe loop return . f++buttonPress   = onButton >=> isPress+buttonRelease = onButton >=> isRelease+isESC = onKey >=> isPress >=> isKey GLFW.ESC ++lineTask :: IO ()+lineTask = (`evalStateT` (S [] False)) . runTask $ do+  -- here the monad is of type M ()+  waitForEvents <- liftIO registerTaskCallbacks+  fork $ forever $ watch onSize >>= liftIO . set2DViewport+  fork $ forever $ watch onClose >> exit+  fork $ forever $ watch onRefresh >> putDirty True+  fork $ forever $ watch isESC >> exit+  fork $ forever $ interaction+  forever $ do+    waitForEvents+    d <- getDirty+    when d $ getLines >>= liftIO . drawLines >> liftIO GLFW.swapBuffers+    putDirty False+    yield+  where+    interaction = do+      watch buttonPress+      (GL.Position x y) <- liftIO $ GL.get GLFW.mousePos+      modifyLines (((x,y):) . ((x,y):)) +      repeatUntil buttonRelease $ do+        (GL.Position x y) <- liftIO $ GL.get GLFW.mousePos+        modifyLines (((x,y):) . tail) +\end{code}+