{-# LANGUAGE RecordWildCards #-} module Affection ( withAffection , getAffection , putAffection -- , withWindow -- , withDefaultWindow , delaySec , get , put , module A ) where import qualified SDL import qualified SDL.Internal.Numbered as SDL (toNumber) import qualified SDL.Raw as Raw import qualified GEGL as G import qualified Data.Text as T import Data.Maybe import Data.IORef import System.Clock import Control.Monad.Loops import Control.Monad.State import Foreign.C.Types (CInt(..)) import Foreign.Storable (peek) import Debug.Trace import Affection.Types as A import Affection.Draw as A import Affection.Particle as A import qualified BABL as B -- | Main function which bootstraps everything else. withAffection :: AffectionConfig us -- ^ Configuration of the Game and its engine. -> IO () withAffection AffectionConfig{..} = do case initComponents of All -> SDL.initializeAll Only is -> SDL.initialize is G.gegl_init execTime <- newIORef =<< getTime Monotonic window <- SDL.createWindow windowTitle windowConfig -- let surface = (flip SDL.Surface Nothing) rawSurfacePtr (oldSurf, surface) <- getSurfaces window let (SDL.Surface ptr _) = surface rawSurfacePtr <- Raw.convertSurfaceFormat ptr (SDL.toNumber SDL.ABGR8888) 0 pixelFormat <- liftIO $ peek . Raw.surfaceFormat =<< peek rawSurfacePtr SDL.V2 (CInt rw) (CInt rh) <- liftIO $ SDL.surfaceDimensions surface let (w, h) = (fromIntegral rw, fromIntegral rh) stride = (fromIntegral $ Raw.pixelFormatBytesPerPixel pixelFormat) * w pixels <- SDL.surfacePixels $ surface let bablFormat = B.PixelFormat B.RGBA B.CFu8 cpp = B.babl_components_per_pixel bablFormat format <- B.babl_format bablFormat initContainer <- (\x -> AffectionData { quitEvent = False , userState = x , drawWindow = window , windowSurface = oldSurf , drawSurface = surface , drawFormat = format , drawPixels = pixels , drawDimensions = (w, h) , drawStride = stride , drawCPP = cpp , drawStack = [] , elapsedTime = 0 }) <$> loadState surface (_, nState) <- runStateT ( A.runState $ do preLoop pad <- get liftIO $ SDL.surfaceBlit (drawSurface pad) Nothing (windowSurface pad) Nothing whileM_ (do current <- get return $ not $ A.quitEvent current ) (do -- Measure time difference form last run now <- liftIO $ getTime Monotonic lastTime <- liftIO $ readIORef execTime -- get state ad <- get -- clean draw requests from last run mapM_ (invalidateDrawRequest (drawPixels ad) (drawStride ad) (drawCPP ad)) (drawStack ad) -- compute dt and update elapsedTime let dt = (fromIntegral $ toNanoSecs $ diffTimeSpec lastTime now) / (fromIntegral 10 ^ 9) put $ ad { drawStack = [] , elapsedTime = elapsedTime ad + dt } -- poll events evs <- preHandleEvents =<< liftIO SDL.pollEvents -- execute user defined update loop updateLoop dt evs -- execute user defined draw loop drawLoop -- handle all new draw requests ad2 <- get clear <- catMaybes <$> mapM (handleDrawRequest (drawPixels ad) (drawStride ad) (drawCPP ad)) (drawStack ad2) -- save all draw requests to clear in next run put $ ad2 { drawStack = clear } -- blit surface and update window liftIO $ SDL.surfaceBlit (drawSurface ad2) Nothing (windowSurface ad2) Nothing liftIO $ SDL.updateWindowSurface $ drawWindow ad2 -- save new time liftIO $ writeIORef execTime $ now ) ) initContainer G.gegl_exit cleanUp $ userState nState SDL.quit getSurfaces :: SDL.Window -> IO (SDL.Surface, SDL.Surface) getSurfaces window = do oldSurf@(SDL.Surface ptr _) <- SDL.getWindowSurface window rawSurfacePtr <- Raw.convertSurfaceFormat ptr (SDL.toNumber SDL.ABGR8888) 0 let surface = (flip SDL.Surface Nothing) rawSurfacePtr return (oldSurf, surface) preHandleEvents :: [SDL.Event] -> Affection us [SDL.Event] preHandleEvents evs = catMaybes <$> mapM handle evs where handle e = case SDL.eventPayload e of SDL.WindowMovedEvent _ -> do liftIO $ traceIO "I was moved" putNewSurface return Nothing _ -> return $ Just e putNewSurface = do ad <- get (oldSurf, surface) <- liftIO $ getSurfaces $ drawWindow ad pixels <- SDL.surfacePixels $ surface SDL.V2 (CInt rw) (CInt rh) <- liftIO $ SDL.surfaceDimensions surface let (SDL.Surface ptr _) = surface rawSurfacePtr <- Raw.convertSurfaceFormat ptr (SDL.toNumber SDL.ABGR8888) 0 pixelFormat <- liftIO $ peek . Raw.surfaceFormat =<< peek rawSurfacePtr let (w, h) = (fromIntegral rw, fromIntegral rh) stride = fromIntegral (Raw.pixelFormatBytesPerPixel pixelFormat) * w put ad { windowSurface = oldSurf , drawSurface = surface , drawPixels = pixels , drawDimensions = (w, h) , drawStride = stride } -- | Return the userstate to the user getAffection :: Affection us us getAffection = do ad <- get return $ userState ad -- | Put altered user state back putAffection :: us -- User state -> Affection us () putAffection us = do ad <- get put $ ad { userState = us } -- | block a thread for a specified amount of time delaySec :: Int -- ^ Number of seconds -> IO () delaySec dur = SDL.delay (fromIntegral $ dur * 1000)