packages feed

helm 0.2.0 → 0.3.0

raw patch · 11 files changed

+482/−111 lines, 11 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ FRP.Helm.Automaton: (<<<) :: Automaton b c -> Automaton a b -> Automaton a c
+ FRP.Helm.Automaton: (>>>) :: Automaton a b -> Automaton b c -> Automaton a c
+ FRP.Helm.Automaton: Step :: (a -> (Automaton a b, b)) -> Automaton a b
+ FRP.Helm.Automaton: combine :: [Automaton a b] -> Automaton a [b]
+ FRP.Helm.Automaton: counter :: Automaton a Int
+ FRP.Helm.Automaton: data Automaton a b
+ FRP.Helm.Automaton: pure :: (a -> b) -> Automaton a b
+ FRP.Helm.Automaton: run :: Automaton a b -> b -> SignalGen (Signal a) -> SignalGen (Signal b)
+ FRP.Helm.Automaton: stateful :: b -> (a -> b -> b) -> Automaton a b
+ FRP.Helm.Automaton: step :: a -> Automaton a b -> (Automaton a b, b)
+ FRP.Helm.Graphics: Text :: String -> Color -> String -> Double -> FontWeight -> FontSlant -> Text
+ FRP.Helm.Graphics: TextElement :: Text -> Element
+ FRP.Helm.Graphics: data Text
+ FRP.Helm.Graphics: fontSize :: Text -> Double
+ FRP.Helm.Graphics: fontSlant :: Text -> FontSlant
+ FRP.Helm.Graphics: fontTypeface :: Text -> String
+ FRP.Helm.Graphics: fontWeight :: Text -> FontWeight
+ FRP.Helm.Graphics: textColor :: Text -> Color
+ FRP.Helm.Graphics: textUTF8 :: Text -> String
+ FRP.Helm.Joystick: available :: SignalGen (Signal Int)
+ FRP.Helm.Joystick: availableAxes :: Joystick -> SignalGen (Signal Int)
+ FRP.Helm.Joystick: availableBalls :: Joystick -> SignalGen (Signal Int)
+ FRP.Helm.Joystick: availableButtons :: Joystick -> SignalGen (Signal Int)
+ FRP.Helm.Joystick: availableHats :: Joystick -> SignalGen (Signal Int)
+ FRP.Helm.Joystick: axis :: Joystick -> Int -> SignalGen (Signal Int)
+ FRP.Helm.Joystick: ball :: Joystick -> Int -> SignalGen (Signal (Int, Int))
+ FRP.Helm.Joystick: button :: Joystick -> Int -> SignalGen (Signal Bool)
+ FRP.Helm.Joystick: hat :: Joystick -> Int -> SignalGen (Signal (Int, Int))
+ FRP.Helm.Joystick: index :: Joystick -> SignalGen (Signal Int)
+ FRP.Helm.Joystick: name :: Int -> SignalGen (Signal String)
+ FRP.Helm.Joystick: open :: Int -> SignalGen (Signal Joystick)
+ FRP.Helm.Joystick: type Joystick = Joystick
+ FRP.Helm.Mouse: instance Enum Mouse
+ FRP.Helm.Text: asText :: Show a => a -> Element
+ FRP.Helm.Text: bold :: Text -> Text
+ FRP.Helm.Text: color :: Color -> Text -> Text
+ FRP.Helm.Text: defaultText :: Text
+ FRP.Helm.Text: header :: Text -> Text
+ FRP.Helm.Text: height :: Double -> Text -> Text
+ FRP.Helm.Text: italic :: Text -> Text
+ FRP.Helm.Text: monospace :: Text -> Text
+ FRP.Helm.Text: plainText :: String -> Element
+ FRP.Helm.Text: text :: Text -> Element
+ FRP.Helm.Text: toText :: String -> Text
+ FRP.Helm.Text: typeface :: String -> Text -> Text

Files

FRP/Helm.hs view
@@ -12,6 +12,7 @@   module FRP.Helm.Graphics, ) where +import Control.Monad (void) import Data.IORef import Foreign.Ptr (castPtr) import FRP.Elerea.Simple@@ -25,7 +26,6 @@ {-| Attempt to change the window dimensions (and initialize the video mode if not already).     Will try to get a hardware accelerated window and then fallback to a software one.     Throws an exception if the software mode can't be used as a fallback. -}--- TODO: userland version of this requestDimensions :: Int -> Int -> IO SDL.Surface requestDimensions w h =	do   mayhaps <- SDL.trySetVideoMode w h 32 [SDL.HWSurface, SDL.DoubleBuf, SDL.Resizable]@@ -83,7 +83,7 @@     >   return $ fmap render dims  -} run :: SignalGen (Signal Element) -> IO ()-run gen = SDL.init [SDL.InitVideo] >> requestDimensions 800 600 >> start gen >>= newEngineState >>= run'+run gen = SDL.init [SDL.InitVideo, SDL.InitJoystick] >> requestDimensions 800 600 >> start gen >>= newEngineState >>= run'  {-| A utility function called by 'run' that samples the element     or quits the entire engine if SDL events say to do so. -}@@ -160,7 +160,7 @@  {-| A utility function for rendering a specific element. -} renderElement :: EngineState -> Element -> Cairo.Render ()-renderElement state (CollageElement _ _ forms) = mapM (renderForm state) forms >> return ()+renderElement state (CollageElement _ _ forms) = void $ mapM_ (renderForm state) forms renderElement state (ImageElement (sx, sy) sw sh src stretch) = do   (surface, w, h) <- Cairo.liftIO $ getSurface state (normalise src) @@ -178,6 +178,12 @@   Cairo.fill   Cairo.restore +renderElement _ (TextElement (Text { textColor = (Color r g b a), .. })) = do+  Cairo.setSourceRGBA r g b a+  Cairo.selectFontFace fontTypeface fontSlant fontWeight+  Cairo.setFontSize fontSize+  Cairo.showText textUTF8+ {-| A utility function that goes into a state of transformation and then pops it when finished. -} withTransform :: Double -> Double -> Double -> Double -> Cairo.Render () -> Cairo.Render () withTransform s t x y f = Cairo.save >> Cairo.scale s s >> Cairo.rotate t >> Cairo.translate x y >> f >> Cairo.restore@@ -212,31 +218,31 @@ setFillStyle :: EngineState -> FillStyle -> Cairo.Render () setFillStyle _ (Solid (Color r g b a)) = Cairo.setSourceRGBA r g b a >> Cairo.fill setFillStyle state (Texture src) = do-  (surface, w, h) <- Cairo.liftIO $ getSurface state (normalise src)+  (surface, _, _) <- Cairo.liftIO $ getSurface state (normalise src) -  Cairo.setSourceSurface surface 0 0 >> Cairo.getSource >>= (flip Cairo.patternSetExtend) Cairo.ExtendRepeat+  Cairo.setSourceSurface surface 0 0 >> Cairo.getSource >>= flip Cairo.patternSetExtend Cairo.ExtendRepeat   Cairo.fill -setFillStyle _ (Gradient (Linear (sx, sy) (ex, ey) points)) = do-  Cairo.withLinearPattern sx sy ex ey $ \pattern -> do-    Cairo.setSource pattern >> mapM (\(o, (Color r g b a)) -> Cairo.patternAddColorStopRGBA pattern o r g b a) points >> Cairo.fill+setFillStyle _ (Gradient (Linear (sx, sy) (ex, ey) points)) =+  Cairo.withLinearPattern sx sy ex ey $ \pattern ->+    Cairo.setSource pattern >> mapM (\(o, Color r g b a) -> Cairo.patternAddColorStopRGBA pattern o r g b a) points >> Cairo.fill -setFillStyle _ (Gradient (Radial (sx, sy) sr (ex, ey) er points)) = do-  Cairo.withRadialPattern sx sy sr ex ey er $ \pattern -> do-    Cairo.setSource pattern >> mapM (\(o, (Color r g b a)) -> Cairo.patternAddColorStopRGBA pattern o r g b a) points >> Cairo.fill+setFillStyle _ (Gradient (Radial (sx, sy) sr (ex, ey) er points)) =+  Cairo.withRadialPattern sx sy sr ex ey er $ \pattern ->+    Cairo.setSource pattern >> mapM (\(o, Color r g b a) -> Cairo.patternAddColorStopRGBA pattern o r g b a) points >> Cairo.fill  {-| A utility that renders a form. -} renderForm :: EngineState -> Form -> Cairo.Render () renderForm _ (Form { style = PathForm style p, .. }) =   withTransform scalar theta x y $ -      setLineStyle style >> Cairo.moveTo hx hy >> mapM (\(x_, y_) -> Cairo.lineTo x_ y_) p >> return ()+      void $ setLineStyle style >> Cairo.moveTo hx hy >> mapM (uncurry Cairo.lineTo) p      where       (hx, hy) = head p  renderForm state (Form { style = ShapeForm style (PolygonShape points), .. }) =   withTransform scalar theta x y $ do-      Cairo.newPath >> Cairo.moveTo hx hy >> mapM (\(x_, y_) -> Cairo.lineTo x_ y_) points >> Cairo.closePath+      Cairo.newPath >> Cairo.moveTo hx hy >> mapM (uncurry Cairo.lineTo) points >> Cairo.closePath        case style of         Left lineStyle -> setLineStyle lineStyle@@ -264,4 +270,4 @@       Right fillStyle -> setFillStyle state fillStyle  renderForm state (Form { style = ElementForm element, .. }) = withTransform scalar theta x y $ renderElement state element-renderForm state (Form { style = GroupForm m forms, .. }) = withTransform scalar theta x y $ Cairo.setMatrix m >> mapM (renderForm state) forms >> return ()+renderForm state (Form { style = GroupForm m forms, .. }) = withTransform scalar theta x y $ void $ Cairo.setMatrix m >> mapM (renderForm state) forms
+ FRP/Helm/Automaton.hs view
@@ -0,0 +1,78 @@+{-| Contains all data structures and functions for composing, calculating and creating automatons. -}+module FRP.Helm.Automaton (+  -- * Types+  Automaton(..),+  -- * Composing+  pure,+  stateful,+  combine,+  (>>>),+  (<<<),+  -- * Computing+  step,+  run,+  counter+) where++import FRP.Elerea.Simple (Signal, SignalGen, transfer)++{-| A data structure describing an automaton.+    An automaton is essentially a high-level way to package piped behavior+    between an input signal and an output signal. Automatons can also+    be composed, allowing you to connect one automaton to another+    and pipe data between them. Automatons are an easy and powerful way+    to create composable dynamic behavior, like animation systems. -}+data Automaton a b = Step (a -> (Automaton a b, b))++{-| Creates a pure automaton that has no accumulated state. It applies input to+    a function at each step. -}+pure :: (a -> b) -> Automaton a b+pure f = Step (\x -> (pure f, f x))++{-| Creates an automaton that has an initial and accumulated state. It applies+    input and the last state to a function at each step. -}+stateful :: b -> (a -> b -> b) -> Automaton a b+stateful s f = Step (\x -> let s' = f x s+                           in (stateful s' f, s'))++{-| Steps an automaton forward, returning the next automaton to step+    and output of the step in a tuple. -}+step :: a -> Automaton a b -> (Automaton a b, b)+step auto (Step f) = f auto++{-| Combines a list of automatons that take some input+    and turns it into an automaton that takes+    the same input and outputs a list of all outputs+    from each separate automaton. -}+combine :: [Automaton a b] -> Automaton a [b]+combine autos =+  Step (\a -> let (autos', bs) = unzip $ map (step a) autos+              in  (combine autos', bs))++{-| Pipes two automatons together. It essentially+    returns an automaton that takes the input of the first+    automaton and outputs the output of the second automaton,+    with the directly connected values being discarded. -}+(>>>) :: Automaton a b -> Automaton b c -> Automaton a c+f >>> g =+  Step (\a -> let (f', b) = step a f+                  (g', c) = step b g+              in (f' >>> g', c))++{-| Pipes two automatons in the opposite order of '>>>'. -}+(<<<) :: Automaton b c -> Automaton a b -> Automaton a c+g <<< f = f >>> g++{-| A useful automaton that outputs the amount of times it has been stepped,+    discarding its input value. -}+counter :: Automaton a Int+counter = stateful 0 (\_ c -> c + 1)++{-| Runs an automaton with an initial output value and input signal generator+    and creates an output signal generator that contains a signal that can be+    sampled for the output value. -}+run :: Automaton a b -> b -> SignalGen (Signal a) -> SignalGen (Signal b)+run auto initial feeder = do+  stepper <- feeder >>= transfer (auto, initial) (\a (Step f, _) -> f a)++  return $ fmap snd stepper
FRP/Helm/Color.hs view
@@ -1,34 +1,34 @@ {-| Contains all data structures and functions for composing colors. -} module FRP.Helm.Color (-	-- * Types-	Color(..),-	Gradient(..),-	-- * Composing-	rgba,-	rgb,+  -- * Types+  Color(..),+  Gradient(..),+  -- * Composing+  rgba,+  rgb,   hsva,   hsv,   complement,-	linear,-	radial,-	-- * Constants-	red,-	lime,-	blue,-	yellow,-	cyan,-	magenta,-	black,-	white,-	gray,-	grey,-	maroon,-	navy,-	green,-	teal,-	purple,-	violet,-	forestGreen+  linear,+  radial,+  -- * Constants+  red,+  lime,+  blue,+  yellow,+  cyan,+  magenta,+  black,+  white,+  gray,+  grey,+  maroon,+  navy,+  green,+  teal,+  purple,+  violet,+  forestGreen ) where  {-| A data structure describing a color. It is represented interally as an RGBA@@ -138,7 +138,7 @@    where     h' = h / 60-    h'' = (floor h') `mod` 6 :: Int+    h'' = floor h' `mod` 6 :: Int     f = h' - fromIntegral h''     p = v * (1 - s)     q = v * (1 - f * s)
FRP/Helm/Graphics.hs view
@@ -3,6 +3,7 @@ module FRP.Helm.Graphics (   -- * Types   Element(..),+  Text(..),   Form(..),   FormStyle(..),   FillStyle(..),@@ -49,8 +50,9 @@   ngon ) where -import FRP.Helm.Color as Color+import FRP.Helm.Color (Color, black, Gradient) import Graphics.Rendering.Cairo.Matrix (Matrix, identity)+import qualified Graphics.Rendering.Cairo as Cairo  {-| A data structure describing something that can be rendered     to the screen. Elements are the most important structure@@ -60,8 +62,19 @@     off to the 'collage' function, which essentially     renders a collection of forms together. -} data Element = CollageElement Int Int [Form] |-               ImageElement (Int, Int) Int Int FilePath Bool+               ImageElement (Int, Int) Int Int FilePath Bool |+               TextElement Text +{-| A data structure describing a piece of formatted text. -}+data Text = Text {+  textUTF8 :: String,+  textColor :: Color,+  fontTypeface :: String,+  fontSize :: Double,+  fontWeight :: Cairo.FontWeight,+  fontSlant :: Cairo.FontSlant+}+ {-| Create an element from an image with a given width, height and image file path.     If the image dimensions are not the same as given, then it will stretch/shrink to fit.     Only PNG files are supported currently. -}@@ -116,7 +129,7 @@     flat caps and regular sharp joints. -} defaultLine :: LineStyle defaultLine = LineStyle {-  color = Color.black,+  color = black,   width = 1,   cap = Flat,   join = Sharp 10,@@ -153,15 +166,15 @@  {-| Creates a form from a shape by filling it with a specific color. -} filled :: Color -> Shape -> Form-filled color shape = fill (Solid color) shape+filled color = fill (Solid color)  {-| Creates a form from a shape with a tiled texture and image file path. -} textured :: String -> Shape -> Form-textured src shape = fill (Texture src) shape+textured src = fill (Texture src)  {-| Creates a form from a shape filled with a gradient. -} gradient :: Gradient -> Shape -> Form-gradient grad shape = fill (Gradient grad) shape+gradient grad = fill (Gradient grad)  {-| Creates a form from a shape by outlining it with a specific line style. -} outlined :: LineStyle -> Shape -> Form@@ -202,11 +215,11 @@  {-| Moves a form's x-coordinate relative to its current position. -} moveX :: Double -> Form -> Form-moveX x f = move (x, 0) f+moveX x = move (x, 0)  {-| Moves a form's y-coordinate relative to its current position. -} moveY :: Double -> Form -> Form-moveY y f = move (0, y) f+moveY y = move (0, y)  {-| Create an element from a collection of forms, with width and height arguments.     Can be used to directly render a collection of forms.@@ -215,7 +228,7 @@     >                  move (100, 100) $ outlined (solid white) $ circle 50]  -} collage :: Int -> Int -> [Form] -> Element-collage w h forms = CollageElement w h forms+collage = CollageElement  {-| A data type made up a collection of points that form a path when joined. -} type Path = [(Double, Double)]@@ -234,7 +247,7 @@  {-| Creates a shape from a path (a set of points). -} polygon :: Path -> Shape-polygon points = PolygonShape points+polygon = PolygonShape  {-| Creates a rectangular shape with a width and height. -} rect :: Double -> Double -> Shape
+ FRP/Helm/Joystick.hs view
@@ -0,0 +1,105 @@+{-| Contains signals that sample input from joysticks. -}+module FRP.Helm.Joystick (+  -- * Types+  Joystick,+  -- * Probing+  available,+  name,+  open,+  index,+  availableAxes,+  availableBalls,+  availableHats,+  availableButtons,+  -- * Joystick State+  axis,+  hat,+  button,+  ball+) where++import Control.Applicative+import Data.Int (Int16)+import FRP.Elerea.Simple+import qualified Graphics.UI.SDL as SDL++{-| A type describing a joystick. -}+type Joystick = SDL.Joystick++{-| The amount of joysticks available. -}+available :: SignalGen (Signal Int)+available = effectful SDL.countAvailable++{-| The name of a joystick. -}+name :: Int -> SignalGen (Signal String)+name i = effectful $ SDL.name i++{-| The joystick at a certain slot. -}+open :: Int -> SignalGen (Signal Joystick)+open i = effectful $ SDL.open i++{-| The index of a joystick. -}+index :: Joystick -> SignalGen (Signal Int)+index j = return $ return $ SDL.index j++{-| The amount of axes available for a joystick. -}+availableAxes :: Joystick -> SignalGen (Signal Int)+availableAxes j = return $ return $ SDL.axesAvailable j++{-| The amount of balls available for a joystick. -}+availableBalls :: Joystick -> SignalGen (Signal Int)+availableBalls j = return $ return $ SDL.ballsAvailable j++{-| The amount of hats available for a joystick. -}+availableHats :: Joystick -> SignalGen (Signal Int)+availableHats j = return $ return $ SDL.hatsAvailable j++{-| The amount of buttons available for a joystick. -}+availableButtons :: Joystick -> SignalGen (Signal Int)+availableButtons j = return $ return $ SDL.buttonsAvailable j++{-| The current state of the axis of the joystick. -}+axis :: Joystick -> Int -> SignalGen (Signal Int)+axis j i = effectful $ SDL.update >> fromIntegral <$> SDL.getAxis j (fromIntegral i)++{-| The current state of the hat of the joystick, returned+    as a directional tuple. For example, up is /(0, -1)/,+    left /(-1, 0)/, bottom-right is /(1, 1)/, etc. -}+hat :: Joystick -> Int -> SignalGen (Signal (Int, Int))+hat j i = effectful $ SDL.update >> hat' <$> SDL.getHat j (fromIntegral i)++{-| A utility function for mapping a list of hat states to an averaged directional tuple. -}+hat' :: [SDL.Hat] -> (Int, Int)+hat' hats = if l > 0 then (round $ fromIntegral hx / l, round $ fromIntegral hy / l) else (0, 0)+  where+    l = realToFrac $ length hats :: Double+    (hx, hy) = foldl hat'' (0, 0) hats++{-| A utility function for accumulating the total directional tuple. -}+hat'' :: (Int, Int) -> SDL.Hat -> (Int, Int)+hat'' (x, y) h =+  case h of+    SDL.HatCentered -> (x, y)+    SDL.HatUp -> (x, y - 1)+    SDL.HatRight -> (x + 1, y)+    SDL.HatDown -> (x, y + 1)+    SDL.HatLeft -> (x - 1, y)+    SDL.HatRightUp -> (x + 1, y - 1)+    SDL.HatRightDown -> (x + 1, y + 1)+    SDL.HatLeftUp -> (x - 1, x - 1)+    SDL.HatLeftDown -> (x - 1, y + 1)++{-| The current state of the button of the joystick. -}+button :: Joystick -> Int -> SignalGen (Signal Bool)+button j i = effectful $ SDL.update >> SDL.getButton j (fromIntegral i)++{-| The current state of the ball of the joystick. -}+ball :: Joystick -> Int -> SignalGen (Signal (Int, Int))+ball j i = effectful $ SDL.update >> ball' <$> SDL.getBall j (fromIntegral i)++{-| A utility function for mapping the optional value to a null tuple or the actual tuple. -}+ball' :: Maybe (Int16, Int16) -> (Int, Int)+ball' mayhaps =+  case mayhaps of+    Just (x, y) -> (fromIntegral x, fromIntegral y)+    Nothing -> (0, 0)
FRP/Helm/Keyboard.hs view
@@ -25,7 +25,7 @@ getKeyState = alloca $ \numkeysPtr -> do   keysPtr <- sdlGetKeyState numkeysPtr   numkeys <- peek numkeysPtr-  (map fromIntegral . findIndices (== 1)) <$> peekArray (fromIntegral numkeys) keysPtr+  (map fromIntegral . elemIndices 1) <$> peekArray (fromIntegral numkeys) keysPtr  {-| A data structure describing a physical key on a keyboard. -} data Key = BackspaceKey | TabKey | ClearKey | EnterKey | PauseKey | EscapeKey |@@ -329,15 +329,15 @@  {-| Whether either shift key is pressed. -} shift :: SignalGen (Signal Bool)-shift = effectful $ (elem SDL.KeyModShift) <$> SDL.getModState+shift = effectful $ elem SDL.KeyModShift <$> SDL.getModState  {-| Whether either control key is pressed. -} ctrl :: SignalGen (Signal Bool)-ctrl = effectful $ (elem SDL.KeyModCtrl) <$> SDL.getModState+ctrl = effectful $ elem SDL.KeyModCtrl <$> SDL.getModState  {-| Whether a key is pressed. -} isDown :: Key -> SignalGen (Signal Bool)-isDown k = effectful $ (elem $ fromEnum k) <$> getKeyState+isDown k = effectful $ elem (fromEnum k) <$> getKeyState  {-| Whether the enter (a.k.a. return) key is pressed. -} enter :: SignalGen (Signal Bool)@@ -349,7 +349,7 @@  {-| A list of keys that are currently being pressed. -} keysDown :: SignalGen (Signal [Key])-keysDown = effectful $ (map toEnum) <$> getKeyState+keysDown = effectful $ map toEnum <$> getKeyState  {-| A directional tuple combined from the arrow keys. When none of the arrow keys     are being pressed this signal samples to /(0, 0)/, otherwise it samples to a
FRP/Helm/Mouse.hs view
@@ -1,20 +1,32 @@ {-| Contains signals that sample input from the mouse. -} module FRP.Helm.Mouse (-	-- * Types-	Mouse(..),-	-- * Position-	isDown,-	-- * Mouse State-	position, x, y+  -- * Types+  Mouse(..),+  -- * Position+  isDown,+  -- * Mouse State+  position, x, y ) where  import Control.Applicative import FRP.Elerea.Simple import qualified Graphics.UI.SDL as SDL+import qualified Graphics.UI.SDL.Utilities as Util  {-| A data structure describing a button on a mouse. -} data Mouse = LeftMouse | MiddleMouse | RightMouse +{- All integer values of this enum are equivalent to the SDL key enum. -}+instance Enum Mouse where+  fromEnum LeftMouse = 1+  fromEnum MiddleMouse = 2+  fromEnum RightMouse = 3++  toEnum 1 = LeftMouse+  toEnum 2 = MiddleMouse+  toEnum 3 = RightMouse+  toEnum _ = error "FRP.Helm.Mouse.Mouse.toEnum: bad argument"+ {-| The current position of the mouse. -} position :: SignalGen (Signal (Int, Int)) position = effectful $ (\(x_, y_, _) -> (x_, y_)) <$> SDL.getMouseState@@ -27,15 +39,7 @@ y :: SignalGen (Signal Int) y = effectful $ (\(_, y_, _) -> y_) <$> SDL.getMouseState -{-| Maps our mouse type into SDL's one. -}-mapMouse :: Mouse -> SDL.MouseButton-mapMouse m =-  case m of-    LeftMouse -> SDL.ButtonLeft-    MiddleMouse -> SDL.ButtonMiddle-    RightMouse -> SDL.ButtonRight- {-| The current state of a certain mouse button.     True if the mouse is down, false otherwise. -} isDown :: Mouse -> SignalGen (Signal Bool)-isDown m = effectful $ (\(_, _, b_) -> elem (mapMouse m) b_) <$> SDL.getMouseState+isDown m = effectful $ (\(_, _, b_) -> elem (Util.toEnum $ fromIntegral $ fromEnum m) b_) <$> SDL.getMouseState
+ FRP/Helm/Text.hs view
@@ -0,0 +1,91 @@+{-| Contains all the data structures and functions for composing+    pieces of formatted text. -}+module FRP.Helm.Text (+  -- * Elements+  plainText,+  asText,+  text,+  -- * Composing+  defaultText,+  toText,+  -- * Formatting+  bold,+  italic,+  color,+  monospace,+  typeface,+  header,+  height+) where++import FRP.Helm.Color (Color, black)+import FRP.Helm.Graphics (Element(TextElement), Text(..))+import qualified Graphics.Rendering.Cairo as Cairo++{-| Creates the default text. By default the text is black sans-serif+    with a height of 14px. -}+defaultText :: Text+defaultText = Text {+  textUTF8 = "",+  textColor = black,+  fontTypeface = "sans-serif",+  fontSize = 14,+  fontWeight = Cairo.FontWeightNormal,+  fontSlant = Cairo.FontSlantNormal+}++{-| Creates a text from a string. -}+toText :: String -> Text+toText utf8 = defaultText { textUTF8 = utf8 }++{-| Creates a text element from a string. -}+plainText :: String -> Element+plainText utf8 = text $ toText utf8++{-| Creates a text element from any showable type, defaulting to+    the monospace typeface. -}+asText :: Show a => a -> Element+asText val = text $ monospace $ toText $ show val++{-| Creates an element from a text. -}+text :: Text -> Element+text = TextElement++{- TODO:+centered+justified+righted+underline+strikeThrough+overline+-}++{-| Sets the weight of a piece of text to bold. -}+bold :: Text -> Text+bold txt = txt { fontWeight = Cairo.FontWeightBold }++{-| Sets the slant of a piece of text to italic. -}+italic :: Text -> Text+italic txt = txt { fontSlant = Cairo.FontSlantItalic }++{-| Sets the color of a piece of text. -}+color :: Color -> Text -> Text+color col txt = txt { textColor = col }++{-| Sets the typeface of the text to monospace. -}+monospace :: Text -> Text+monospace txt = txt { fontTypeface = "monospace" }++{-| Sets the typeface of the text. Only fonts+    supported by Cairo's toy font API are currently+    supported. -}+typeface :: String -> Text -> Text+typeface face txt = txt { fontTypeface = face }++{-| Sets the size of a text noticeably large. -}+header :: Text -> Text+header = height 32++{-| Sets the size of a piece of text. -}+height :: Double -> Text -> Text+height size txt = txt { fontSize = size }
FRP/Helm/Window.hs view
@@ -5,17 +5,18 @@ ) where  import Control.Applicative+import Control.Arrow import FRP.Elerea.Simple import qualified Graphics.UI.SDL as SDL --- |The current dimensions of the window.+{-| The current dimensions of the window. -} dimensions :: SignalGen (Signal (Int, Int))-dimensions = effectful $ (\s -> (SDL.surfaceGetWidth s, SDL.surfaceGetHeight s)) <$> SDL.getVideoSurface+dimensions = effectful $ (SDL.surfaceGetWidth &&& SDL.surfaceGetHeight) <$> SDL.getVideoSurface --- |The current width of the window.+{-| The current width of the window. -} width :: SignalGen (Signal Int) width = effectful $ SDL.surfaceGetWidth <$> SDL.getVideoSurface --- |The current height of the window.+{-| The current height of the window. -} height :: SignalGen (Signal Int) height = effectful $ SDL.surfaceGetHeight <$> SDL.getVideoSurface
README.md view
@@ -3,10 +3,10 @@ ## Introduction  Helm is a functionally reactive game engine written in Haskell and built around-the [Elerea](https://github.com/cobbpg/elerea) FRP framework. Helm is+the [Elerea FRP framework](https://github.com/cobbpg/elerea). Helm is heavily inspired by the [Elm programming language](http://elm-lang.org) (especially the API). All rendering is done through a vector-graphics based API. At the core, Helm is-built on SDL and the Cairo vector graphics library. This may change to a more+built on SDL and the Cairo vector graphics library. The plan is to change to a more robust setup in the future, such as a lightweight homebrewed renderer built on OpenGL. But for now, Cairo performs pretty well. @@ -18,45 +18,66 @@ ## Features  * Allows you to express game logic dependent on input in a straightforward manner,-  treating events as (almost) first class objects (the essence of FRP).+  treating events as first class values (the essence of FRP).+ * Vector graphics based rendering, allow you to either write art   designed for any resolution or still load generic images and render   those as you would with any pixel-based direct blitting game engine.-* Straightforward API heavily inspired by the Elm programming language. -## Future Work+* Straightforward API heavily inspired by the Elm programming language. The API+  is broken up into the following areas: -* Improve the API. There's a few API calls from Elm that would work-  just as nicely in Helm. These are marked inside TODOs in the code.-  There also other important things that it's missing,-  such as audio, text rendering, joysticks and loading a larger range of-  image formats.-* Backend wise, it would be nice to use OpenGL instead of Cairo.-  Cairo isn't particuarly that well performing for graphic intensive games,-  although work is done being towards to fix that. However, using-  OpenGL would make the engine more lightweight, easier to port-  and be incredibly easier to accelerate. This means I have-  to write the full vector graphics stack myself, but the worse part-  will probably just be line styles, the rest should be moderately easy.-  This will also allow loading of multiple image formats, as the current-  reason for not using SDL_image is that it's annoying as fuck-  to integrate with Cairo.-* Optimizations and testing. This is the first release of the engine so-  obviously little testing or optimizations have been done.-  It's a little hard to set up a test framework for a game engine,-  but I have a few ideas, such as writing a dummy version of the backend-  that simply renders to a PNG file that is fed fake (but predictable) input,-  which is then compared to a static PNG file to see if the final expected-  rendering outcome was achieved.-* Port and support multiple platforms. I've only been testing it on-  Linux, but there's really no reason that it wouldn't work out of the box-  on Windows or OSX after setting up the dependencies. But I'd definitely-  also like to investigate Android and iOS.+  * `FRP.Helm` contains the main code for interfacing with the game engine but+    also includes some utility functions and the modules `FRP.Helm.Color` and `FRP.Helm.Graphics`+    in the style of a sort of prelude library, allowing it to be included and readily+    make the most basic of games. +  * `FRP.Helm.Automaton` contains the `Automaton` data structure and functions+    for composing, creating and calculating them. Automatons are a useful+    abstraction of a dynamic process that is fed input from a signal+    and feeds output through a signal. This is really useful for things+    like animation systems, accumulating network packets and other+    stateful but input dependent things.++  * `FRP.Helm.Color` contains the `Color` data structure, functions for composing+    colors and a few pre-defined colors that are usually used in games.++  * `FRP.Helm.Graphics` contains all the graphics data structures, functions+    for composing these structures and other general graphical utilities.++  * `FRP.Helm.Joystick` contains signals for working with joystick state.++  * `FRP.Helm.Keyboard` contains signals for working with keyboard state.++  * `FRP.Helm.Mouse` contains signals for working with mouse state.++  * `FRP.Helm.Text` contains functions for composing text, formatting it+    and then turning it into an element.++  * `FRP.Helm.Window` contains signals for working with the game window state.+ ## Example -The following examples is the barebones of a game. It shows how to create-an accumulated state that depends on the values sampled from signals (e.g. mouse input and such).+The simplest example of a Helm game that doesn't require any input from the user is the following:++```haskell+import FRP.Helm+import qualified FRP.Helm.Window as Window++render :: (Int, Int) -> Element+render (w, h) = collage w h [move (100, 100) $ filled red $ square 64]++main :: IO ()+main = run $ do+  dims <- Window.dimensions++  return $ fmap render dims+```++It renders a red square at the position `(100, 100)` with a side length of 64px.  +  +The next example is the barebones of a game that depends on input. It shows how to create+an accumulated state that depends on the values sampled from signals (e.g. mouse input). You should see a white square on the screen and pressing the arrow keys allows you to move it.  ```haskell@@ -71,7 +92,8 @@ data State = State { mx :: Double, my :: Double }  step :: (Int, Int) -> State -> State-step (dx, dy) state = state { mx = (realToFrac dx) + mx state, my = (realToFrac dy) + my state }+step (dx, dy) state = state { mx = (realToFrac dx) + mx state,+                              my = (realToFrac dy) + my state }  render :: (Int, Int) -> State -> Element render (w, h) (State { .. }) = collage w h [move (mx, my) $ filled white $ square 100]@@ -106,3 +128,51 @@ ## License  Helm is licensed under the MIT license. See the `LICENSE` file for more details.++## Contributing++Helm would benefit from either of the following contributions:++1. Try out the engine, reporting any issues or suggestions you have.++2. Look through the source, get a feel for the code and then+   contribute some features or fixes. If you plan on contributing+   code please submit a pull request and follow the formatting+   styles set out in the current code: 2 space indents, documentation+   on every top-level function, favouring monad operators over+   do blocks, etc.++The following is a list of areas I want to tackle in the future, +and possible targets that others could try for:++* Improve the API. There's a few API calls from Elm that would work+  just as nicely in Helm. These are marked inside TODOs in the code.+  There also other important things that it's missing,+  such as audio, joysticks and loading a larger range of+  image formats.++* Backend wise, it would be nice to use OpenGL instead of Cairo.+  Cairo isn't particuarly that well performing for graphic intensive games,+  although work is done being towards to fix that. However, using+  OpenGL would make the engine more lightweight, easier to port+  and be incredibly easier to accelerate. This means I have+  to write the full vector graphics stack myself, but the worse part+  will probably just be line styles, the rest should be moderately easy.+  This will also allow loading of multiple image formats, as the current+  reason for not using SDL_image is that it's annoying as fuck+  to integrate with Cairo. Helm also currently uses the Cairo toy text+  API for rendering, which isn't suppose to be used in production. If switched+  to OpenGL, SDL_ttf would be a better fit.++* Optimizations and testing. This is a early release of the engine so+  obviously little testing or optimizations have been done.+  It's a little hard to set up a test framework for a game engine,+  but I have a few ideas, such as writing a dummy version of the backend+  that simply renders to a PNG file that is fed fake (but predictable) input,+  which is then compared to a static PNG file to see if the final expected+  rendering outcome was achieved.++* Port and support multiple platforms. I've only been testing it on+  Linux, but there's really no reason that it wouldn't work out of the box+  on Windows or OSX after setting up the dependencies. But I'd definitely+  also like to investigate Android and iOS.
helm.cabal view
@@ -1,5 +1,5 @@ name: helm-version: 0.2.0+version: 0.3.0 synopsis: A functionally reactive game engine. description: A functionally reactive game engine, with headgear to protect you              from the headache of game development provided.@@ -12,7 +12,7 @@ author: Zack Corr maintainer: Zack Corr <zack@z0w0.me> copyright: (c) 2013, Zack Corr-category: Game Engine+category: Game Engine, FRP build-type: Simple cabal-version: >=1.10 @@ -23,10 +23,13 @@ library   exposed-modules:     FRP.Helm+    FRP.Helm.Automaton     FRP.Helm.Color     FRP.Helm.Graphics+    FRP.Helm.Joystick     FRP.Helm.Keyboard     FRP.Helm.Mouse+    FRP.Helm.Text     FRP.Helm.Window   build-depends:     base >= 4 && < 5,