{-| Module : SDL.Framerate Copyright : (c) 2015 Siniša Biđin License : MIT Maintainer : sinisa@bidin.eu Stability : experimental Bindings to @SDL2_gfx@'s framerate management functionality. These functions should allow you to set, manage and query a target application framerate. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} module SDL.Framerate ( Framerate , Manager(..) , with , manager , set , delay , delay_ , minimum , maximum , get , count , destroyManager ) where import Control.Exception.Lifted (bracket) import Control.Monad (void) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Control (MonadBaseControl) import Foreign.Marshal.Alloc (malloc, free) import Foreign.Ptr (Ptr) import Prelude hiding (minimum, maximum) import qualified SDL.Raw.Framerate -- | A framerate manager, counting frames and keeping track of time delays -- necessary to reach a certain target framerate. newtype Manager = Manager (Ptr SDL.Raw.Framerate.Manager) deriving (Eq, Show) -- | A certain number of frames per second. type Framerate = Int -- | Creates a new framerate 'Manager', sets a target 'Framerate' and frees the -- 'Manager' after the inner computation ends. -- -- This is the recommended way to create a 'Manager'. with :: (MonadBaseControl IO m, MonadIO m) => Framerate -> (Manager -> m a) -> m a with fps act = bracket manager destroyManager $ \m -> set m fps >> act m -- | Create a new framerate 'Manager' using the default settings. -- -- You have to take care to call 'destroyManager' yourself. It's recommended to -- use 'with' instead. manager :: MonadIO m => m Manager manager = fmap Manager . liftIO $ do ptr <- malloc SDL.Raw.Framerate.init ptr return ptr -- | The smallest allowed framerate. minimum :: Framerate minimum = SDL.Raw.Framerate.FPS_LOWER_LIMIT -- | The largest allowed framerate. maximum :: Framerate maximum = SDL.Raw.Framerate.FPS_UPPER_LIMIT -- | Set a target framerate and reset delay interpolation. -- -- Note that the given framerate must be within the allowed range -- otherwise -- the minimum or maximum allowed framerate is used instead. set :: MonadIO m => Manager -> Framerate -> m () set (Manager ptr) = void . set' . min maximum . max minimum where set' = SDL.Raw.Framerate.setFramerate ptr . fromIntegral -- | Get the currently set framerate. get :: MonadIO m => Manager -> m Framerate get (Manager ptr) = fromIntegral <$> SDL.Raw.Framerate.getFramerate ptr -- | Returns the framecount. Each time 'delay' is called, a frame is counted. count :: MonadIO m => Manager -> m Int count (Manager ptr) = fromIntegral <$> SDL.Raw.Framerate.getFramecount ptr -- | Generate and apply a delay in order to maintain a constant target -- framerate. -- -- This should be called once per rendering loop. -- -- Delay will automatically be set to zero if the computer cannot keep up (if -- rendering is too slow). Returns the number of milliseconds since the last -- time 'delay' was called (possibly zero). delay :: MonadIO m => Manager -> m Int delay (Manager ptr) = fromIntegral <$> SDL.Raw.Framerate.framerateDelay ptr -- | Same as 'delay', but doesn't return the time since it was last called. delay_ :: MonadIO m => Manager -> m () delay_ = void . delay -- | Frees a framerate manager. Make sure not to use it again after this action. destroyManager :: MonadIO m => Manager -> m () destroyManager (Manager ptr) = liftIO $ free ptr