free-game 0.3.2.7 → 0.9
raw patch · 27 files changed
+1294/−1281 lines, 27 filesdep +data-defaultdep +lineardep +profunctorsdep −vectdep ~OpenGLRawdep ~repa
Dependencies added: data-default, linear, profunctors, vector
Dependencies removed: vect
Dependency ranges changed: OpenGLRaw, repa
Files
- Graphics/FreeGame.hs +0/−52
- Graphics/FreeGame/Backends/GLFW.hs +0/−254
- Graphics/FreeGame/Base.hs +0/−170
- Graphics/FreeGame/Data/Bitmap.hs +0/−84
- Graphics/FreeGame/Data/Color.hs +0/−92
- Graphics/FreeGame/Data/Font.hs +0/−158
- Graphics/FreeGame/Input.hs +0/−87
- Graphics/FreeGame/Internal/Finalizer.hs +0/−34
- Graphics/FreeGame/Simple.hs +0/−92
- Graphics/FreeGame/Types.hs +0/−17
- Graphics/FreeGame/Util.hs +0/−107
- Graphics/UI/FreeGame.hs +62/−0
- Graphics/UI/FreeGame/Base.hs +282/−0
- Graphics/UI/FreeGame/Data/Bitmap.hs +84/−0
- Graphics/UI/FreeGame/Data/Color.hs +96/−0
- Graphics/UI/FreeGame/Data/Font.hs +159/−0
- Graphics/UI/FreeGame/GUI.hs +126/−0
- Graphics/UI/FreeGame/GUI/GLFW.hs +243/−0
- Graphics/UI/FreeGame/Internal/Finalizer.hs +34/−0
- Graphics/UI/FreeGame/Types.hs +40/−0
- Graphics/UI/FreeGame/Util.hs +127/−0
- examples/VL-PGothic-Regular.ttf too large to diff
- examples/bitmap.hs +0/−21
- examples/font.hs +0/−16
- examples/images/logo.png binary
- examples/test.hs +0/−59
- free-game.cabal +41/−38
− Graphics/FreeGame.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE Rank2Types, FlexibleContexts #-} -{-| -Module : Graphics.FreeGame -Copyright : (C) 2012 Fumiaki Kinoshita -License : BSD-style (see the file LICENSE) -Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> - -free-game is a library that abstracts and purifies GUI applications with simple interfaces. --} -module Graphics.FreeGame - ( -- * Examples - -- $example - - -- * Reexports - module Graphics.FreeGame.Base, - module Graphics.FreeGame.Data.Bitmap, - module Graphics.FreeGame.Data.Font, - module Graphics.FreeGame.Data.Color, - module Graphics.FreeGame.Input, - module Graphics.FreeGame.Util, - runGame, - runGame' -) where - -import Graphics.FreeGame.Base -import Graphics.FreeGame.Input -import Graphics.FreeGame.Util -import Graphics.FreeGame.Data.Bitmap -import Graphics.FreeGame.Data.Font -import Graphics.FreeGame.Data.Color -import qualified Graphics.FreeGame.Backends.GLFW as GLFW -import Control.Monad.Free - --- | Run a 'Game' computation. -runGame :: GameParam -> Game a -> IO (Maybe a) -runGame = GLFW.runGame - --- | In most cases there's no unwrapping (Game a -> GameAction (Game a)). --- | The use of ('>>=') is more efficient than 'runGame' in such situation. -runGame' :: GameParam -> (forall m. MonadFree GameAction m => m a) -> IO (Maybe a) -runGame' = GLFW.runGame' - -{- $example - -> import Graphics.FreeGame.Simple -> main = runSimple defaultGameParam () return - -shows a window and does nothing. - -for more examples, see <https://github.com/fumieval/free-game/tree/master/examples>. - --}
− Graphics/FreeGame/Backends/GLFW.hs
@@ -1,254 +0,0 @@------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Backends.GLFW --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : experimental --- Portability : non-portable --- ----------------------------------------------------------------------------- -{-# LANGUAGE ImplicitParams, ScopedTypeVariables, Rank2Types, FlexibleContexts #-} -{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} -module Graphics.FreeGame.Backends.GLFW (runGame, runGame') where -import Control.Applicative -import Control.Monad -import Control.Monad.Free -import Control.Monad.Free.Church -import Control.Monad.IO.Class -import Data.IORef -import Foreign.ForeignPtr -import Graphics.FreeGame.Base -import Graphics.FreeGame.Data.Bitmap -import Graphics.FreeGame.Internal.Finalizer -import Graphics.Rendering.OpenGL.Raw.ARB.Compatibility -import Graphics.UI.GLFW as GLFW -import Graphics.Rendering.OpenGL.GL.StateVar -import qualified Data.Array.Repa.Repr.ForeignPtr as RF -import qualified Data.IntMap as IM -import qualified Graphics.FreeGame.Input as I -import qualified Graphics.Rendering.OpenGL.GL as GL -import System.Mem -import Unsafe.Coerce - -runGame :: GameParam -> Game a -> IO (Maybe a) -runGame param m = launch param $ \r s -> runFreeGame param r s m - -runGame' :: GameParam -> (forall m. MonadFree GameAction m => m a) -> IO (Maybe a) -runGame' param m = launch param $ \r s -> runF m (return . Just) (runAction param r s) - -runFreeGame :: GameParam -> IORef (IM.IntMap Texture) -> IORef Int -> Free GameAction a -> FinalizerT IO (Maybe a) -runFreeGame p r s = go where - go (Free f) = runAction p r s $ go <$> f - go (Pure a) = return $ Just a - -runAction :: GameParam - -> IORef (IM.IntMap Texture) - -> IORef Int - -> GameAction (FinalizerT IO (Maybe a)) -> FinalizerT IO (Maybe a) -runAction param refTextures refFrame _f = case _f of - DrawPicture pic cont -> let ?refTextures = refTextures in drawPic pic >> cont - EmbedIO m -> join (liftIO m) - Bracket m -> liftIO (runFinalizerT $ runFreeGame param refTextures refFrame m) >>= maybe (return Nothing) id - Tick cont -> do - liftIO $ do - GL.matrixMode $= GL.Projection - swapBuffers - t <- getTime - n <- readIORef refFrame - sleep (fromIntegral n / fromIntegral (framePerSecond param) - t) - if t > 1 - then resetTime >> writeIORef refFrame 0 - else writeIORef refFrame (succ n) - - r <- liftIO $ windowIsOpen - if not r then return Nothing else do - liftIO $ do - GL.clear [GL.ColorBuffer] - performGC - GL.loadIdentity - GL.scale (gf 1) (-1) 1 - let Vec2 ox oy = windowOrigin param - windowL = realToFrac ox - windowR = realToFrac ox + fromIntegral (fst $ windowSize param) - windowT = realToFrac oy - windowB = realToFrac oy + fromIntegral (snd $ windowSize param) - GL.ortho windowL windowR windowT windowB 0 (-100) - GL.matrixMode $= GL.Modelview 0 - cont - GetButtonState key fcont -> liftIO (either keyIsPressed mouseButtonIsPressed (mapKey key)) >>= fcont - GetMousePosition fcont -> do - (x, y) <- liftIO $ GLFW.getMousePosition - fcont $ Vec2 (fromIntegral x) (fromIntegral y) - GetMouseWheel fcont -> liftIO GLFW.getMouseWheel >>= fcont - GetGameParam fcont -> do -- There may be a better way - dim <- liftIO GLFW.getWindowDimensions - fcont $ param { windowSize = dim } - QuitGame -> return Nothing - -data Texture = Texture GL.TextureObject Int Int - -launch :: GameParam -> (IORef (IM.IntMap Texture) -> IORef Int -> FinalizerT IO (Maybe a)) -> IO (Maybe a) -launch param m = do - True <- initialize - pf <- openGLProfile - True <- openWindow $ defaultDisplayOptions { - displayOptions_width = fromIntegral $ fst $ windowSize param - ,displayOptions_height = fromIntegral $ snd $ windowSize param - ,displayOptions_displayMode = if windowed param then Window else Fullscreen - ,displayOptions_windowIsResizable = False - ,displayOptions_openGLProfile = pf - } - - if cursorVisible param - then enableMouseCursor - else disableMouseCursor - - setWindowTitle $ windowTitle param - GL.lineSmooth $= GL.Enabled - GL.blend $= GL.Enabled - GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha) - GL.shadeModel $= GL.Smooth - GL.texture GL.Texture2D $= GL.Enabled - GL.textureFunction $= GL.Combine - - let Color r g b a = clearColor param in GL.clearColor $= GL.Color4 (gf r) (gf g) (gf b) (gf a) - - ref <- newIORef IM.empty - ref' <- newIORef 0 - r <- runFinalizerT $ m ref ref' - - closeWindow - terminate - return r - -installTexture :: Bitmap -> FinalizerT IO Texture -installTexture bmp = do - [tex] <- liftIO $ GL.genObjectNames 1 - liftIO $ GL.textureBinding GL.Texture2D GL.$= Just tex - - let (width, height) = bitmapSize bmp - liftIO $ withForeignPtr (RF.toForeignPtr $ bitmapData bmp) - $ GL.texImage2D Nothing GL.NoProxy 0 GL.RGBA8 (GL.TextureSize2D (gsizei width) (gsizei height)) 0 - . GL.PixelData GL.RGBA GL.UnsignedInt8888 - finalizer $ GL.deleteObjectNames [tex] - return $ Texture tex width height - -drawTexture :: Texture -> IO () -drawTexture (Texture tex width height) = do - let (w, h) = (fromIntegral width / 2, fromIntegral height / 2) - GL.textureFilter GL.Texture2D $= ((GL.Nearest, Nothing), GL.Nearest) - GL.textureBinding GL.Texture2D $= Just tex - GL.renderPrimitive GL.Polygon $ zipWithM_ - (\(pX, pY) (tX, tY) -> do - GL.texCoord $ GL.TexCoord2 (gf tX) (gf tY) - GL.vertex $ GL.Vertex2 (gf pX) (gf pY)) - [(-w, -h), (w, -h), (w, h), (-w, h)] - [(0,0), (1.0,0), (1.0,1.0), (0,1.0)] - -drawPic :: (?refTextures :: IORef (IM.IntMap Texture)) => Picture -> FinalizerT IO () -drawPic (Bitmap bmp) = case bitmapHash bmp of - Just h -> do - m <- liftIO $ readIORef ?refTextures - case IM.lookup h m of - Just t -> liftIO $ drawTexture t - Nothing -> do - t <- installTexture bmp - liftIO $ writeIORef ?refTextures $ IM.insert h t m - liftIO $ drawTexture t - finalizer $ modifyIORef ?refTextures $ IM.delete h - Nothing -> liftIO $ runFinalizerT $ installTexture bmp >>= liftIO . drawTexture - -drawPic (BitmapPicture bmp) = drawPic (Bitmap bmp) - -drawPic (Rotate theta p) = preservingMatrix' $ do - liftIO $ GL.rotate (gf (-theta)) (GL.Vector3 0 0 1) - drawPic p - -drawPic (Scale (Vec2 sx sy) p) = preservingMatrix' $ do - liftIO $ GL.scale (gf sx) (gf sy) 1 - drawPic p - -drawPic (Translate (Vec2 tx ty) p) = preservingMatrix' $ do - liftIO $ GL.translate (GL.Vector3 (gf tx) (gf ty) 0) - drawPic p - -drawPic (Pictures ps) = mapM_ drawPic ps - -drawPic (PictureWithFinalizer m) = m >>= drawPic - -drawPic (Colored (Color r g b a) pic) = do - oldColor <- liftIO $ get GL.currentColor - liftIO $ GL.currentColor $= GL.Color4 (gf r) (gf g) (gf b) (gf a) - drawPic pic - liftIO $ GL.currentColor $= oldColor - -preservingMatrix' :: MonadIO m => m () -> m () -preservingMatrix' m = do - liftIO $ glPushMatrix - _ <- m - liftIO $ glPopMatrix - -mapKey :: I.Button -> Either Key MouseButton -mapKey k = case k of - I.KeyChar c -> Left $ CharKey c - I.KeySpace -> Left KeySpace - I.KeyF1 -> Left KeyF1 - I.KeyF2 -> Left KeyF2 - I.KeyF3 -> Left KeyF3 - I.KeyF4 -> Left KeyF4 - I.KeyF5 -> Left KeyF5 - I.KeyF6 -> Left KeyF6 - I.KeyF7 -> Left KeyF7 - I.KeyF8 -> Left KeyF8 - I.KeyF9 -> Left KeyF9 - I.KeyF10 -> Left KeyF10 - I.KeyF11 -> Left KeyF11 - I.KeyF12 -> Left KeyF12 - I.KeyEsc -> Left KeyEsc - I.KeyUp -> Left KeyUp - I.KeyDown -> Left KeyDown - I.KeyLeft -> Left KeyLeft - I.KeyRight -> Left KeyRight - I.KeyLeftShift -> Left KeyLeftShift - I.KeyRightShift -> Left KeyLeftShift - I.KeyLeftControl -> Left KeyLeftCtrl - I.KeyRightControl -> Left KeyRightCtrl - I.KeyTab -> Left KeyTab - I.KeyEnter -> Left KeyEnter - I.KeyBackspace -> Left KeyBackspace - I.KeyInsert -> Left KeyInsert - I.KeyDelete -> Left KeyDel - I.KeyPageUp -> Left KeyPageup - I.KeyPageDown -> Left KeyPagedown - I.KeyHome -> Left KeyHome - I.KeyEnd -> Left KeyEnd - I.KeyPad0 -> Left KeyPad0 - I.KeyPad1 -> Left KeyPad1 - I.KeyPad2 -> Left KeyPad2 - I.KeyPad3 -> Left KeyPad3 - I.KeyPad4 -> Left KeyPad4 - I.KeyPad5 -> Left KeyPad5 - I.KeyPad6 -> Left KeyPad6 - I.KeyPad7 -> Left KeyPad7 - I.KeyPad8 -> Left KeyPad8 - I.KeyPad9 -> Left KeyPad9 - I.KeyPadDivide -> Left KeyPadDivide - I.KeyPadMultiply -> Left KeyPadMultiply - I.KeyPadSubtract -> Left KeyPadSubtract - I.KeyPadAdd -> Left KeyPadAdd - I.KeyPadDecimal -> Left KeyPadDecimal - I.KeyPadEqual -> Left KeyPadEqual - I.KeyPadEnter -> Left KeyPadEnter - I.MouseLeft -> Right MouseButton0 - I.MouseRight -> Right MouseButton1 - I.MouseMiddle -> Right MouseButton2 - -gf :: Float -> GL.GLfloat -{-# INLINE gf #-} -gf x = unsafeCoerce x - -gsizei :: Int -> GL.GLsizei -{-# INLINE gsizei #-} -gsizei x = unsafeCoerce x
− Graphics/FreeGame/Base.hs
@@ -1,170 +0,0 @@-{-# LANGUAGE FlexibleContexts, DeriveFunctor, FlexibleInstances #-} ------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Base --- Copyright : (C) 2012-2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : experimental --- Portability : non-portable --- --- Abstract structures that represents user interfaces ----------------------------------------------------------------------------- - -module Graphics.FreeGame.Base ( - -- * Types - Game - ,GameAction(..) - - -- * Basic operations - ,tick - ,embedIO - ,bracket - ,quitGame - - -- * Pictures - ,Vec2(..) - ,Color(..) - ,Picture(..) - ,transPicture - ,drawPicture - - -- * Inputs - ,getButtonState - ,getMousePosition - ,getMouseWheel - - -- * Settings - ,GameParam(..) - ,defaultGameParam - ,getCurrentGameParam - - -- * Deprecated - ,askInput - ,getMouseState -) where - -import Control.Monad.Free -import Control.Monad -import Graphics.FreeGame.Data.Color -import Graphics.FreeGame.Data.Bitmap -import Graphics.FreeGame.Input -import Graphics.FreeGame.Internal.Finalizer -import Control.Monad.IO.Class -import Data.Vect -import Data.Void - -infixr 5 `Translate` -infixr 5 `Rotate` -infixr 5 `Scale` -infixr 5 `Colored` - --- | 'Game' is a 'Monad' that abstracts user interfaces. -type Game = Free GameAction - -instance MonadIO Game where - liftIO = embedIO - --- | A base for 'Game' monad. -data GameAction a - = Tick a - | EmbedIO (IO a) - | Bracket (Game a) - | DrawPicture Picture a - | GetButtonState Button (Bool -> a) - | GetMousePosition (Vec2 -> a) - | GetMouseWheel (Int -> a) - | GetGameParam (GameParam -> a) - | QuitGame - deriving Functor - --- | Finalize the current frame and refresh the screen. -tick :: MonadFree GameAction m => m () -tick = wrap $ Tick (return ()) - --- | Embed arbitrary 'IO' actions into a 'Game' monad. -embedIO :: MonadFree GameAction m => IO a -> m a -embedIO m = wrap $ EmbedIO $ liftM return m - --- | Run a Game monad in a Game monad. resources (e.g. pictures) will be released when inner computation is done. -bracket :: MonadFree GameAction m => Game a -> m a -bracket m = wrap $ Bracket $ liftM return m - --- | Break the current computation. -quitGame :: MonadFree GameAction m => m Void -quitGame = wrap QuitGame - --- | Draw a 'Picture'. -drawPicture :: MonadFree GameAction m => Picture -> m () -drawPicture pic = wrap $ DrawPicture pic (return ()) - --- | Is the specified 'Button' is pressed? -getButtonState :: MonadFree GameAction m => Button -> m Bool -getButtonState key = wrap $ GetButtonState key return - -getMouseWheel :: MonadFree GameAction m => m Int -getMouseWheel = wrap $ GetMouseWheel return - -getMousePosition :: MonadFree GameAction m => m Vec2 -getMousePosition = wrap $ GetMousePosition return - --- | Get the game params that apply to the currently running game. -getCurrentGameParam :: MonadFree GameAction m => m GameParam -getCurrentGameParam = wrap $ GetGameParam return - --- | Lift a picture transformation into transformation of 'GameAction' -transPicture :: (Picture -> Picture) -> GameAction cont -> GameAction cont -transPicture f (DrawPicture p cont) = DrawPicture (f p) cont -transPicture _ x = x - --- | A 2D Picture. -data Picture - -- | A 'Bitmap' as a 'Picture'. - = Bitmap Bitmap - -- | Deprecated synonym for 'Bitmap'. - | BitmapPicture Bitmap - -- | A picture consist of some 'Picture's. - | Pictures [Picture] - -- | A picture that may have side effects(internal use only). - | PictureWithFinalizer (FinalizerT IO Picture) - -- | Rotated picture by the given angle (in degrees, counterclockwise). - | Rotate Float Picture - -- | Scaled picture. - | Scale Vec2 Picture - -- | A picture translated by the given coordinate. - | Translate Vec2 Picture - -- | Colored picture. - | Colored Color Picture - -{-# DEPRECATED BitmapPicture "use Bitmap instead" #-} - --- | Parameters of the application. -data GameParam = GameParam - { framePerSecond :: Int - , windowSize :: (Int, Int) - , windowTitle :: String - , windowed :: Bool - , cursorVisible :: Bool - , clearColor :: Color - , windowOrigin :: Vec2 - } deriving Show - --- | 640*480(windowed), 60fps -defaultGameParam :: GameParam -defaultGameParam = GameParam 60 (640,480) "free-game" True True white (Vec2 0 0) - --- | Yields the mouse's state. -getMouseState :: MonadFree GameAction m => m MouseState -getMouseState = MouseState - `liftM` getMousePosition - `ap` askInput MouseLeft - `ap` askInput MouseMiddle - `ap` askInput MouseRight - `ap` getMouseWheel -{-# DEPRECATED getMouseState "use getMousePosition and getMouseWheel instead." #-} - --- | Deprecated synonym for 'getButtonState'. -askInput :: MonadFree GameAction m => Button -> m Bool -askInput = getButtonState -{-# DEPRECATED askInput "use getButtonState instead." #-}
− Graphics/FreeGame/Data/Bitmap.hs
@@ -1,84 +0,0 @@------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Data.Bitmap --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : experimental --- Portability : non-portable --- --- Manipulating bitmaps ----------------------------------------------------------------------------- - -module Graphics.FreeGame.Data.Bitmap ( - -- * Basic types and functions - Bitmap - ,bitmapData - ,bitmapHash - ,bitmapSize - - -- * Loading from a file - ,loadBitmapFromFile - - -- * Constructing bitmaps - ,toBitmap - ,toStableBitmap - ,makeStableBitmap - - -- * Bitmap operations - ,onBitmap - ,onBitmapWithHashable - ,cropBitmap - - ) where - -import Control.Applicative -import Codec.Picture.Repa -import Data.Array.Repa as R -import qualified Data.Array.Repa.Repr.ForeignPtr as RF -import Data.Word -import System.Random -import Data.Hashable - --- | Concrete bitmap data -data Bitmap = Bitmap { - bitmapData :: R.Array RF.F DIM3 Word8 -- ^ Bare the 'Bitmap''s internal representation (y * x * RGBA). - ,bitmapHash :: Maybe Int -- ^ This value is used to ensure that two bitmaps are equivalent. - } - --- | Create unstable 'Bitmap' from the given array. -toBitmap :: R.Array RF.F DIM3 Word8 -> Bitmap -toBitmap ar = Bitmap ar Nothing - --- | Create stable 'Bitmap' from the given array and compute the hash. -toStableBitmap :: R.Array RF.F DIM3 Word8 -> Bitmap -toStableBitmap ar = Bitmap ar $ Just $ head $ foldAllP combine 0 $ R.map fromIntegral ar where - combine p q = hash (p, q) - --- | Create stable 'Bitmap' with unique hash from the given array. -makeStableBitmap :: R.Array RF.F DIM3 Word8 -> IO Bitmap -makeStableBitmap ar = Bitmap ar <$> Just <$> randomIO - --- | Get the size of the 'Bitmap'. -bitmapSize :: Bitmap -> (Int, Int) -bitmapSize bmp = let (Z :. h :. w :. _) = R.extent (bitmapData bmp) in (w, h) - --- | Create a 'Bitmap' from the given file. -loadBitmapFromFile :: FilePath -> IO Bitmap -loadBitmapFromFile path = readImageRGBA path >>= either fail return >>= makeStableBitmap . imgData - --- | Convert the 'Bitmap' by the given function. -onBitmap :: (R.Array RF.F DIM3 Word8 -> R.Array RF.F DIM3 Word8) -> Bitmap -> Bitmap -onBitmap f = toStableBitmap . f . bitmapData - --- | Convert the 'Bitmap' uniformalized by the 'Hashable' value by the given function. -onBitmapWithHashable :: Hashable h => h -> (R.Array RF.F DIM3 Word8 -> R.Array RF.F DIM3 Word8) -> Bitmap -> Bitmap -onBitmapWithHashable v f (Bitmap ar h) = Bitmap (f ar) (hash <$> (,) v <$> h) - --- | Extract a 'Bitmap' from the specified range. -cropBitmap :: Bitmap -- ^original bitmap - -> (Int, Int) -- ^width and height - -> (Int, Int) -- ^x and y - -> Bitmap -- ^result -cropBitmap bmp (w, h) (x, y) = onBitmapWithHashable (w,h,x,y) (head . computeP . extract (Z :. y :. x :. 0) (Z :. h :. w :. 4)) bmp
− Graphics/FreeGame/Data/Color.hs
@@ -1,92 +0,0 @@------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Data.Color --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : provisional --- Portability : non-portable --- --- Colors and its operations ----------------------------------------------------------------------------- -module Graphics.FreeGame.Data.Color ( - -- * The type - Color(..) - -- * Color operations - , transparent - , lighten - , darken - , intermediate - -- * Deprecated - , halfD - , halfB - -- * Basic colors - , white, black, red, green, blue, yellow, cyan, magenta - ) where - -import Data.String -import Data.Char - --- | A color that has red, green, blue, alpha as its component. -data Color = Color Float Float Float Float deriving (Show, Eq, Ord) - -hf :: Char -> Float -hf x = fromIntegral (digitToInt x) / 15 - -hf' :: Char -> Char -> Float -hf' x y = fromIntegral (digitToInt x * 16 + digitToInt y) / 255 - -instance IsString Color where - fromString xs@[r,g,b,a] | all isHexDigit xs = Color (hf r) (hf g) (hf b) (hf a) - fromString xs@[r,g,b] | all isHexDigit xs = Color (hf r) (hf g) (hf b) 1 - fromString xs@[r1,r0,g1,g0,b1,b0,a1,a0] | all isHexDigit xs = Color (hf' r1 r0) (hf' g1 g0) (hf' b1 b0) (hf' a1 a0) - fromString xs@[r1,r0,g1,g0,b1,b0] | all isHexDigit xs = Color (hf' r1 r0) (hf' g1 g0) (hf' b1 b0) 1 - fromString x = error $ "Invalid color representation: " ++ x - -transparent :: Float -> Color -> Color -transparent f (Color r g b a) = Color r g b (f * a) - -lighten :: Float -> Color -> Color -lighten f (Color r g b a) = Color (r * (1 - f) + f) (g * (1 - f) + f) (b * (1 - f) + f) a - -darken :: Float -> Color -> Color -darken f (Color r g b a) = Color (r * (1 - f)) (g * (1 - f)) (b * (1 - f)) a - --- | An intermediate between the given colors. -intermediate :: Color -> Color -> Color -intermediate (Color r0 g0 b0 a0) (Color r1 g1 b1 a1) = Color ((r0 + r1)/2) ((g0 + g1)/2) ((b0 + b1)/2) ((a0 + a1)/2) - -{-# DEPRECATED halfD "use darken 0.5 instead" #-} --- | An intermediate between the black and the given color -halfD :: Color -> Color -halfD = intermediate black - -{-# DEPRECATED halfB "use lighten 0.5 instead" #-} --- | An intermediate between the white and the given color -halfB :: Color -> Color -halfB = intermediate white - -white :: Color -white = Color 1.0 1.0 1.0 1.0 - -black :: Color -black = Color 0.0 0.0 0.0 1.0 - -red :: Color -red = Color 1.0 0.0 0.0 1.0 - -green :: Color -green = Color 0.0 1.0 0.0 1.0 - -blue :: Color -blue = Color 0.0 0.0 1.0 1.0 - -yellow :: Color -yellow = Color 1.0 1.0 0.0 1.0 - -cyan :: Color -cyan = Color 0.0 1.0 1.0 1.0 - -magenta :: Color -magenta = Color 1.0 0.0 1.0 1.0
− Graphics/FreeGame/Data/Font.hs
@@ -1,158 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-} ------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Data.Font --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : provisional --- Portability : non-portable --- --- Rendering characters ----------------------------------------------------------------------------- -module Graphics.FreeGame.Data.Font - ( Font - , loadFont - , Metrics(..) - , Graphics.FreeGame.Data.Font.metrics - , fontBoundingBox - , charToBitmap - , RenderedChar(..) - , text - , renderCharacters - ) where - -import Control.Applicative -import Control.Monad.IO.Class -import Data.Array.Repa as R -import Data.Array.Repa.Eval -import Data.Vect -import Data.IORef -import qualified Data.Map as M -import Data.Word -import Graphics.FreeGame.Base -import Graphics.FreeGame.Types -import Graphics.FreeGame.Data.Bitmap -import Graphics.FreeGame.Internal.Finalizer -import Graphics.Rendering.FreeType.Internal -import qualified Graphics.Rendering.FreeType.Internal.GlyphSlot as GS -import qualified Graphics.Rendering.FreeType.Internal.Vector as V -import Graphics.Rendering.FreeType.Internal.Bitmap as B -import Graphics.Rendering.FreeType.Internal.PrimitiveTypes as PT -import Graphics.Rendering.FreeType.Internal.Face as F -import Graphics.Rendering.FreeType.Internal.Library as L -import Graphics.Rendering.FreeType.Internal.BBox as BB -import Foreign.Marshal.Alloc -import Foreign.C.Types -import Foreign.C.String -import Foreign.Storable -import System.IO.Unsafe -import Unsafe.Coerce - --- | Font object -data Font = Font FT_Face Metrics BoundingBox (IORef (M.Map (Float, Char) RenderedChar)) - --- | Create a 'Font' from the given file. -loadFont :: FilePath -> IO Font -loadFont path = alloca $ \p -> do - e <- withCString path $ \str -> ft_New_Face freeType str 0 p - failFreeType e - f <- peek p - b <- peek (bbox f) - asc <- peek (ascender f) - desc <- peek (descender f) - u <- fromIntegral <$> peek (units_per_EM f) - let m = Metrics (fromIntegral asc/u) (fromIntegral desc/u) - box = BoundingBox (Vec2 (fromIntegral (xMin b)/u) (fromIntegral (yMin b)/u)) - (Vec2 (fromIntegral (xMax b)/u) (fromIntegral (yMin b)/u)) - Font f m box <$> newIORef M.empty --- | Get the font's metrics. -metrics :: Font -> Metrics -metrics (Font _ m _ _) = m - -fontBoundingBox :: Font -> BoundingBox -fontBoundingBox (Font _ _ b _) = b - --- | Render a text by the specified 'Font'. -text :: Font -> Float -> String -> Picture -text font siz str = PictureWithFinalizer $ Pictures <$> renderCharacters font siz str - -failFreeType :: Monad m => CInt -> m () -failFreeType 0 = return () -failFreeType e = fail $ "FreeType Error:" Prelude.++ show e - -freeType :: FT_Library -freeType = unsafePerformIO $ alloca $ \p -> do - failFreeType =<< ft_Init_FreeType p - peek p - -data RenderedChar = RenderedChar - { charBitmap :: Bitmap - , charOffset :: Vec2 - , charAdvance :: Float - } - -data Metrics = Metrics - { metricsAscent :: Float - , metricsDescent :: Float - } - --- | The resolution used to render fonts. -resolutionDPI :: Int -resolutionDPI = 300 - -charToBitmap :: Font -> Float -> Char -> FinalizerT IO RenderedChar -charToBitmap (Font face _ _ refCache) pixel ch = do - cache <- liftIO $ readIORef refCache - case M.lookup (siz, ch) cache of - Nothing -> do - d <- liftIO render - liftIO $ writeIORef refCache $ M.insert (siz, ch) d cache - finalizer $ modifyIORef refCache $ M.delete (siz, ch) - return d - Just d -> return d - where - siz = pixel * 72 / fromIntegral resolutionDPI - render = do - let dpi = fromIntegral resolutionDPI - - failFreeType =<< ft_Set_Char_Size face 0 (floor $ siz * 64) dpi dpi - - ix <- ft_Get_Char_Index face (fromIntegral $ fromEnum ch) - failFreeType =<< ft_Load_Glyph face ix ft_LOAD_DEFAULT - - slot <- peek $ glyph face - failFreeType =<< ft_Render_Glyph slot ft_RENDER_MODE_NORMAL - - bmp <- peek $ GS.bitmap slot - left <- fmap fromIntegral $ peek $ GS.bitmap_left slot - top <- fmap fromIntegral $ peek $ GS.bitmap_top slot - - let h = fromIntegral $ B.rows bmp - w = fromIntegral $ B.width bmp - - mv <- newMVec (w * h) - - fillChunkedIOP (w * h) (unsafeWriteMVec mv) $ const $ return - $ fmap unsafeCoerce . peekElemOff (buffer bmp) - - adv <- peek $ GS.advance slot - - ar :: R.Array U DIM2 Word8 <- unsafeFreezeMVec (Z:.h:.w) mv - - let pix (crd:.0) = R.index ar crd - pix (_:._) = 255 - - result <- computeP (fromFunction (Z:.h:.w:.4) pix) >>= makeStableBitmap - - return $ RenderedChar result (Vec2 left (-top)) (fromIntegral (V.x adv) / 64) - -renderCharacters :: Font -> Float -> String -> FinalizerT IO [Picture] -renderCharacters font pixel str = render str 0 where - render [] _ = return [] - render (c:cs) pen = do - RenderedChar b (Vec2 x y) adv <- charToBitmap font pixel c - let (w,h) = bitmapSize b - offset = Vec2 (pen + x + fromIntegral w / 2) (y + fromIntegral h / 2) - (Translate offset (Bitmap b):) <$> render cs (pen + adv)
− Graphics/FreeGame/Input.hs
@@ -1,87 +0,0 @@------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Input --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : experimental --- Portability : non-portable --- ----------------------------------------------------------------------------- -module Graphics.FreeGame.Input ( - Button(..) - -- * Deprecated - , Key - , MouseState(..) - ) where - -import Data.Vect - --- | Buttons. -data Button - = KeyChar Char - | KeySpace - | KeyEsc - | KeyF1 - | KeyF2 - | KeyF3 - | KeyF4 - | KeyF5 - | KeyF6 - | KeyF7 - | KeyF8 - | KeyF9 - | KeyF10 - | KeyF11 - | KeyF12 - | KeyLeftShift - | KeyRightShift - | KeyLeftControl - | KeyRightControl - | KeyUp - | KeyDown - | KeyLeft - | KeyRight - | KeyTab - | KeyEnter - | KeyBackspace - | KeyInsert - | KeyDelete - | KeyPageUp - | KeyPageDown - | KeyHome - | KeyEnd - | KeyPad0 - | KeyPad1 - | KeyPad2 - | KeyPad3 - | KeyPad4 - | KeyPad5 - | KeyPad6 - | KeyPad7 - | KeyPad8 - | KeyPad9 - | KeyPadDivide - | KeyPadMultiply - | KeyPadSubtract - | KeyPadAdd - | KeyPadDecimal - | KeyPadEqual - | KeyPadEnter - | MouseLeft - | MouseRight - | MouseMiddle - deriving (Show, Eq, Ord) - -data MouseState = MouseState - { mousePosition :: Vec2 - , leftButton :: Bool - , middleButton :: Bool - , rightButton :: Bool - , mouseWheel :: Int - } deriving Show - -{-# DEPRECATED Key "use Button instead" #-} --- Deprecated synonym for 'Button'. -type Key = Button
− Graphics/FreeGame/Internal/Finalizer.hs
@@ -1,34 +0,0 @@-{-# LANGUAGE Rank2Types #-} -module Graphics.FreeGame.Internal.Finalizer (FinalizerT(..), finalizer, runFinalizerT) where - -import Control.Monad.IO.Class -import Control.Applicative - --- | An action with explicit releasing action. -newtype FinalizerT m a = FinalizerT - { unFinalizerT :: forall r. (a -> m r) -> (IO () -> r -> m r) -> m r } - --- | Add a finalizer. -finalizer :: Monad m => IO () -> FinalizerT m () -finalizer m = FinalizerT $ \p f -> p () >>= f m - -instance Functor (FinalizerT m) where - fmap f (FinalizerT g) = FinalizerT $ \p -> g (p . f) - -instance Applicative (FinalizerT m) where - pure a = FinalizerT $ \p _ -> p a - FinalizerT ff <*> FinalizerT fa = FinalizerT $ \p f -> ff (\a -> fa (\b -> p (a b)) f) f - -instance Monad (FinalizerT m) where - return a = FinalizerT $ \p _ -> p a - FinalizerT rf >>= k = FinalizerT $ \p f -> rf (\x -> unFinalizerT (k x) p f) f - -instance MonadIO m => MonadIO (FinalizerT m) where - liftIO m = FinalizerT $ \r _ -> liftIO m >>= r - --- | Run the action and run all associated finalizers. -runFinalizerT :: MonadIO m => FinalizerT m a -> m a -runFinalizerT (FinalizerT z) = do - (fin, a) <- z (\a -> return (return (), a)) (\m (fs, r) -> return (m >> fs, r)) - liftIO fin - return a
− Graphics/FreeGame/Simple.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE RankNTypes, FlexibleContexts #-} ------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Simple --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : provisional --- Portability : non-portable --- --- The essentials of using free-game ----------------------------------------------------------------------------- -module Graphics.FreeGame.Simple ( - -- * Basic type - Game - ,GameAction - - -- * Run the game - ,GameParam(..) - ,defaultGameParam - ,runSimple - ,runSimple' - - -- * In the Game monad - ,drawPicture - ,getButtonState - ,getMousePosition - ,getMouseWheel - ,embedIO - ,quitGame - ,tick - ,untick - ,untickInfinite - ,untickGame - - -- * About Picture - ,Picture(..) - ,Bitmap - ,loadBitmapFromFile - ,loadBitmaps - ,loadBitmapsWith - ,Vec2(..) - - -- * Drawing texts - ,Font - ,loadFont - ,text - - -- * Utilities - ,randomness - ,degrees - - -- * Deprecated - ,askInput - ,getMouseState - - -- * Reexports - ,module Graphics.FreeGame.Input - ,module Graphics.FreeGame.Data.Color - ) - -where - -import Graphics.FreeGame -import Graphics.FreeGame.Data.Color -import Graphics.FreeGame.Input -import Control.Monad -import Control.Monad.Free - --- | Run a 'Game' by the given initial state and updating function. -runSimple :: GameParam - -> world -- ^ An initial world - -> (world -> Game world) -- ^ A computation yielding new world - -> IO () -runSimple param initial m = void $ runGame param $ looping initial where - looping world = do - world' <- m world - tick - looping world' - --- | In most cases there's no unwrapping (Game a -> GameAction (Game a)). --- | The use of ('>>=') is more efficient than 'runGame' in such situation. -runSimple' :: GameParam - -> world -- ^ An initial world - -> (world -> forall m. MonadFree GameAction m => m world) -- ^ A computation yielding new world - -> IO () -runSimple' param initial m = void $ runGame' param $ looping initial where - looping world = do - world' <- m world - tick - looping world'
− Graphics/FreeGame/Types.hs
@@ -1,17 +0,0 @@------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Types --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : provisional --- Portability : non-portable --- ----------------------------------------------------------------------------- -module Graphics.FreeGame.Types (BoundingBox(..)) where -import Data.Vect -data BoundingBox = BoundingBox - { topLeft :: Vec2 - , bottomRight :: Vec2 - } deriving (Show)
− Graphics/FreeGame/Util.hs
@@ -1,107 +0,0 @@-{-# LANGUAGE FlexibleContexts, TemplateHaskell #-} ------------------------------------------------------------------------------ --- | --- Module : Graphics.FreeGame.Util --- Copyright : (C) 2013 Fumiaki Kinoshita --- License : BSD-style (see the file LICENSE) --- --- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> --- Stability : experimental --- Portability : non-portable --- ----------------------------------------------------------------------------- - -module Graphics.FreeGame.Util ( - untick, - untickGame, - untickInfinite, - randomness, - degrees, - radians, - loadPictureFromFile, - loadBitmaps, - loadBitmapsWith) where -import Control.Monad -import Control.Monad.Free -import Control.Applicative -import Data.Char -import Graphics.FreeGame.Base -import Graphics.FreeGame.Data.Bitmap -import System.Random -import Language.Haskell.TH -import System.Directory -import System.FilePath -import System.IO.Unsafe -import Data.Void - -untickGame :: MonadFree GameAction m => Free GameAction a -> m (Free GameAction a) -untickGame (Pure a) = return (Pure a) -untickGame (Free (Tick cont)) = return cont -untickGame (Free fm) = wrap $ fmap untickGame fm -{-# DEPRECATED untickGame "use untick or untickInfinite instead" #-} - --- | Run a 'Game' as one frame. -untick :: MonadFree GameAction m => Free GameAction a -> m (Either (Free GameAction a) a) -untick (Pure a) = return (Right a) -untick (Free (Tick cont)) = return (Left cont) -untick (Free f) = wrap $ fmap untick f - --- | An infinite version of 'untick'. -untickInfinite :: MonadFree GameAction m => Free GameAction Void -> m (Free GameAction Void) -untickInfinite = liftM (either id absurd) . untick - --- | Get a given range of value. -randomness :: (Random r, MonadFree GameAction m) => (r, r) -> m r -randomness r = embedIO $ randomRIO r - --- | Convert radians to degrees. -degrees :: Float -> Float -{-# INLINE degrees #-} -degrees x = x / pi * 180 - --- | Convert degrees to radians. -radians :: Float -> Float -{-# INLINE radians #-} -radians x = x / 180 * pi - --- | Create a 'Picture' from the given file. -loadPictureFromFile :: MonadFree GameAction m => FilePath -> m Picture -loadPictureFromFile = embedIO . fmap Bitmap . loadBitmapFromFile - --- | Load and define all pictures in the specified directory. -loadBitmapsWith :: Name -> FilePath -> Q [Dec] -loadBitmapsWith getFullPath path = do - loc <- (</>path) <$> takeDirectory <$> loc_filename <$> location - paths <- runIO $ getFileList loc - - sequence $ do - p <- paths - let name = pathToName p - [ return $ SigD (mkName name) (ConT ''Bitmap) - , funD (mkName name) [clause [] (normalB $ load name $ loc </> p) []] - ] - where - load name fp = do - runIO $ putStrLn $ "Defined: " ++ fp ++ " as `" ++ name ++ "'" - appE (varE 'unsafePerformIO) $ uInfixE (appE (varE getFullPath) (litE $ StringL fp)) - (varE '(>>=)) - (varE 'loadBitmapFromFile) - --- | use with getDataFileName -loadBitmaps :: FilePath -> Q [Dec] -loadBitmaps = loadBitmapsWith 'canonicalizePath - -getFileList :: FilePath -> IO [FilePath] -getFileList path = do - allContents <- filter notHidden `fmap` getDirectoryContents path - files <- filterM (doesFileExist . (path</>)) allContents - dirs <- filterM (doesDirectoryExist . (path</>)) allContents - fmap ((files++).concat) $ forM dirs $ \i -> map (i</>) `fmap` getFileList (path</>i) - where - notHidden ('.':_) = False - notHidden _ = True - -pathToName :: FilePath -> String -pathToName = ('_':) . map p where - p c | isAlphaNum c = c - | otherwise = '_'
+ Graphics/UI/FreeGame.hs view
@@ -0,0 +1,62 @@+{-| +Module : Graphics.UI.FreeGame +Copyright : (C) 2013 Fumiaki Kinoshita +License : BSD-style (see the file LICENSE) +Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> + +free-game is a library that abstracts and purifies GUI applications with simple interfaces. +-} +module Graphics.UI.FreeGame + ( -- * Examples + -- $example + -- * Main + Game, + runGame, + def, + -- * Reexports + module Graphics.UI.FreeGame.Base, + module Graphics.UI.FreeGame.Data.Bitmap, + module Graphics.UI.FreeGame.Data.Font, + module Graphics.UI.FreeGame.Data.Color, + module Graphics.UI.FreeGame.GUI, + module Graphics.UI.FreeGame.Util, + module Graphics.UI.FreeGame.Types + +) where + +import Graphics.UI.FreeGame.Base +import Graphics.UI.FreeGame.GUI (GUI, GUIParam(..), Picture) +import Graphics.UI.FreeGame.Util +import Graphics.UI.FreeGame.Types +import Graphics.UI.FreeGame.Data.Bitmap +import Graphics.UI.FreeGame.Data.Font +import Graphics.UI.FreeGame.Data.Color +import qualified Graphics.UI.FreeGame.GUI.GLFW as GLFW +import Control.Monad.Free.Church +import Data.Default + +-- | 'Game' is a "free" monad which describes GUIs. +-- This monad is an instance of 'Picture2D' so you can create it using 'fromBitmap' and can be transformed with 'translate', 'scale', 'rotate', 'colored'. +-- +-- It is also an instance of 'Keyboard' and 'Mouse'. +-- +-- You have to call 'tick' at the end of the current frame. +-- +-- The only way to embody 'Game' as real thing is to apply 'runGame'. +type Game = F GUI + +-- | Run a 'Game'. +runGame :: GUIParam -> Game a -> IO (Maybe a) +runGame = GLFW.runGame + +{- $example + +> import Control.Monad +> import Graphics.UI.FreeGame +> main = runGame def $ forever tick + +shows a window and does nothing. + +for more examples, see <https://github.com/fumieval/free-game/tree/master/examples>. + +-}
+ Graphics/UI/FreeGame/Base.hs view
@@ -0,0 +1,282 @@+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Graphics.UI.FreeGame.Base+-- Copyright : (C) 2012-2013 Fumiaki Kinoshita+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com>+-- Stability : experimental+-- Portability : non-portable+--+-- Abstract structures for descripting user interfaces+----------------------------------------------------------------------------++module Graphics.UI.FreeGame.Base (+ -- * Types+ UI(..)++ -- * Basic operations+ ,tick+ ,bracket+ ,quit+ ,embedIO+ ,liftUI+ ,_LiftUI+ -- * Classes+ ,Picture2D(..)+ ,Keyboard(..)+ ,Mouse(..)+ ,FromFinalizer(..)+ ,SpecialKey(..)+) where++import Control.Applicative+import Control.Applicative.Free as Ap+import Control.Monad.IO.Class+import Data.Monoid+import Data.Void+import Graphics.UI.FreeGame.Data.Bitmap+import Graphics.UI.FreeGame.Data.Color+import Graphics.UI.FreeGame.Internal.Finalizer+import Graphics.UI.FreeGame.Internal.Raindrop+import Linear hiding (rotate)++import Control.Monad.Free.Class+import Control.Monad.Free.Church+import qualified Control.Monad.Free as Free++import Control.Monad.Trans+import Control.Monad.Trans.Reader+import Control.Monad.Trans.Cont+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.List+import Control.Monad.Trans.Error+import Control.Monad.Trans.Identity+import qualified Control.Monad.State.Lazy as Lazy+import qualified Control.Monad.State.Strict as Strict+import qualified Control.Monad.Writer.Lazy as Lazy+import qualified Control.Monad.Writer.Strict as Strict+import qualified Control.Monad.Trans.RWS.Strict as Strict+import qualified Control.Monad.Trans.RWS.Lazy as Lazy++infixr 5 `translate`+infixr 5 `rotate`+infixr 5 `scale`+infixr 5 `colored`++instance (Functor m) => MonadIO (F (UI m)) where+ liftIO = embedIO++instance (Functor m) => MonadIO (Free.Free (UI m)) where+ liftIO = embedIO++-- | A functor enriches given functor with control structure.+data UI m a+ = Tick a+ | EmbedIO (IO a)+ | LiftUI (m a)+ | Bracket (F (UI m) a)+ | Quit+ deriving Functor++-- | Finalize the current frame and refresh the screen.+tick :: MonadFree (UI n) m => m ()+tick = wrap $ Tick (return ())++-- | Run a Game monad in a Game monad. resources (e.g. pictures) will be released when inner computation is done.+bracket :: MonadFree (UI n) m => F (UI n) a -> m a+bracket = wrap . Bracket . fmap return++-- | Break the entire computation.+quit :: MonadFree (UI n) m => m Void+quit = wrap Quit++-- | Lift 'UI''s base functor.+liftUI :: (Functor n, MonadFree (UI n) m) => n a -> m a+liftUI = wrap . LiftUI . fmap return++-- | Lift an arbitrary 'IO' action.+embedIO :: (MonadFree (UI n) m) => IO a -> m a+embedIO = wrap . EmbedIO . fmap return++-- | @'_LiftUI' :: Traversal' ('UI' m a) (m a)@+_LiftUI :: Applicative f => (m a -> f (m a)) -> UI m a -> f (UI m a)+_LiftUI f (LiftUI o) = fmap LiftUI (f o)+_LiftUI _ x = pure x++hoistFreeR :: (Functor f, MonadFree g m) => (f (m a) -> g (m a)) -> Free.Free f a -> m a+hoistFreeR _ (Free.Pure a) = return a+hoistFreeR t (Free.Free f) = wrap . t $ fmap (hoistFreeR t) f+{-# INLINE hoistFreeR #-}++hoistFR :: MonadFree g m => (f (m a) -> g (m a)) -> F f a -> m a+hoistFR t (F m) = m return (wrap . t)+{-# INLINE hoistFR #-}++-- | The class of types that can be regarded as a kind of picture.+class Picture2D p where+ -- | Construct a 'Picture2D' from a 'Bitmap'.+ fromBitmap :: Bitmap -> p ()+ -- | Counterclockwise, degrees+ rotate :: Float -> p a -> p a+ scale :: V2 Float -> p a -> p a+ translate :: V2 Float -> p a -> p a+ colored :: Color -> p a -> p a++-- | The class of types that can handle inputs of the keyboard.+class Keyboard t where+ keyChar :: Char -> t Bool+ keySpecial :: SpecialKey -> t Bool++-- | The class of types that can handle inputs of the mouse.+class Mouse t where+ mousePosition :: t (V2 Float)+ mouseWheel :: t Int+ mouseButtonL :: t Bool+ mouseButtonM :: t Bool+ mouseButtonR :: t Bool++class FromFinalizer m where+ fromFinalizer :: FinalizerT IO a -> m a++data SpecialKey = KeySpace+ | KeyEsc+ | KeyLeftShift+ | KeyRightShift+ | KeyLeftControl+ | KeyRightControl+ | KeyUp+ | KeyDown+ | KeyLeft+ | KeyRight+ | KeyTab+ | KeyEnter+ | KeyBackspace+ | KeyInsert+ | KeyDelete+ | KeyPageUp+ | KeyPageDown+ | KeyHome+ | KeyEnd+ | KeyF1+ | KeyF2+ | KeyF3+ | KeyF4+ | KeyF5+ | KeyF6+ | KeyF7+ | KeyF8+ | KeyF9+ | KeyF10+ | KeyF11+ | KeyF12+ | KeyPad0+ | KeyPad1+ | KeyPad2+ | KeyPad3+ | KeyPad4+ | KeyPad5+ | KeyPad6+ | KeyPad7+ | KeyPad8+ | KeyPad9+ | KeyPadDivide+ | KeyPadMultiply+ | KeyPadSubtract+ | KeyPadAdd+ | KeyPadDecimal+ | KeyPadEqual+ | KeyPadEnter+ deriving (Show, Eq, Ord, Enum)++#define _COMMA_ ,++#define MK_PICTURE_2D(cxt, ty, l, t) instance (Picture2D m cxt) => Picture2D (ty) where { \+ fromBitmap = (l) . fromBitmap; \+ rotate = (t) . rotate; \+ translate = (t) . translate; \+ scale = (t) . scale; \+ colored = (t) . colored }++#define MK_KEYBOARD(cxt, ty, l) instance (Keyboard m cxt) => Keyboard (ty) where { \+ keyChar = (l) . keyChar; \+ keySpecial = (l) . keySpecial }++#define MK_MOUSE(cxt, ty, l) instance (Mouse m cxt) => Mouse (ty) where { \+ mousePosition = (l) mousePosition; \+ mouseWheel = (l) mouseWheel; \+ mouseButtonL = (l) mouseButtonL; \+ mouseButtonR = (l) mouseButtonR; \+ mouseButtonM = (l) mouseButtonM }++#define MK_FROM_FINALIZER(cxt, ty, l) instance (FromFinalizer m cxt) => FromFinalizer (ty) where { \+ fromFinalizer = (l) . fromFinalizer }++MK_PICTURE_2D(_COMMA_ Functor m, F m, liftF, hoistFR)+MK_PICTURE_2D( , UI m, LiftUI, over _LiftUI)+MK_PICTURE_2D(_COMMA_ Functor m, Free.Free m, Free.liftF, hoistFreeR)+MK_PICTURE_2D(_COMMA_ Monad m, ReaderT r m, lift, mapReaderT)+MK_PICTURE_2D(_COMMA_ Monad m, Lazy.StateT s m, lift, Lazy.mapStateT)+MK_PICTURE_2D(_COMMA_ Monad m, Strict.StateT s m, lift, Strict.mapStateT)+MK_PICTURE_2D(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.WriterT w m, lift, Lazy.mapWriterT)+MK_PICTURE_2D(_COMMA_ Monad m _COMMA_ Monoid w, Strict.WriterT w m, lift, Strict.mapWriterT)+MK_PICTURE_2D(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.RWST r w s m, lift, Lazy.mapRWST)+MK_PICTURE_2D(_COMMA_ Monad m _COMMA_ Monoid w, Strict.RWST r w s m, lift, Strict.mapRWST)+MK_PICTURE_2D(_COMMA_ Monad m, IdentityT m, lift, mapIdentityT)+MK_PICTURE_2D(_COMMA_ Monad m, MaybeT m, lift, mapMaybeT)+MK_PICTURE_2D(_COMMA_ Monad m, ListT m, lift, mapListT)+MK_PICTURE_2D(_COMMA_ Monad m _COMMA_ Error e, ErrorT e m, lift, mapErrorT)+MK_PICTURE_2D(_COMMA_ Monad m, ContT r m, lift, mapContT)++MK_KEYBOARD(, Ap m, liftAp)+MK_KEYBOARD(, UI m, LiftUI)+MK_KEYBOARD(_COMMA_ Functor m, F m, liftF)+MK_KEYBOARD(_COMMA_ Functor m, Free.Free m, Free.liftF)+MK_KEYBOARD(_COMMA_ Monad m, ReaderT s m, lift)+MK_KEYBOARD(_COMMA_ Monad m, Lazy.StateT s m, lift)+MK_KEYBOARD(_COMMA_ Monad m, Strict.StateT s m, lift)+MK_KEYBOARD(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.WriterT w m, lift)+MK_KEYBOARD(_COMMA_ Monad m _COMMA_ Monoid w, Strict.WriterT w m, lift)+MK_KEYBOARD(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.RWST r w s m, lift)+MK_KEYBOARD(_COMMA_ Monad m _COMMA_ Monoid w, Strict.RWST r w s m, lift)+MK_KEYBOARD(_COMMA_ Monad m, IdentityT m, lift)+MK_KEYBOARD(_COMMA_ Monad m, MaybeT m, lift)+MK_KEYBOARD(_COMMA_ Monad m, ListT m, lift)+MK_KEYBOARD(_COMMA_ Monad m _COMMA_ Error e, ErrorT e m, lift)+MK_KEYBOARD(_COMMA_ Monad m, ContT r m, lift)++MK_MOUSE(, Ap m, liftAp)+MK_MOUSE(, UI m, LiftUI)+MK_MOUSE(_COMMA_ Functor m, F m, liftF)+MK_MOUSE(_COMMA_ Functor m, Free.Free m, Free.liftF)+MK_MOUSE(_COMMA_ Monad m, ReaderT r m, lift)+MK_MOUSE(_COMMA_ Monad m, Lazy.StateT s m, lift)+MK_MOUSE(_COMMA_ Monad m, Strict.StateT s m, lift)+MK_MOUSE(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.WriterT w m, lift)+MK_MOUSE(_COMMA_ Monad m _COMMA_ Monoid w, Strict.WriterT w m, lift)+MK_MOUSE(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.RWST r w s m, lift)+MK_MOUSE(_COMMA_ Monad m _COMMA_ Monoid w, Strict.RWST r w s m, lift)+MK_MOUSE(_COMMA_ Monad m, IdentityT m, lift)+MK_MOUSE(_COMMA_ Monad m, MaybeT m, lift)+MK_MOUSE(_COMMA_ Monad m, ListT m, lift)+MK_MOUSE(_COMMA_ Monad m _COMMA_ Error e, ErrorT e m, lift)+MK_MOUSE(_COMMA_ Monad m, ContT r m, lift)++MK_FROM_FINALIZER(, UI m, LiftUI)+MK_FROM_FINALIZER(_COMMA_ Functor m, F m, liftF)+MK_FROM_FINALIZER(_COMMA_ Functor m, Free.Free m, Free.liftF)+MK_FROM_FINALIZER(_COMMA_ Monad m, Lazy.StateT s m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m, Strict.StateT s m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.WriterT w m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m _COMMA_ Monoid w, Strict.WriterT w m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m _COMMA_ Monoid w, Lazy.RWST r w s m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m _COMMA_ Monoid w, Strict.RWST r w s m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m, IdentityT m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m, MaybeT m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m, ListT m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m _COMMA_ Error e, ErrorT e m, lift)+MK_FROM_FINALIZER(_COMMA_ Monad m, ContT r m, lift)
+ Graphics/UI/FreeGame/Data/Bitmap.hs view
@@ -0,0 +1,84 @@+----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.Data.Bitmap +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : experimental +-- Portability : non-portable +-- +-- Manipulating bitmaps +---------------------------------------------------------------------------- + +module Graphics.UI.FreeGame.Data.Bitmap ( + -- * Basic types and functions + Bitmap(..) + , _BitmapArray + , _BitmapHash + ,bitmapSize + + -- * Loading from a file + ,loadBitmapFromFile + + -- * Constructing bitmaps + ,toBitmap + ,toStableBitmap + ,makeStableBitmap + + -- * Bitmap operations + ,onBitmapWithHashable + ,cropBitmap + + ) where + +import Control.Applicative +import Codec.Picture.Repa +import Data.Array.Repa as R +import qualified Data.Array.Repa.Repr.ForeignPtr as RF +import Data.Word +import System.Random +import Data.Hashable + +-- | Concrete bitmap data. Internal representation is stored as y * x * RGBA. +data Bitmap = BitmapData (R.Array RF.F DIM3 Word8) (Maybe Int) -- ^ This value is used to ensure that two bitmaps are equivalent. + +-- | @'_BitmapArray' :: Lens' 'Bitmap' ('R.Array' 'RF.F' 'DIM3' 'Word8')@ +_BitmapArray :: Functor f => (R.Array RF.F DIM3 Word8 -> f (R.Array RF.F DIM3 Word8)) -> Bitmap -> f Bitmap +_BitmapArray f (BitmapData a h) = fmap (\a' -> BitmapData a' h) (f a) + +-- | @'_BitmapHash' :: Lens' 'Bitmap' ('Maybe' 'Int')@ +_BitmapHash :: Functor f => (Maybe Int -> f (Maybe Int)) -> Bitmap -> f Bitmap +_BitmapHash f (BitmapData a h) = fmap (\h' -> BitmapData a h') (f h) + +-- | Create unstable 'Bitmap' from the given array. +toBitmap :: R.Array RF.F DIM3 Word8 -> Bitmap +toBitmap ar = BitmapData ar Nothing + +-- | Create stable 'Bitmap' from the given array and compute the hash. +toStableBitmap :: R.Array RF.F DIM3 Word8 -> Bitmap +toStableBitmap ar = BitmapData ar $ Just $ head $ foldAllP combine 0 $ R.map fromIntegral ar where + combine p q = hash (p, q) + +-- | Create stable 'Bitmap' with unique hash from the given array. +makeStableBitmap :: R.Array RF.F DIM3 Word8 -> IO Bitmap +makeStableBitmap ar = BitmapData ar <$> Just <$> randomIO + +-- | Get the size of the 'Bitmap'. +bitmapSize :: Bitmap -> (Int, Int) +bitmapSize (BitmapData a _) = let (Z :. h :. w :. _) = R.extent a in (w, h) + +-- | Create a 'Bitmap' from the given file. +loadBitmapFromFile :: FilePath -> IO Bitmap +loadBitmapFromFile path = readImageRGBA path >>= either fail return >>= makeStableBitmap . imgData + +-- | Convert the 'Bitmap' uniformalized by the 'Hashable' value by the given function. +onBitmapWithHashable :: Hashable h => h -> (R.Array RF.F DIM3 Word8 -> R.Array RF.F DIM3 Word8) -> Bitmap -> Bitmap +onBitmapWithHashable v f (BitmapData ar h) = BitmapData (f ar) (hash <$> (,) v <$> h) + +-- | Extract a 'Bitmap' from the specified range. +cropBitmap :: Bitmap -- ^original bitmap + -> (Int, Int) -- ^width and height + -> (Int, Int) -- ^x and y + -> Bitmap -- ^result +cropBitmap bmp (w, h) (x, y) = onBitmapWithHashable (w,h,x,y) (head . computeP . extract (Z :. y :. x :. 0) (Z :. h :. w :. 4)) bmp
+ Graphics/UI/FreeGame/Data/Color.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE Rank2Types #-} +----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.Data.Color +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : provisional +-- Portability : non-portable +-- +-- Colors and its operations +---------------------------------------------------------------------------- +module Graphics.UI.FreeGame.Data.Color ( + -- * The type + Color(..) + -- * Color operations + , blend + -- * Lenses + , _Red, _Green, _Blue, _Alpha, _8Bit + -- * Basic colors + , white, black, red, green, blue, yellow, cyan, magenta + ) where + +import Data.String +import Data.Char +import Data.Profunctor +import Data.Word + +-- | A color that has red, green, blue, alpha as its component. +data Color = Color Float Float Float Float deriving (Show, Eq, Ord) + +-- | @'_8Bit' :: Iso' 'Float' 'Word8'@ +_8Bit :: forall p f. (Profunctor p, Functor f) => p Word8 (f Word8) -> p Float (f Float) +_8Bit = dimap (floor.(*255)) (fmap ((/255) . fromIntegral)) + +-- | @'_Red' :: Lens' 'Color' 'Float'@ +_Red :: Functor f => (Float -> f Float) -> Color -> f Color +_Red f (Color r g b a) = fmap (\r' -> Color r' g b a) (f r) + +-- | @'_Green' :: Lens' 'Color' 'Float'@ +_Green :: Functor f => (Float -> f Float) -> Color -> f Color +_Green f (Color r g b a) = fmap (\g' -> Color r g' b a) (f g) + +-- | @'_Blue' :: Lens' 'Color' 'Float'@ +_Blue :: Functor f => (Float -> f Float) -> Color -> f Color +_Blue f (Color r g b a) = fmap (\b' -> Color r g b' a) (f b) + +-- | @'_Alpha' :: Lens' 'Color' 'Float'@ +_Alpha :: Functor f => (Float -> f Float) -> Color -> f Color +_Alpha f (Color r g b a) = fmap (\a' -> Color r g b a') (f a) + +hf :: Char -> Float +hf x = fromIntegral (digitToInt x) / 15 + +hf' :: Char -> Char -> Float +hf' x y = fromIntegral (digitToInt x * 16 + digitToInt y) / 255 + +instance IsString Color where + fromString xs@[r,g,b,a] | all isHexDigit xs = Color (hf r) (hf g) (hf b) (hf a) + fromString xs@[r,g,b] | all isHexDigit xs = Color (hf r) (hf g) (hf b) 1 + fromString xs@[r1,r0,g1,g0,b1,b0,a1,a0] | all isHexDigit xs = Color (hf' r1 r0) (hf' g1 g0) (hf' b1 b0) (hf' a1 a0) + fromString xs@[r1,r0,g1,g0,b1,b0] | all isHexDigit xs = Color (hf' r1 r0) (hf' g1 g0) (hf' b1 b0) 1 + fromString x = error $ "Invalid color representation: " ++ x + +-- | Blend two colors. +blend :: Float -> Color -> Color -> Color +blend t (Color r0 g0 b0 a0) (Color r1 g1 b1 a1) = Color + (r0 * (1 - t) + r1 * t) + (g0 * (1 - t) + g1 * t) + (b0 * (1 - t) + b1 * t) + (a0 * (1 - t) + a1 * t) + +white :: Color +white = Color 1.0 1.0 1.0 1.0 + +black :: Color +black = Color 0.0 0.0 0.0 1.0 + +red :: Color +red = Color 1.0 0.0 0.0 1.0 + +green :: Color +green = Color 0.0 1.0 0.0 1.0 + +blue :: Color +blue = Color 0.0 0.0 1.0 1.0 + +yellow :: Color +yellow = Color 1.0 1.0 0.0 1.0 + +cyan :: Color +cyan = Color 0.0 1.0 1.0 1.0 + +magenta :: Color +magenta = Color 1.0 0.0 1.0 1.0
+ Graphics/UI/FreeGame/Data/Font.hs view
@@ -0,0 +1,159 @@+----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.Data.Font +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : provisional +-- Portability : non-portable +-- +-- Rendering characters +---------------------------------------------------------------------------- +module Graphics.UI.FreeGame.Data.Font + ( Font + , loadFont + , Metrics(..) + , Graphics.UI.FreeGame.Data.Font.metrics + , fontBoundingBox + , charToBitmap + , RenderedChar(..) + , text + , renderCharacters + ) where + +import Control.Applicative +import Control.Monad +import Control.Monad.IO.Class +import Data.IORef +import Data.Array.Repa as R +import Data.Array.Repa.Eval +import qualified Data.Map as M +import Data.Word +import Linear +import Graphics.UI.FreeGame.Base +import Graphics.UI.FreeGame.Types +import Graphics.UI.FreeGame.Data.Bitmap +import Graphics.UI.FreeGame.Internal.Finalizer +import Graphics.Rendering.FreeType.Internal +import qualified Graphics.Rendering.FreeType.Internal.GlyphSlot as GS +import qualified Graphics.Rendering.FreeType.Internal.Vector as V +import Graphics.Rendering.FreeType.Internal.Bitmap as B +import Graphics.Rendering.FreeType.Internal.PrimitiveTypes as PT +import Graphics.Rendering.FreeType.Internal.Face as F +import Graphics.Rendering.FreeType.Internal.Library as L +import Graphics.Rendering.FreeType.Internal.BBox as BB +import Foreign.Marshal.Alloc +import Foreign.C.Types +import Foreign.C.String +import Foreign.Storable +import System.IO.Unsafe +import Unsafe.Coerce + +-- | Font object +data Font = Font FT_Face Metrics (BoundingBox Float) (IORef (M.Map (Float, Char) RenderedChar)) + +-- | Create a 'Font' from the given file. +loadFont :: FilePath -> IO Font +loadFont path = alloca $ \p -> do + e <- withCString path $ \str -> ft_New_Face freeType str 0 p + failFreeType e + f <- peek p + b <- peek (bbox f) + asc <- peek (ascender f) + desc <- peek (descender f) + u <- fromIntegral <$> peek (units_per_EM f) + let m = Metrics (fromIntegral asc/u) (fromIntegral desc/u) + box = BoundingBox (fromIntegral (xMin b)/u) (fromIntegral (yMin b)/u) + (fromIntegral (xMax b)/u) (fromIntegral (yMax b)/u) + Font f m box <$> newIORef M.empty + +-- | Get the font's metrics. +metrics :: Font -> Metrics +metrics (Font _ m _ _) = m + +fontBoundingBox :: Font -> BoundingBox Float +fontBoundingBox (Font _ _ b _) = b + +-- | Render a text by the specified 'Font'. +text :: (Monad p, Picture2D p, FromFinalizer p) => Font -> Float -> String -> p () +text font siz str = join $ fromFinalizer $ fmap sequence_ (renderCharacters font siz str) + +failFreeType :: Monad m => CInt -> m () +failFreeType 0 = return () +failFreeType e = fail $ "FreeType Error:" Prelude.++ show e + +freeType :: FT_Library +freeType = unsafePerformIO $ alloca $ \p -> do + failFreeType =<< ft_Init_FreeType p + peek p + +data RenderedChar = RenderedChar + { charBitmap :: Bitmap + , charOffset :: V2 Float + , charAdvance :: Float + } + +data Metrics = Metrics + { metricsAscent :: Float + , metricsDescent :: Float + } + +-- | The resolution used to render fonts. +resolutionDPI :: Int +resolutionDPI = 300 + +charToBitmap :: Font -> Float -> Char -> FinalizerT IO RenderedChar +charToBitmap (Font face _ _ refCache) pixel ch = do + cache <- liftIO $ readIORef refCache + case M.lookup (siz, ch) cache of + Nothing -> do + d <- liftIO render + liftIO $ writeIORef refCache $ M.insert (siz, ch) d cache + finalizer $ modifyIORef refCache $ M.delete (siz, ch) + return d + Just d -> return d + where + siz = pixel * 72 / fromIntegral resolutionDPI + render = do + let dpi = fromIntegral resolutionDPI + + failFreeType =<< ft_Set_Char_Size face 0 (floor $ siz * 64) dpi dpi + + ix <- ft_Get_Char_Index face (fromIntegral $ fromEnum ch) + failFreeType =<< ft_Load_Glyph face ix ft_LOAD_DEFAULT + + slot <- peek $ glyph face + failFreeType =<< ft_Render_Glyph slot ft_RENDER_MODE_NORMAL + + bmp <- peek $ GS.bitmap slot + left <- fmap fromIntegral $ peek $ GS.bitmap_left slot + top <- fmap fromIntegral $ peek $ GS.bitmap_top slot + + let h = fromIntegral $ B.rows bmp + w = fromIntegral $ B.width bmp + + mv <- newMVec (w * h) + + fillChunkedIOP (w * h) (unsafeWriteMVec mv) $ const $ return + $ fmap unsafeCoerce . peekElemOff (buffer bmp) + + adv <- peek $ GS.advance slot + + ar <- unsafeFreezeMVec (Z:.h:.w) mv :: IO (R.Array U DIM2 Word8) + + let pix (crd:.0) = R.index ar crd + pix (_:._) = 255 + + result <- computeP (fromFunction (Z:.h:.w:.4) pix) >>= makeStableBitmap + + return $ RenderedChar result (V2 left (-top)) (fromIntegral (V.x adv) / 64) + +renderCharacters :: (Monad p, Picture2D p) => Font -> Float -> String -> FinalizerT IO [p ()] +renderCharacters font pixel str = render str 0 where + render [] _ = return [] + render (c:cs) pen = do + RenderedChar b (V2 x y) adv <- charToBitmap font pixel c + let (w,h) = bitmapSize b + offset = V2 (pen + x + fromIntegral w / 2) (y + fromIntegral h / 2) + (translate offset (fromBitmap b):) <$> render cs (pen + adv)
+ Graphics/UI/FreeGame/GUI.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE DeriveFunctor #-} +----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.GUI +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : provisional +-- Portability : non-portable +-- Provides the "free" embodiment. +---------------------------------------------------------------------------- +module Graphics.UI.FreeGame.GUI ( + GUI + , GUIBase(..) + , _Draw + , _Input + , GUIInput(..) + , Picture(..) + , GUIParam(..) +) where + +import Graphics.UI.FreeGame.Base +import Graphics.UI.FreeGame.Data.Color +import Graphics.UI.FreeGame.Data.Bitmap +import Graphics.UI.FreeGame.Internal.Finalizer +import Graphics.UI.FreeGame.Internal.Raindrop +import Control.Applicative +import Control.Applicative.Free (Ap) +import Data.Default +import Linear hiding (rotate) + +-- | A 'Functor' which represents graphical user interfaces. +type GUI = UI GUIBase + +data GUIBase a = Input (Ap GUIInput a) | Draw (Picture a) deriving Functor + +_Draw :: Applicative f => (Picture a -> f (Picture a)) -> GUIBase a -> f (GUIBase a) +_Draw f (Draw o) = fmap Draw (f o) +_Draw _ x = pure x + +_Input :: Applicative f => (Ap GUIInput a -> f (Ap GUIInput a)) -> GUIBase a -> f (GUIBase a) +_Input f (Input o) = fmap Input (f o) +_Input _ x = pure x + +instance Picture2D GUIBase where + fromBitmap = Draw . fromBitmap + rotate = over _Draw . rotate + scale = over _Draw . scale + translate = over _Draw . translate + colored = over _Draw . colored + +instance Keyboard GUIBase where + keyChar = Input . keyChar + keySpecial = Input . keySpecial + +instance Mouse GUIBase where + mousePosition = Input mousePosition + mouseWheel = Input mouseWheel + mouseButtonL = Input mouseButtonL + mouseButtonR = Input mouseButtonR + mouseButtonM = Input mouseButtonR + +instance FromFinalizer GUIBase where + fromFinalizer = Draw . fromFinalizer + +data GUIInput a = + ICharKey Char (Bool -> a) + | ISpecialKey SpecialKey (Bool -> a) + | IMousePosition (V2 Float -> a) + | IMouseWheel (Int -> a) + | IMouseButtonL (Bool -> a) + | IMouseButtonM (Bool -> a) + | IMouseButtonR (Bool -> a) + deriving Functor + +data Picture a + = LiftBitmap Bitmap a + | PictureWithFinalizer (FinalizerT IO a) + | Rotate Float (Picture a) + | Scale (V2 Float) (Picture a) + | Translate (V2 Float) (Picture a) + | Colored Color (Picture a) + deriving Functor + +instance Picture2D Picture where + fromBitmap = flip LiftBitmap () + rotate = Rotate + scale = Scale + translate = Translate + colored = Colored + +instance FromFinalizer Picture where + fromFinalizer = PictureWithFinalizer + +instance Keyboard GUIInput where + keyChar x = ICharKey x id + keySpecial x = ISpecialKey x id + +instance Mouse GUIInput where + mousePosition = IMousePosition id + mouseWheel = IMouseWheel id + mouseButtonL = IMouseButtonL id + mouseButtonR = IMouseButtonR id + mouseButtonM = IMouseButtonM id + +data GUIParam = GUIParam + { _framePerSecond :: Int + , _windowSize :: V2 Int + , _windowTitle :: String + , _windowed :: Bool + , _cursorVisible :: Bool + , _clearColor :: Color + , _windowOrigin :: V2 Float + } deriving Show + +instance Default GUIParam where + def = GUIParam + { _framePerSecond = 60 + , _windowSize = V2 640 480 + , _windowTitle = "free-game" + , _windowed = True + , _cursorVisible = True + , _clearColor = white + , _windowOrigin = V2 0 0 + }
+ Graphics/UI/FreeGame/GUI/GLFW.hs view
@@ -0,0 +1,243 @@+{-# LANGUAGE ImplicitParams #-} +----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.GUI.GLFW +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : experimental +-- Portability : non-portable +-- +---------------------------------------------------------------------------- +module Graphics.UI.FreeGame.GUI.GLFW (runGame) where +import Control.Applicative +import Control.Applicative.Free as Ap +import Control.Monad +import Control.Monad.Free.Church +import Control.Monad.IO.Class +import Data.IORef +import Foreign.ForeignPtr +import Graphics.UI.FreeGame.Base +import Graphics.UI.FreeGame.Data.Bitmap +import Graphics.UI.FreeGame.Data.Color +import Graphics.UI.FreeGame.Internal.Finalizer +import Graphics.UI.FreeGame.GUI +import Graphics.Rendering.OpenGL.Raw.ARB.Compatibility +import qualified Data.Array.Repa.Repr.ForeignPtr as RF +import qualified Graphics.UI.GLFW as GLFW +import Graphics.Rendering.OpenGL.GL.StateVar +import qualified Data.IntMap as IM +import qualified Graphics.Rendering.OpenGL.GL as GL +import System.Mem +import Unsafe.Coerce +import Linear + +runGame :: GUIParam -> F GUI a -> IO (Maybe a) +runGame param m = launch param $ \r s -> runF m (return . Just) (runAction param r s) + +runAction :: GUIParam + -> IORef (IM.IntMap Texture) + -> IORef Int + -> GUI (FinalizerT IO (Maybe a)) -> FinalizerT IO (Maybe a) +runAction param refTextures refFrame _f = case _f of + LiftUI (Draw pic) -> let ?refTextures = refTextures in join $ runPicture pic + EmbedIO m -> join (liftIO m) + Bracket m -> liftIO (runFinalizerT $ runF m (return.Just) (runAction param refTextures refFrame)) >>= maybe (return Nothing) id + LiftUI (Input i) -> join $ liftIO $ runInput i + Quit -> return Nothing + Tick cont -> do + liftIO $ do + GL.matrixMode $= GL.Projection + GLFW.swapBuffers + performGC + t <- GLFW.getTime + n <- readIORef refFrame + GLFW.sleep $ fromIntegral n / fromIntegral (_framePerSecond param) - t + if t > 1 + then GLFW.resetTime >> writeIORef refFrame 0 + else writeIORef refFrame (succ n) + + r <- liftIO $ GLFW.windowIsOpen + if not r then return Nothing else do + liftIO $ do + GL.clear [GL.ColorBuffer] + GL.loadIdentity + GL.scale (gf 1) (-1) 1 + let V2 ox oy = _windowOrigin param + V2 ww wh = _windowSize param + windowL = realToFrac ox + windowR = realToFrac ox + fromIntegral ww + windowT = realToFrac oy + windowB = realToFrac oy + fromIntegral wh + GL.ortho windowL windowR windowT windowB 0 (-100) + GL.matrixMode $= GL.Modelview 0 + cont + +data Texture = Texture GL.TextureObject !Int !Int + +bool :: a -> a -> Bool -> a +bool r _ False = r +bool _ r True = r + +launch :: GUIParam -> (IORef (IM.IntMap Texture) -> IORef Int -> FinalizerT IO (Maybe a)) -> IO (Maybe a) +launch param m = do + GLFW.initialize >>= bool (fail "Failed to initialize") (return ()) + pf <- GLFW.openGLProfile + let V2 ww wh = _windowSize param + (>>=bool (fail "Failed to initialize") (return ())) $ GLFW.openWindow $ GLFW.defaultDisplayOptions { + GLFW.displayOptions_width = ww + ,GLFW.displayOptions_height = wh + ,GLFW.displayOptions_displayMode = if _windowed param then GLFW.Window else GLFW.Fullscreen + ,GLFW.displayOptions_windowIsResizable = False + ,GLFW.displayOptions_openGLProfile = pf + } + + GLFW.setWindowTitle $ _windowTitle param + GL.lineSmooth $= GL.Enabled + GL.blend $= GL.Enabled + GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha) + GL.shadeModel $= GL.Smooth + GL.texture GL.Texture2D $= GL.Enabled + GL.textureFunction $= GL.Combine + + let Color r g b a = _clearColor param in GL.clearColor $= GL.Color4 (gf r) (gf g) (gf b) (gf a) + + res <- runFinalizerT $ join $ m <$> liftIO (newIORef IM.empty) <*> liftIO (newIORef 0) + + GLFW.closeWindow + GLFW.terminate + return res + +installTexture :: Bitmap -> FinalizerT IO Texture +installTexture bmp@(BitmapData ar _) = do + [tex] <- liftIO $ GL.genObjectNames 1 + liftIO $ GL.textureBinding GL.Texture2D GL.$= Just tex + let (width, height) = bitmapSize bmp + let siz = GL.TextureSize2D (gsizei width) (gsizei height) + liftIO $ withForeignPtr (RF.toForeignPtr ar) + $ GL.texImage2D Nothing GL.NoProxy 0 GL.RGBA8 siz 0 + . GL.PixelData GL.RGBA GL.UnsignedInt8888 + finalizer $ GL.deleteObjectNames [tex] + return $ Texture tex width height + +runInput :: Ap GUIInput a -> IO a +runInput (Ap.Pure a) = pure a +runInput (Ap.Ap v af) = (runInput af <*>) $ case v of + ICharKey ch cont -> cont <$> GLFW.keyIsPressed (GLFW.CharKey ch) + ISpecialKey x cont -> cont <$> GLFW.keyIsPressed (mapSpecialKey x) + IMouseButtonL cont -> cont <$> GLFW.mouseButtonIsPressed GLFW.MouseButton0 + IMouseButtonR cont -> cont <$> GLFW.mouseButtonIsPressed GLFW.MouseButton1 + IMouseButtonM cont -> cont <$> GLFW.mouseButtonIsPressed GLFW.MouseButton2 + IMousePosition cont -> do + (x, y) <- GLFW.getMousePosition + return $ cont $ V2 (fromIntegral x) (fromIntegral y) + IMouseWheel cont -> cont <$> GLFW.getMouseWheel + +runPicture :: (?refTextures :: IORef (IM.IntMap Texture)) => Picture a -> FinalizerT IO a +runPicture (LiftBitmap bmp@(BitmapData _ (Just h)) r) = do + m <- liftIO $ readIORef ?refTextures + case IM.lookup h m of + Just t -> liftIO $ drawTexture t + Nothing -> do + t <- installTexture bmp + liftIO $ writeIORef ?refTextures $ IM.insert h t m + liftIO $ drawTexture t + finalizer $ modifyIORef ?refTextures $ IM.delete h + return r +runPicture (LiftBitmap bmp@(BitmapData _ Nothing) r) = do + liftIO $ runFinalizerT $ installTexture bmp >>= liftIO . drawTexture + return r +runPicture (Rotate theta cont) = preservingMatrix' $ do + liftIO $ GL.rotate (gf (-theta)) (GL.Vector3 0 0 1) + runPicture cont +runPicture (Scale (V2 sx sy) cont) = preservingMatrix' $ do + liftIO $ GL.scale (gf sx) (gf sy) 1 + runPicture cont +runPicture (Translate (V2 tx ty) cont) = preservingMatrix' $ do + liftIO $ GL.translate (GL.Vector3 (gf tx) (gf ty) 0) + runPicture cont +runPicture (PictureWithFinalizer m) = m +runPicture (Colored (Color r g b a) cont) = do + oldColor <- liftIO $ get GL.currentColor + liftIO $ GL.currentColor $= GL.Color4 (gf r) (gf g) (gf b) (gf a) + res <- runPicture cont + liftIO $ GL.currentColor $= oldColor + return res + +preservingMatrix' :: MonadIO m => m a -> m a +preservingMatrix' m = do + liftIO $ glPushMatrix + r <- m + liftIO $ glPopMatrix + return r + +gf :: Float -> GL.GLfloat +{-# INLINE gf #-} +gf x = unsafeCoerce x + +gsizei :: Int -> GL.GLsizei +{-# INLINE gsizei #-} +gsizei x = unsafeCoerce x + +drawTexture :: Texture -> IO () +drawTexture (Texture tex width height) = do + let (w, h) = (fromIntegral width / 2, fromIntegral height / 2) + GL.textureFilter GL.Texture2D $= ((GL.Nearest, Nothing), GL.Nearest) + GL.textureBinding GL.Texture2D $= Just tex + GL.renderPrimitive GL.Polygon $ zipWithM_ + (\(pX, pY) (tX, tY) -> do + GL.texCoord $ GL.TexCoord2 (gf tX) (gf tY) + GL.vertex $ GL.Vertex2 (gf pX) (gf pY)) + [(-w, -h), (w, -h), (w, h), (-w, h)] + [(0,0), (1.0,0), (1.0,1.0), (0,1.0)] + +mapSpecialKey :: SpecialKey -> GLFW.Key +mapSpecialKey KeySpace = GLFW.KeySpace +mapSpecialKey KeyEsc = GLFW.KeyEsc +mapSpecialKey KeyLeftShift = GLFW.KeyLeftShift +mapSpecialKey KeyRightShift = GLFW.KeyRightShift +mapSpecialKey KeyLeftControl = GLFW.KeyLeftCtrl +mapSpecialKey KeyRightControl = GLFW.KeyRightCtrl +mapSpecialKey KeyUp = GLFW.KeyUp +mapSpecialKey KeyDown = GLFW.KeyDown +mapSpecialKey KeyLeft = GLFW.KeyLeft +mapSpecialKey KeyRight = GLFW.KeyRight +mapSpecialKey KeyTab = GLFW.KeyTab +mapSpecialKey KeyEnter = GLFW.KeyEnter +mapSpecialKey KeyBackspace = GLFW.KeyBackspace +mapSpecialKey KeyInsert = GLFW.KeyInsert +mapSpecialKey KeyDelete = GLFW.KeyDel +mapSpecialKey KeyPageUp = GLFW.KeyPageup +mapSpecialKey KeyPageDown = GLFW.KeyPagedown +mapSpecialKey KeyHome = GLFW.KeyHome +mapSpecialKey KeyEnd = GLFW.KeyEnd +mapSpecialKey KeyF1 = GLFW.KeyF1 +mapSpecialKey KeyF2 = GLFW.KeyF2 +mapSpecialKey KeyF3 = GLFW.KeyF3 +mapSpecialKey KeyF4 = GLFW.KeyF4 +mapSpecialKey KeyF5 = GLFW.KeyF5 +mapSpecialKey KeyF6 = GLFW.KeyF6 +mapSpecialKey KeyF7 = GLFW.KeyF7 +mapSpecialKey KeyF8 = GLFW.KeyF8 +mapSpecialKey KeyF9 = GLFW.KeyF9 +mapSpecialKey KeyF10 = GLFW.KeyF10 +mapSpecialKey KeyF11 = GLFW.KeyF11 +mapSpecialKey KeyF12 = GLFW.KeyF12 +mapSpecialKey KeyPad0 = GLFW.KeyPad0 +mapSpecialKey KeyPad1 = GLFW.KeyPad1 +mapSpecialKey KeyPad2 = GLFW.KeyPad2 +mapSpecialKey KeyPad3 = GLFW.KeyPad3 +mapSpecialKey KeyPad4 = GLFW.KeyPad4 +mapSpecialKey KeyPad5 = GLFW.KeyPad5 +mapSpecialKey KeyPad6 = GLFW.KeyPad6 +mapSpecialKey KeyPad7 = GLFW.KeyPad7 +mapSpecialKey KeyPad8 = GLFW.KeyPad8 +mapSpecialKey KeyPad9 = GLFW.KeyPad9 +mapSpecialKey KeyPadDivide = GLFW.KeyPadDivide +mapSpecialKey KeyPadMultiply = GLFW.KeyPadMultiply +mapSpecialKey KeyPadSubtract = GLFW.KeyPadSubtract +mapSpecialKey KeyPadAdd = GLFW.KeyPadAdd +mapSpecialKey KeyPadDecimal = GLFW.KeyPadDecimal +mapSpecialKey KeyPadEqual = GLFW.KeyPadEqual +mapSpecialKey KeyPadEnter = GLFW.KeyPadEnter
+ Graphics/UI/FreeGame/Internal/Finalizer.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE Rank2Types #-} +module Graphics.UI.FreeGame.Internal.Finalizer (FinalizerT(..), finalizer, runFinalizerT) where + +import Control.Monad.IO.Class +import Control.Applicative + +-- | An action with explicit releasing action. +newtype FinalizerT m a = FinalizerT + { unFinalizerT :: forall r. (a -> m r) -> (IO () -> r -> m r) -> m r } + +-- | Add a finalizer. +finalizer :: Monad m => IO () -> FinalizerT m () +finalizer m = FinalizerT $ \p f -> p () >>= f m + +instance Functor (FinalizerT m) where + fmap f (FinalizerT g) = FinalizerT $ \p -> g (p . f) + +instance Applicative (FinalizerT m) where + pure a = FinalizerT $ \p _ -> p a + FinalizerT ff <*> FinalizerT fa = FinalizerT $ \p f -> ff (\a -> fa (\b -> p (a b)) f) f + +instance Monad (FinalizerT m) where + return a = FinalizerT $ \p _ -> p a + FinalizerT rf >>= k = FinalizerT $ \p f -> rf (\x -> unFinalizerT (k x) p f) f + +instance MonadIO m => MonadIO (FinalizerT m) where + liftIO m = FinalizerT $ \r _ -> liftIO m >>= r + +-- | Run the action and run all associated finalizers. +runFinalizerT :: MonadIO m => FinalizerT m a -> m a +runFinalizerT (FinalizerT z) = do + (fin, a) <- z (\a -> return (return (), a)) (\m (fs, r) -> return (m >> fs, r)) + liftIO fin + return a
+ Graphics/UI/FreeGame/Types.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DeriveFunctor #-} +----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.Types +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : provisional +-- Portability : non-portable +-- +---------------------------------------------------------------------------- +module Graphics.UI.FreeGame.Types ( + BoundingBox(..), + _TopLeft, + _TopRight, + _BottomLeft, + _BottomRight + ) where + +import Linear.V2 + +-- | 2D bounding box. +data BoundingBox a = BoundingBox a a a a deriving (Show, Eq, Ord, Functor, Read) + +-- | @'_TopLeft' :: Lens' ('BoundingBox' a) ('V2' a)@ +_TopLeft :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) +_TopLeft f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x0' y0') -> BoundingBox x0' y0' x1 y1) (f (V2 x0 y0)) + +-- | @'_TopRight' :: Lens' ('BoundingBox' a) ('V2' a)@ +_TopRight :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) +_TopRight f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x1' y0') -> BoundingBox x0 y0' x1' y1) (f (V2 x1 y0)) + +-- | @'_BottomLeft' :: Lens' ('BoundingBox' a) ('V2' a)@ +_BottomLeft :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) +_BottomLeft f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x0' y1') -> BoundingBox x0' y0 x1 y1') (f (V2 x0 y1)) + +-- | @'_BottomRight' :: Lens' ('BoundingBox' a) ('V2' a)@ +_BottomRight :: Functor f => (V2 a -> f (V2 a)) -> (BoundingBox a -> f (BoundingBox a)) +_BottomRight f (BoundingBox x0 y0 x1 y1) = fmap (\(V2 x1' y1') -> BoundingBox x0 y0 x1' y1') (f (V2 x1 y1))
+ Graphics/UI/FreeGame/Util.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE FlexibleContexts, TemplateHaskell #-} +----------------------------------------------------------------------------- +-- | +-- Module : Graphics.UI.FreeGame.Util +-- Copyright : (C) 2013 Fumiaki Kinoshita +-- License : BSD-style (see the file LICENSE) +-- +-- Maintainer : Fumiaki Kinsohita <fumiexcel@gmail.com> +-- Stability : experimental +-- Portability : non-portable +-- +---------------------------------------------------------------------------- + +module Graphics.UI.FreeGame.Util ( + -- * Combinators + notF, + (<&&>), + (<||>), + -- * Controlling + untick, + untickInfinite, + -- * Random + randomness, + -- * Helper + degrees, + radians, + sinCos, + -- * Loading + loadPictureFromFile, + loadBitmaps, + loadBitmapsWith + ) where +import Control.Monad +import Control.Monad.Free +import Control.Applicative +import Data.Char +import Graphics.UI.FreeGame.Base +import Graphics.UI.FreeGame.Data.Bitmap +import System.Random +import Language.Haskell.TH +import System.Directory +import System.FilePath +import System.IO.Unsafe +import Data.Void +import Linear + +-- | A lifted 'not'. +notF :: Functor f => f Bool -> f Bool +notF = fmap not + +-- | A lifted '(&&)'. +(<&&>) :: Applicative f => f Bool -> f Bool -> f Bool +(<&&>) = liftA2 (&&) + +-- | A lifted '(||)' +(<||>) :: Applicative f => f Bool -> f Bool -> f Bool +(<||>) = liftA2 (||) + +-- | Create a unit vector from a direction. +sinCos :: Floating a => a -> V2 a +sinCos t = V2 (cos t) (sin t) + +-- | Extract the next frame of the action. +untick :: (Functor n, MonadFree (UI n) m) => Free (UI n) a -> m (Either (Free (UI n) a) a) +untick (Pure a) = return (Right a) +untick (Free (Tick cont)) = return (Left cont) +untick (Free f) = wrap $ fmap untick f + +-- | An infinite version of 'untick'. +untickInfinite :: (Functor n, MonadFree (UI n) m) => Free (UI n) Void -> m (Free (UI n) Void) +untickInfinite = liftM (either id absurd) . untick + +-- | Get a given range of value. +randomness :: (Random r, MonadFree (UI n) m) => (r, r) -> m r +randomness = embedIO . randomRIO + +-- | Convert radians to degrees. +degrees :: Float -> Float +{-# INLINE degrees #-} +degrees x = x / pi * 180 + +-- | Convert degrees to radians. +radians :: Float -> Float +{-# INLINE radians #-} +radians x = x / 180 * pi + +-- | Create a 'Picture' from the given file. +loadPictureFromFile :: (Picture2D p, MonadFree (UI n) m) => FilePath -> m (p ()) +loadPictureFromFile = embedIO . fmap fromBitmap . loadBitmapFromFile + +-- | Load and define all pictures in the specified directory. +loadBitmapsWith :: Name -> FilePath -> Q [Dec] +loadBitmapsWith getFullPath path = do + loc <- (</>path) <$> takeDirectory <$> loc_filename <$> location + paths <- runIO $ getFileList loc + + sequence $ do + p <- paths + let name = pathToName p + [ return $ SigD (mkName name) (ConT ''Bitmap) + , funD (mkName name) [clause [] (normalB $ load name $ loc </> p) []] + ] + where + load name fp = do + runIO $ putStrLn $ "Defined: " ++ fp ++ " as `" ++ name ++ "'" + appE (varE 'unsafePerformIO) $ uInfixE (appE (varE getFullPath) (litE $ StringL fp)) + (varE '(>>=)) + (varE 'loadBitmapFromFile) + +-- | use with getDataFileName +loadBitmaps :: FilePath -> Q [Dec] +loadBitmaps = loadBitmapsWith 'canonicalizePath + +getFileList :: FilePath -> IO [FilePath] +getFileList path = do + allContents <- filter notHidden `fmap` getDirectoryContents path + files <- filterM (doesFileExist . (path</>)) allContents + dirs <- filterM (doesDirectoryExist . (path</>)) allContents + fmap ((files++).concat) $ forM dirs $ \i -> map (i</>) `fmap` getFileList (path</>i) + where + notHidden ('.':_) = False + notHidden _ = True + +pathToName :: FilePath -> String +pathToName = ('_':) . map p where + p c | isAlphaNum c = c + | otherwise = '_'
− examples/VL-PGothic-Regular.ttf
file too large to diff
− examples/bitmap.hs
@@ -1,21 +0,0 @@-import Graphics.FreeGame.Simple -import Graphics.FreeGame.Data.Bitmap -import Control.Monad -import Data.Array.Repa -import Data.Word - -renderCircle :: Int -> (Word8, Word8, Word8, Word8) -> Bitmap -renderCircle size (r,g,b,a) = toStableBitmap $ computeS $ fromFunction (Z :. size :. size :. 4) render where - center = fromIntegral size / 2 - render (Z:.y:.x:.0) - | s < 0 = a - | s >= 1 = 0 - | otherwise = floor ((1 - s) * 256) - where - r = sqrt $ (fromIntegral y - center) ^ 2 + (fromIntegral x - center) ^ 2 - s = r - fromIntegral size / 2 - render (Z:._:._:.c) = [undefined,b,g,r] !! c - -main = runSimple defaultGameParam () $ \_ -> drawPicture $ Translate (Vec2 240 240) circle - where - circle = Scale (Vec2 0.5 0.5) $ BitmapPicture $ renderCircle 128 (128,216,128,255)
− examples/font.hs
@@ -1,16 +0,0 @@-import Graphics.FreeGame.Simple -import Graphics.FreeGame.Data.Color -import Graphics.FreeGame.Data.Font -import Control.Monad - -main = do - font <- loadFont "VL-PGothic-Regular.ttf" - print $ fontBoundingBox font - runSimple defaultGameParam 0 $ \n -> do - drawPicture $ Translate (Vec2 30 120) $ Colored (halfD red) $ text font 14 ("Counter: " ++ show n) - - drawPicture $ Translate (Vec2 30 240) $ Colored (halfD blue) $ text font 20 "日本語も、美しくレンダリング。" - - -- drawPicture $ Translate (Vec2 30 (240 + * 20)) $ Colored (halfD green) $ text font 20 "___" - - return $! n + 1
− examples/images/logo.png
binary file changed (1893 → absent bytes)
− examples/test.hs
@@ -1,59 +0,0 @@-{-# LANGUAGE TemplateHaskell #-} -import Graphics.FreeGame.Simple -import Control.Applicative -import Control.Monad -import Data.Vect -import Control.Monad.State -import Data.Void -import Control.Lens -- using lens (http://hackage.haskell.org/package/lens) - -$(loadBitmaps "images") - -data Object = Object - { _position :: Vec2 - , _velocity :: Vec2 - , _pressed :: Bool - } - -$(makeLenses ''Object) - -obj :: StateT Object Game Void -obj = forever $ do - pos@(Vec2 x y) <- use position - - vel@(Vec2 dx dy) <- use velocity - - let dx' | x <= 0 = abs dx - | x >= 640 = -(abs dx) - | otherwise = dx - dy' | y <= 0 = abs dy - | y >= 480 = -(abs dy) - | otherwise = dy - - position .= pos &+ vel - velocity .= Vec2 dx' dy' - - mpos <- getMousePosition - - w <- if norm (mpos &- pos) < 32 - then do - btn <- use pressed - btn' <- getButtonState MouseLeft - when (not btn && btn') $ velocity <~ (&*4) <$> sinCos <$> randomness (0, 2 * pi) - pressed .= btn' - return id - - else return $ Colored (transparent 0.7 white) - - drawPicture $ Translate pos $ w (Bitmap _logo_png) - - tick - -initial :: Game Void -initial = do - x <- randomness (0,640) - y <- randomness (0,480) - a <- randomness (0, 2 * pi) - evalStateT obj $ Object (Vec2 x y) (sinCos a &* 4) False - -main = runSimple defaultGameParam (replicate 100 initial) $ mapM untickInfinite
free-game.cabal view
@@ -1,6 +1,6 @@ name: free-game -version: 0.3.2.7 -synopsis: Create graphical applications for free. +version: 0.9 +synopsis: Create graphical applications for free description: Cross-platform GUI library based on free monads homepage: https://github.com/fumieval/free-game bug-reports: https://github.com/fumieval/free-game/issues @@ -9,48 +9,51 @@ author: Fumiaki Kinoshita maintainer: Fumiaki Kinoshita <fumiexcel@gmail.com> copyright: Copyright (C) 2012-2013 Fumiaki Kinoshita -category: Graphics +category: Graphics, Monads build-type: Simple stability: experimental -cabal-version: >=1.8 -extra-source-files: - examples/*.hs - examples/images/*.png - examples/VL-PGothic-Regular.ttf +cabal-version: >=1.10 source-repository head type: git location: https://github.com/fumieval/free-game.git library - exposed-modules: Graphics.FreeGame - , Graphics.FreeGame.Simple - , Graphics.FreeGame.Util - , Graphics.FreeGame.Input - , Graphics.FreeGame.Base - , Graphics.FreeGame.Types - , Graphics.FreeGame.Backends.GLFW - , Graphics.FreeGame.Data.Color - , Graphics.FreeGame.Data.Bitmap - , Graphics.FreeGame.Data.Font - , Graphics.FreeGame.Internal.Finalizer + default-language: Haskell2010 + exposed-modules: + Graphics.UI.FreeGame + Graphics.UI.FreeGame.Base + Graphics.UI.FreeGame.Data.Bitmap + Graphics.UI.FreeGame.Data.Color + Graphics.UI.FreeGame.Data.Font + Graphics.UI.FreeGame.GUI + Graphics.UI.FreeGame.GUI.GLFW + Graphics.UI.FreeGame.Internal.Finalizer + Graphics.UI.FreeGame.Types + Graphics.UI.FreeGame.Util + ghc-options: -Wall -fexcess-precision -O2 - build-depends: base == 4.* - , array >= 0.4 - , containers >= 0.4 - , directory >= 1.0 - , filepath >= 1.3 - , free == 3.* - , freetype2 >= 0.1 - , GLFW-b >= 0.1.0.3 - , hashable >= 1.2 - , JuicyPixels-repa >= 0.5.1 - , mtl >= 2.1 - , OpenGL == 2.8.* - , OpenGLRaw >= 1.3 - , random == 1.* - , repa >= 3.2 - , transformers >= 0.3 - , vect >= 0.4.7 - , void >= 0.5 - , template-haskell+ + build-depends: + base == 4.*, + array >= 0.4, + containers >= 0.4, + directory >= 1.0, + filepath >= 1.3, + free == 3.*, + repa, + freetype2 >= 0.1, + GLFW-b >= 0.1.0.3, + hashable >= 1.2, + mtl >= 2.1, + OpenGL == 2.8.*, + OpenGLRaw == 1.3.*, + profunctors == 3.*, + random == 1.*, + transformers >= 0.3, + void >= 0.5, + template-haskell, + linear >= 1.1 && < 1.3, + vector == 0.10.*, + JuicyPixels-repa >= 0.5.1, + data-default