spade-0.1.0.9: src/Interpreter/Lib/SDL.hs
module Interpreter.Lib.SDL where
import qualified Data.Text as T
import Control.Monad.IO.Class
import Control.Monad.Loops
import Control.Monad.State.Strict
import qualified Data.ByteString as BS
import System.Process (createPipe)
import Data.Coerce
import Data.IORef
import Data.Int (Int16, Int32)
import qualified Data.List as DL
import qualified Data.Map as M
import Data.Maybe
import Data.WAVE
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import Data.Word
import Foreign.C.Types
import SDL hiding (Keycode, Scancode, get)
import qualified SDL
import SDL.Mixer as SDLM
import qualified SDL.Primitive as SDLP
import qualified SDL.Font as SDLF
import Interpreter.Common
import Interpreter.Interpreter
import qualified Interpreter.Lib.Fonts as Fonts
makeSinWaveChunk :: Double -> BS.ByteString
makeSinWaveChunk freq = BS.pack $
fmap (\n ->
let t = fromIntegral n / 22050 :: Double
in round $ 127 + (127 * sin (2 * pi * freq * t)))
[0 :: Int32 .. 22050]
createGraphicsWindow :: BuiltInFnWithDoc ['("width", Int), '("height", Int), '("accelerated", Maybe Bool)]
createGraphicsWindow ((coerce -> w) :> (coerce -> h) :> (coerce -> maccelerated) :>_) =
initGraphics (Just (w, h)) False (fromMaybe False maccelerated)
createGraphicsFullscrenRes :: BuiltInFnWithDoc ['("width", Int), '("height", Int), '("accelerated", Maybe Bool)]
createGraphicsFullscrenRes ((coerce -> w) :> (coerce -> h) :> (coerce -> maccelerated) :>_) =
initGraphics (Just (w, h)) True (fromMaybe False maccelerated)
createGraphicsFullscreen :: BuiltInFnWithDoc '[ '("accelerated", Maybe Bool)]
createGraphicsFullscreen ((coerce -> maccelerated) :>_) =
initGraphics Nothing True (fromMaybe False maccelerated)
initGraphics :: Maybe (Int, Int) -> Bool -> Bool -> InterpretM (Maybe Value)
initGraphics md isfulscren acc = do
let windowName = "S.P.A.D.E Program"
(renderer, window) <- liftIO $ do
SDL.initialize [SDL.InitVideo, SDL.InitAudio, SDL.InitEvents, SDL.InitTimer]
SDLF.initialize
SDLM.openAudio SDLM.defaultAudio 256
window <- if isfulscren
then case md of
Just (w, h) -> SDL.createWindow windowName (fullscreenConfigRes (fromIntegral w) (fromIntegral h))
Nothing -> SDL.createWindow windowName fullscreenConfig
else case md of
Just (w, h) -> SDL.createWindow windowName (windowConfig w h)
Nothing -> SDL.createWindow windowName (windowConfig 800 600)
renderer <- case acc of
True -> SDL.createRenderer window (-1) SDL.defaultRenderer
_ -> SDL.createRenderer window (-1) $ SDL.defaultRenderer { rendererType = SoftwareRenderer }
SDL.rendererDrawColor renderer $= V4 255 255 255 255
pure (renderer, window)
sdlWindowRefs <- isSDLWindows <$> getInterpretM
liftIO $ modifyIORef sdlWindowRefs (\l -> (window : l))
font <- SDLF.decode Fonts.defaultFont 10
modifyInterpretM (\x -> x
{ isDefaultWindow = Just window
, isDefaultRenderer = Just renderer
, isAccelerated = Just acc
, isDefaultFont = Just font
})
pure $ Just $ SDLValue $ Renderer renderer
fullscreenConfig :: SDL.WindowConfig
fullscreenConfig = SDL.defaultWindow
{ windowHighDPI = False
, windowMode = FullscreenDesktop
}
fullscreenConfigRes :: CInt -> CInt -> SDL.WindowConfig
fullscreenConfigRes x y = SDL.defaultWindow
{ windowHighDPI = False
, windowMode = FullscreenDesktop
, windowInitialSize = V2 x y
}
windowConfig :: Int -> Int -> SDL.WindowConfig
windowConfig w h = SDL.defaultWindow
{ windowHighDPI = False
, windowInitialSize = (SDL.V2 (fromIntegral w) (fromIntegral h))
, windowMode = Windowed
}
setLogicalSize :: BuiltInFnWithDoc ['("x", CInt), '("y", CInt)]
setLogicalSize ((coerce -> (lx :: CInt)) :> (coerce -> (ly :: CInt)) :> _) =
isDefaultRenderer <$> getInterpretM >>= \case
Just renderer -> do
SDL.V2 x y <- getWindowSize'
SDL.rendererScale renderer SDL.$= (SDL.V2 (realToFrac x/realToFrac lx) (realToFrac y/realToFrac ly))
pure Nothing
Nothing -> throwErr $ SDLError "Graphics not Initialized"
createTextTexture :: BuiltInFnWithDoc '[ '("content", T.Text)]
createTextTexture ((coerce -> (content :: T.Text)) :> _) = do
isDefaultRenderer <$> getInterpretM >>= \case
Just renderer -> do
(V4 r g b a) <- SDL.get (SDL.rendererDrawColor renderer)
(Just . SDLValue . Texture) <$> createTextTexture' (r, g, b, a) content
Nothing -> throwErr $ SDLError "Graphics not Initialized"
loadFont :: BuiltInFnWithDoc ['("pointSize", SDLF.PointSize), '("filePath", Maybe FilePath)]
loadFont ( (coerce -> pointSize) :> (coerce -> mFilePath) :> _) = do
fontSrc <- case mFilePath of
Just filePath -> liftIO $ BS.readFile filePath
Nothing -> pure $ Fonts.defaultFont
font <- SDLF.decode fontSrc pointSize
pure $ Just $ SDLValue $ Font font
setFont :: BuiltInFnWithDoc '[ '("font", Value)]
setFont ((coerce -> fsValue) :> _) = do
case fsValue of
SDLValue (Font f) -> modifyInterpretM (\im -> im { isDefaultFont = Just f })
_ -> throwErr $ SDLError "Font value expected"
pure Nothing
drawTextAt
:: BuiltInFnWithDoc ['("x", CInt), '("y", CInt), '("content", T.Text)]
drawTextAt ((coerce -> x) :> (coerce -> y) :> (coerce -> (content :: T.Text)) :> _) = do
drawTextAt' x y 0 content
pure Nothing
drawTextAtRotated
:: BuiltInFnWithDoc ['("x", CInt), '("y", CInt), '("angle", CDouble), '("content", T.Text)]
drawTextAtRotated ((coerce -> x) :> (coerce -> y) :> (coerce -> rotAng) :> (coerce -> (content :: T.Text)) :> _) = do
drawTextAt' x y rotAng content
pure Nothing
drawTextAt' :: CInt -> CInt -> CDouble -> T.Text -> InterpretM ()
drawTextAt' x y rotAng content = do
renderer <- getDefaultRenderer
(V4 r g b a) <- SDL.get (SDL.rendererDrawColor renderer)
texture <- createTextTexture' (r, g, b, a) content
ti <- SDL.queryTexture texture
SDL.copyEx renderer texture Nothing (Just $ SDL.Rectangle (SDL.P (SDL.V2 x y)) (SDL.V2 (SDL.textureWidth ti) (SDL.textureHeight ti))) rotAng (Just $ mkPoint 0 (SDL.textureHeight ti)) (SDL.V2 False False)
drawIfNotAccelerated
createTextTexture' :: (Word8, Word8, Word8, Word8) -> T.Text -> InterpretM SDL.Texture
createTextTexture' (r, g, b, a) t = do
renderer <- getDefaultRenderer
isDefaultFont <$> getInterpretM >>= \case
Just font -> do
texture <- SDLF.solid font (V4 r g b a) t >>= SDL.createTextureFromSurface renderer
pure texture
Nothing -> throwErr $ SDLError "Font not set"
loadTexture' :: FilePath -> InterpretM Value
loadTexture' fp = do
renderer <- getDefaultRenderer
surface <- SDL.loadBMP fp
texture <- SDL.createTextureFromSurface renderer surface
pure $ SDLValue $ Texture texture
textureInfo' :: SDL.Texture -> InterpretM (CInt, CInt)
textureInfo' texture = do
ti <- SDL.queryTexture texture
pure (SDL.textureWidth ti, SDL.textureHeight ti)
copyTexture' :: SDL.Texture -> CInt -> CInt -> CInt -> CInt -> InterpretM ()
copyTexture' texture x y w h = do
renderer <- getDefaultRenderer
SDL.copy renderer texture Nothing (Just $ SDL.Rectangle (SDL.P (SDL.V2 x y)) (SDL.V2 w h))
drawIfNotAccelerated
copyTexturePart' :: SDL.Texture -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> InterpretM ()
copyTexturePart' texture sx sy sw sh x y w h = do
renderer <- getDefaultRenderer
SDL.copy renderer texture
(Just $ SDL.Rectangle (SDL.P (SDL.V2 sx sy)) (SDL.V2 sw sh))
(Just $ SDL.Rectangle (SDL.P (SDL.V2 x y)) (SDL.V2 w h))
drawIfNotAccelerated
copyTextureRotated'
:: SDL.Texture
-> CInt
-> CInt
-> CInt
-> CInt
-> CInt
-> CInt
-> CInt
-> CInt
-> CDouble
-> CInt
-> CInt
-> Bool
-> Bool
-> InterpretM ()
copyTextureRotated' texture sx sy sw sh x y w h rotationDeg rx ry fx fy = do
renderer <- getDefaultRenderer
SDL.copyEx renderer texture
(Just $ SDL.Rectangle (SDL.P (SDL.V2 sx sy)) (SDL.V2 sw sh))
(Just $ SDL.Rectangle (SDL.P (SDL.V2 x y)) (SDL.V2 w h))
rotationDeg
(Just $ mkPoint rx ry)
(SDL.V2 fx fy)
drawIfNotAccelerated
loadTexture :: BuiltInFnWithDoc '[ '("bmp_file", FilePath)]
loadTexture ((coerce -> fp) :> EmptyArgs) =
Just <$> loadTexture' fp
destroyTexture :: BuiltInFnWithDoc '[ '("texture", SDL.Texture)]
destroyTexture ((coerce -> texture) :> EmptyArgs) = do
SDL.destroyTexture texture
pure Nothing
textureInfo :: BuiltInFnWithDoc
'[ '("texture", SDL.Texture)
]
textureInfo
( (coerce -> texture) :> EmptyArgs) = do
(w, h) <- textureInfo' texture
pure $ Just $ ObjectValue $ M.fromList
[ ("width", NumberValue $ NumberInt $ fromIntegral w)
, ("height", NumberValue $ NumberInt $ fromIntegral h)
]
copyTexture :: BuiltInFnWithDoc
'[ '("texture", SDL.Texture)
, '("dst_x", CInt)
, '("dst_y", CInt)
, '("dst_w", CInt)
, '("dst_h", CInt)
]
copyTexture
( (coerce -> texture)
:> (coerce -> dx)
:> (coerce -> dy)
:> (coerce -> dh)
:> (coerce -> dw)
:> EmptyArgs) = do
copyTexture' texture dx dy dh dw
pure Nothing
copyTexturePart :: BuiltInFnWithDoc
'[ '("texture", SDL.Texture)
, '("src_x", CInt)
, '("src_y", CInt)
, '("src_w", CInt)
, '("src_h", CInt)
, '("dst_x", CInt)
, '("dst_y", CInt)
, '("dst_w", CInt)
, '("dst_h", CInt)
]
copyTexturePart
( (coerce -> texture)
:> (coerce -> sx)
:> (coerce -> sy)
:> (coerce -> sh)
:> (coerce -> sw)
:> (coerce -> dx)
:> (coerce -> dy)
:> (coerce -> dh)
:> (coerce -> dw)
:> EmptyArgs) = do
copyTexturePart' texture sx sy sh sw dx dy dh dw
pure Nothing
copyTextureRotated :: BuiltInFnWithDoc
'[ '("texture", SDL.Texture)
, '("src_x", CInt)
, '("src_y", CInt)
, '("src_w", CInt)
, '("src_h", CInt)
, '("dst_x", CInt)
, '("dst_y", CInt)
, '("dst_w", CInt)
, '("dst_h", CInt)
, '("rotation_deg", CDouble)
, '("rotation_x", CInt)
, '("rotation_y", CInt)
, '("flipx", Bool)
, '("flipy", Bool)
]
copyTextureRotated
( (coerce -> texture)
:> (coerce -> sx)
:> (coerce -> sy)
:> (coerce -> sh)
:> (coerce -> sw)
:> (coerce -> dx)
:> (coerce -> dy)
:> (coerce -> dh)
:> (coerce -> dw)
:> (coerce -> rDeg)
:> (coerce -> rx)
:> (coerce -> ry)
:> (coerce -> flipx)
:> (coerce -> flipy)
:> EmptyArgs) = do
copyTextureRotated' texture sx sy sh sw dx dy dh dw rDeg rx ry flipx flipy
pure Nothing
getDefaultWindow :: InterpretM SDL.Window
getDefaultWindow = isDefaultWindow <$> getInterpretM >>= \case
Just x -> pure x
Nothing -> throwErr $ SDLError "Graphics not Initialized"
getDefaultRenderer :: InterpretM SDL.Renderer
getDefaultRenderer = isDefaultRenderer <$> getInterpretM >>= \case
Just x -> pure x
Nothing -> throwErr $ SDLError "Graphics not Initialized"
draw :: BuiltInFnWithDoc '[]
draw _ = do
draw'
pure Nothing
draw' :: InterpretM ()
draw' = getDefaultRenderer >>= SDL.present
drawIfNotAccelerated :: InterpretM ()
drawIfNotAccelerated = (isAccelerated <$> getInterpretM) >>= \case
(Just False) -> draw'
_ -> pure ()
setDrawColor :: BuiltInFnWithDoc '[ '("red_component", Word8), '("green_component", Word8), '("blue_component", Word8)]
setDrawColor ((coerce -> r) :> (coerce -> g) :> (coerce -> b) :> _) = do
setDrawColor' r g b 255
pure Nothing
setBgColor :: BuiltInFnWithDoc '[ '("red_component", Word8), '("green_component", Word8), '("blue_component", Word8)]
setBgColor ((coerce -> r) :> (coerce -> g) :> (coerce -> b) :> _) = do
setBgColor' r g b 255
pure Nothing
setDrawColorAlpha :: BuiltInFnWithDoc '[ '("red_component", Word8), '("green_component", Word8), '("blue_component", Word8), '("transparency", Word8)]
setDrawColorAlpha ((coerce -> r) :> (coerce -> g) :> (coerce -> b) :> (coerce -> t) :> _) = do
setDrawColor' r g b t
pure Nothing
setDrawColor' :: Word8 -> Word8 -> Word8 -> Word8 -> InterpretM ()
setDrawColor' r g b a = do
getDefaultRenderer >>= \renderer -> do
SDL.rendererDrawColor renderer $= V4 r g b a
setBgColor' :: Word8 -> Word8 -> Word8 -> Word8 -> InterpretM ()
setBgColor' r g b a = modifyInterpretM (\im -> im { isBgColor = Just (r, g, b, a) })
clear :: BuiltInFnWithDoc '[]
clear _ =
getDefaultRenderer >>= \renderer -> do
isBgColor <$> getInterpretM >>= \case
Just (r, g, b, a) -> do
(V4 r' g' b' a') <- SDL.get (SDL.rendererDrawColor renderer)
SDL.rendererDrawColor renderer $= V4 r g b a
SDL.clear renderer
SDL.rendererDrawColor renderer $= V4 r' g' b' a'
drawIfNotAccelerated
Nothing -> throwErr $ SDLError "Background color not set"
pure Nothing
drawPoint :: BuiltInFnWithDoc ['("x", CInt), '("y", CInt)]
drawPoint ((coerce -> x) :> (coerce -> y) :> _) = do
renderer <- getDefaultRenderer
SDL.drawPoint renderer (mkPoint x y)
drawIfNotAccelerated
pure Nothing
drawPoints :: BuiltInFnWithDoc '[ '("points", VS.Vector (SDL.Point V2 CInt))]
drawPoints ((coerce -> v) :> _) = do
renderer <- getDefaultRenderer
SDL.drawPoints renderer v
drawIfNotAccelerated
pure Nothing
drawLines :: BuiltInFnWithDoc '[ '("points", VS.Vector (SDL.Point V2 CInt))]
drawLines ((coerce -> v) :> _) = do
drawLines' v
pure Nothing
drawLines' :: VS.Vector (SDL.Point V2 CInt) -> InterpretM ()
drawLines' v = do
renderer <- getDefaultRenderer
SDL.drawLines renderer v
if VS.length v > 0 then do
let last' = VS.last v
SDL.drawPoint renderer last'
SDL.drawPoint renderer last'
else pure ()
drawIfNotAccelerated
v2ToTuple :: Point V2 a -> (a, a)
v2ToTuple (P (V2 a b)) = (a, b)
v2Fst :: Point V2 a -> a
v2Fst (P (V2 a _)) = a
v2Snd :: Point V2 a -> a
v2Snd (P (V2 _ a)) = a
drawPoly :: BuiltInFnWithDoc '[ '("points", VS.Vector (SDL.Point V2 Int16)), '("fill", Maybe Bool)]
drawPoly ((coerce -> v) :> (coerce -> f) :> EmptyArgs) = do
drawPoly' v False f
pure Nothing
drawPolySmooth :: BuiltInFnWithDoc '[ '("points", VS.Vector (SDL.Point V2 Int16))]
drawPolySmooth ((coerce -> v) :> EmptyArgs) = do
drawPoly' v True Nothing
pure Nothing
drawPoly' :: VS.Vector (SDL.Point V2 Int16) -> Bool -> Maybe Bool -> InterpretM ()
drawPoly' v smth f = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
if (fromMaybe False f)
then SDLP.fillPolygon renderer (toSingle (\(P (V2 a _)) -> a) v) (toSingle (\(P (V2 _ b)) -> b) v) dcl
else if smth
then SDLP.smoothPolygon renderer (toSingle (\(P (V2 a _)) -> a) v) (toSingle (\(P (V2 _ b)) -> b) v) dcl
else SDLP.polygon renderer (toSingle (\(P (V2 a _)) -> a) v) (toSingle (\(P (V2 _ b)) -> b) v) dcl
drawIfNotAccelerated
where
toSingle :: (SDL.Point V2 Int16 -> Int16) -> VS.Vector (SDL.Point V2 Int16) -> VS.Vector Int16
toSingle fn vec = VS.map fn vec
drawLine :: BuiltInFnWithDoc ['("start_x", CInt), '("start_y", CInt), '("end_x", CInt), '("end_y", CInt)]
drawLine ((coerce -> x) :> (coerce -> y) :> (coerce -> xEnd) :> (coerce -> yEnd) :>_) = do
renderer <- getDefaultRenderer
let endpoint = mkPoint xEnd yEnd
SDL.drawLine renderer (mkPoint x y) endpoint
SDL.drawPoint renderer endpoint
SDL.drawPoint renderer endpoint
-- Due to some bug in SDL, without these extra call
-- the next draw item appear to have a stray pixel with the same color
-- that was used to draw this one.
drawIfNotAccelerated
pure Nothing
drawBox :: BuiltInFnWithDoc ['("start_x", CInt), '("start_y", CInt), '("width", CInt), '("height", CInt), '("fill", Maybe Bool)]
drawBox ((coerce -> x) :> (coerce -> y) :> (coerce -> width) :> (coerce -> height) :> (coerce -> fill) :> _) = do
renderer <- getDefaultRenderer
let
f = case fill of
Just b -> b
Nothing -> False
if f
then SDL.fillRect renderer $ Just $ SDL.Rectangle (mkPoint x y) (SDL.V2 width height)
else SDL.drawRect renderer $ Just $ SDL.Rectangle (mkPoint x y) (SDL.V2 width height)
drawIfNotAccelerated
pure Nothing
drawArc :: BuiltInFnWithDoc ['("center_x", CInt), '("center_y", CInt), '("radius", CInt), '("angle_start", CInt), '("angle_end", CInt)]
drawArc ((coerce -> x) :> (coerce -> y) :> (coerce -> radius) :> (coerce -> angles) :> (coerce -> anglee) :> _) = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
SDLP.arc renderer (SDL.V2 x y) radius angles anglee dcl
drawIfNotAccelerated
pure Nothing
drawPie :: BuiltInFnWithDoc ['("center_x", CInt), '("center_y", CInt), '("radius", CInt), '("angle_start", CInt), '("angle_end", CInt), '("fill", Maybe Bool)]
drawPie ((coerce -> x) :> (coerce -> y) :> (coerce -> radius) :> (coerce -> angles) :> (coerce -> anglee) :> (coerce -> (f :: Maybe Bool)) :> _) = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
case f of
Just True -> SDLP.fillPie renderer (SDL.V2 x y) radius angles anglee dcl
_ -> SDLP.pie renderer (SDL.V2 x y) radius angles anglee dcl
drawIfNotAccelerated
pure Nothing
drawCircle :: BuiltInFnWithDoc ['("center_x", CInt), '("center_y", CInt), '("radius", CInt), '("fill", Maybe Bool)]
drawCircle ((coerce -> x) :> (coerce -> y) :> (coerce -> radius) :> (coerce -> (f :: Maybe Bool)) :> _) = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
case f of
Just True -> SDLP.fillCircle renderer (SDL.V2 x y) radius dcl
_ -> SDLP.circle renderer (SDL.V2 x y) radius dcl
drawIfNotAccelerated
pure Nothing
drawEllipse :: BuiltInFnWithDoc ['("center_x", CInt), '("center_y", CInt), '("radiusx", CInt), '("radiusy", CInt), '("fill", Maybe Bool)]
drawEllipse ((coerce -> x) :> (coerce -> y) :> (coerce -> radiusx) :> (coerce -> radiusy) :> (coerce -> (f :: Maybe Bool)) :> _) = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
case f of
Just True -> SDLP.fillEllipse renderer (SDL.V2 x y) radiusx radiusy dcl
_ -> SDLP.ellipse renderer (SDL.V2 x y) radiusx radiusy dcl
drawIfNotAccelerated
pure Nothing
drawSmoothEllipse :: BuiltInFnWithDoc ['("center_x", CInt), '("center_y", CInt), '("radiusx", CInt), '("radiusy", CInt)]
drawSmoothEllipse ((coerce -> x) :> (coerce -> y) :> (coerce -> radiusx) :> (coerce -> radiusy) :> _) = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
SDLP.smoothEllipse renderer (SDL.V2 x y) radiusx radiusy dcl
drawIfNotAccelerated
pure Nothing
drawSmoothCircle :: BuiltInFnWithDoc ['("center_x", CInt), '("center_y", CInt), '("radius", CInt)]
drawSmoothCircle ((coerce -> x) :> (coerce -> y) :> (coerce -> radius) :> _) = do
renderer <- getDefaultRenderer
dcl <- SDL.get (SDL.rendererDrawColor renderer)
SDLP.smoothCircle renderer (SDL.V2 x y) radius dcl
drawIfNotAccelerated
pure Nothing
waitForSDLKey :: BuiltInFnWithDoc '[]
waitForSDLKey _ = do
mv <- iterateWhile isNothing $ do
events <- (filter filterEvent) <$> pollEvents
case events of
[] -> pure Nothing
(h:_) -> pure $ convertEvent h
pure mv
where
convertEvent :: Event -> Maybe Value
convertEvent event =
case eventPayload event of
KeyboardEvent keyboardEvent ->
Just $ SDLValue $ Keycode ((keysymKeycode (keyboardEventKeysym keyboardEvent)))
_ -> Nothing
filterEvent :: Event -> Bool
filterEvent event =
case eventPayload event of
KeyboardEvent keyboardEvent ->
(keyboardEventKeyMotion keyboardEvent == Pressed)
_ -> False
getWindowSize' :: InterpretM (SDL.V2 CInt)
getWindowSize' = do
window <- getDefaultWindow
liftIO $ SDL.get (windowSize window)
getWindowSize :: BuiltInFnWithDoc '[]
getWindowSize _ = do
(SDL.V2 x y) <- getWindowSize'
pure $ Just $ ObjectValue $ M.fromList [("width", NumberValue $ NumberInt $ fromIntegral x), ("height", NumberValue $ NumberInt $ fromIntegral y)]
getKeyboardState :: BuiltInFnWithDoc '[]
getKeyboardState _ = do
SDL.pumpEvents
fn <- SDL.getKeyboardState
pure $ Just $ SDLValue $ KeyboardState $ SDLKeyboardStateCallback fn
wasKeyDownIn :: BuiltInFnWithDoc '[ '("keyboard_state", SDLKeyboardStateCallback), '("key", SDL.Scancode) ]
wasKeyDownIn ((coerce -> (SDLKeyboardStateCallback cb)) :> (coerce -> scancode ) :> _) =
pure $ Just $ BoolValue $ cb scancode
getKeys :: BuiltInFnWithDoc '[]
getKeys _ = do
events <- pollEvents
pure $ Just $ ArrayValue $ DL.foldl' convertEvent V.empty events
where
convertEvent :: V.Vector Value -> Event -> V.Vector Value
convertEvent inp event =
case eventPayload event of
KeyboardEvent keyboardEvent ->
V.cons (SDLValue $ Keycode ((keysymKeycode (keyboardEventKeysym keyboardEvent)))) inp
_ -> inp
builtInSetSampleVolume :: BuiltInFnWithDoc '[ '("channel", Channel), '("volume", Int)]
builtInSetSampleVolume ((coerce -> (channel :: Channel)) :> (coerce -> volume) :> _) = do
SDLM.setVolume volume channel
pure Nothing
builtInSetSampleLRVolume :: BuiltInFnWithDoc '[ '("sample", Channel), '("volume_left", Int), '("volume_right", Int)]
builtInSetSampleLRVolume ((coerce -> (channel :: Channel)) :> (coerce -> volumel) :> (coerce -> volumer) :> _) = do
void $ SDLM.effectPan channel volumel volumer
pure Nothing
builtInPlaySoundSample :: BuiltInFnWithDoc '[ '("sample", Sample), '("channel", Int)]
builtInPlaySoundSample ((coerce -> sample) :> (coerce -> (channel :: Int)) :> _) = do
void $ SDLM.playOn (fromIntegral channel) SDLM.Forever sample
pure Nothing
makeSound :: (Int, [WAVESample]) -> InterpretM Value
makeSound (sampleCount, samplesRaw) = do
(rEnd, wEnd) <- liftIO createPipe
let waveData = WAVE (WAVEHeader 1 44100 16 $ Just sampleCount) [samplesRaw]
waveEncodedData <- liftIO $ do
hPutWAVE wEnd waveData
BS.hGetContents rEnd
chunk <- liftIO $ SDLM.decode waveEncodedData
pure $ SDLValue $ SoundSample chunk
builtInMakeTone :: BuiltInFnWithDoc '[ '("freq", Double) ]
builtInMakeTone ((coerce -> freq) :> _) = do
let samplesInOneCycle = 44100 / freq
let multiplier = (2 * pi)/samplesInOneCycle
let samplesRaw = [doubleToSample $ sin (realToFrac x * multiplier) | x <- [0 .. (round samplesInOneCycle - 1)]]
Just <$> makeSound (round samplesInOneCycle, samplesRaw)
builtInMakeSoundSample :: BuiltInFnWithDoc '[ '("samplecount", Int), '("callback", Callback)]
builtInMakeSoundSample ((coerce -> sampleCount) :> (coerce -> (cb :: Callback)) :> _) = do
samplesRaw <- getSamples sampleCount
Just <$> makeSound (sampleCount , samplesRaw)
where
getSamples :: Int -> InterpretM [WAVESample]
getSamples sc = mapM (\x -> mapFn x) [1..(fromIntegral sc)]
mapFn :: Integer -> InterpretM WAVESample
mapFn si = (doubleToSample . (fromValue @Double) . fromMaybe (throwErr MissingProcedureReturn)) <$>
evaluateCallback cb [NumberValue $ NumberInt si]
builtInMakeSoundSampleFromFile :: BuiltInFnWithDoc '[ '("filepath", FilePath)]
builtInMakeSoundSampleFromFile ((coerce -> filePath) :> _) = do
chunk <- liftIO $ SDLM.load filePath
pure $ Just $ SDLValue $ SoundSample chunk
cleanupSDL :: InterpretM ()
cleanupSDL = do
sdlWindowRefs <- isSDLWindows <$> getInterpretM
windows <- liftIO $ readIORef sdlWindowRefs
mapM_ (liftIO . SDL.destroyWindow) windows
modifyInterpretM (\x -> x { isDefaultRenderer = Nothing })
modifyInterpretM (\x -> x { isDefaultWindow = Nothing })
SDLM.closeAudio
SDL.quit
keycodes :: Value
keycodes = ObjectValue $ M.fromList
[ ("up", SDLValue $ Keycode KeycodeUp)
, ("down", SDLValue $ Keycode KeycodeDown)
, ("left", SDLValue $ Keycode KeycodeLeft)
, ("right", SDLValue $ Keycode KeycodeRight)
, ("a", SDLValue $ Keycode KeycodeA)
, ("b", SDLValue $ Keycode KeycodeB)
, ("c", SDLValue $ Keycode KeycodeC)
, ("d", SDLValue $ Keycode KeycodeD)
, ("e", SDLValue $ Keycode KeycodeE)
, ("f", SDLValue $ Keycode KeycodeF)
, ("g", SDLValue $ Keycode KeycodeG)
, ("h", SDLValue $ Keycode KeycodeH)
, ("i", SDLValue $ Keycode KeycodeI)
, ("j", SDLValue $ Keycode KeycodeJ)
, ("k", SDLValue $ Keycode KeycodeK)
, ("l", SDLValue $ Keycode KeycodeL)
, ("m", SDLValue $ Keycode KeycodeM)
, ("n", SDLValue $ Keycode KeycodeN)
, ("o", SDLValue $ Keycode KeycodeO)
, ("p", SDLValue $ Keycode KeycodeP)
, ("q", SDLValue $ Keycode KeycodeQ)
, ("r", SDLValue $ Keycode KeycodeR)
, ("s", SDLValue $ Keycode KeycodeS)
, ("t", SDLValue $ Keycode KeycodeT)
, ("u", SDLValue $ Keycode KeycodeU)
, ("v", SDLValue $ Keycode KeycodeV)
, ("w", SDLValue $ Keycode KeycodeW)
, ("x", SDLValue $ Keycode KeycodeX)
, ("y", SDLValue $ Keycode KeycodeY)
, ("z", SDLValue $ Keycode KeycodeZ)
, ("return", SDLValue $ Keycode KeycodeReturn)
, ("escape", SDLValue $ Keycode KeycodeEscape)
]
scancodes :: Value
scancodes = ObjectValue $ M.fromList
[ ("up", SDLValue $ Scancode ScancodeUp)
, ("down", SDLValue $ Scancode ScancodeDown)
, ("left", SDLValue $ Scancode ScancodeLeft)
, ("right", SDLValue $ Scancode ScancodeRight)
, ("a", SDLValue $ Scancode ScancodeA)
, ("b", SDLValue $ Scancode ScancodeB)
, ("c", SDLValue $ Scancode ScancodeC)
, ("d", SDLValue $ Scancode ScancodeD)
, ("e", SDLValue $ Scancode ScancodeE)
, ("f", SDLValue $ Scancode ScancodeF)
, ("g", SDLValue $ Scancode ScancodeG)
, ("h", SDLValue $ Scancode ScancodeH)
, ("i", SDLValue $ Scancode ScancodeI)
, ("j", SDLValue $ Scancode ScancodeJ)
, ("k", SDLValue $ Scancode ScancodeK)
, ("l", SDLValue $ Scancode ScancodeL)
, ("m", SDLValue $ Scancode ScancodeM)
, ("n", SDLValue $ Scancode ScancodeN)
, ("o", SDLValue $ Scancode ScancodeO)
, ("p", SDLValue $ Scancode ScancodeP)
, ("q", SDLValue $ Scancode ScancodeQ)
, ("r", SDLValue $ Scancode ScancodeR)
, ("s", SDLValue $ Scancode ScancodeS)
, ("t", SDLValue $ Scancode ScancodeT)
, ("u", SDLValue $ Scancode ScancodeU)
, ("v", SDLValue $ Scancode ScancodeV)
, ("w", SDLValue $ Scancode ScancodeW)
, ("x", SDLValue $ Scancode ScancodeX)
, ("y", SDLValue $ Scancode ScancodeY)
, ("z", SDLValue $ Scancode ScancodeZ)
]