Add FPS counter.

This commit is contained in:
Robbie Gleichman 2020-08-02 14:23:26 -07:00
parent 373e913119
commit 00309dcc9c
2 changed files with 68 additions and 38 deletions

View File

@ -79,6 +79,7 @@ executable glance-gui
, transformers , transformers
, old-time , old-time
, containers , containers
, time
default-language: Haskell2010 default-language: Haskell2010
Other-modules: Other-modules:

View File

@ -7,19 +7,20 @@
module Main (main) where module Main (main) where
import Control.Concurrent import Data.Coerce
import Control.Monad import Control.Monad
import Control.Monad (when) import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Reader (runReaderT) import Control.Monad.Trans.Reader (runReaderT)
import Data.IORef import Data.IORef
import qualified Data.IntMap.Strict as IntMap import qualified Data.IntMap.Strict as IntMap
import Data.Maybe (fromJust, fromMaybe) import Data.Maybe (fromJust, fromMaybe)
import Data.Time.Clock.System
import Data.GI.Base import Data.GI.Base
import qualified GI.Cairo as GI.Cairo import qualified GI.Cairo as GI.Cairo
import qualified GI.GLib as GLib import qualified GI.GLib as GLib
import qualified GI.Gdk as Gdk import qualified GI.Gdk as Gdk
import qualified GI.GdkPixbuf as GP -- import qualified GI.GdkPixbuf as GP
import qualified GI.Gio as Gio import qualified GI.Gio as Gio
import qualified GI.Gtk as Gtk import qualified GI.Gtk as Gtk
@ -28,6 +29,7 @@ import Graphics.Rendering.Cairo
import Graphics.Rendering.Cairo.Internal (Render(runRender)) import Graphics.Rendering.Cairo.Internal (Render(runRender))
import Graphics.Rendering.Cairo.Types (Cairo(Cairo)) import Graphics.Rendering.Cairo.Types (Cairo(Cairo))
nodeSize :: (Double, Double)
nodeSize = (100, 40) nodeSize = (100, 40)
-- | A graphical element that can be clicked -- | A graphical element that can be clicked
@ -42,24 +44,32 @@ data AppState = AppState
, _asEdges :: [(Element, Element)] , _asEdges :: [(Element, Element)]
, _asMouseXandY :: !(Double, Double) , _asMouseXandY :: !(Double, Double)
, _asElements :: IntMap.IntMap Element , _asElements :: IntMap.IntMap Element
, _asTime :: SystemTime
, _asFPSr :: Double -- ^ FPS rouned down to nearest hundred if over 200 fps.
} }
emptyAppState :: AppState
emptyAppState = AppState emptyAppState = AppState
{ _asMovingNode = Nothing { _asMovingNode = Nothing
, _asEdges = [] , _asEdges = []
, _asMouseXandY = (0, 0) , _asMouseXandY = (0, 0)
, _asElements = mempty , _asElements = mempty
, _asTime = MkSystemTime 0 0
, _asFPSr = 0
} }
renderCairo :: Coercible a (ManagedPtr ()) => a -> Render c -> IO c
renderCairo c r = withManagedPtr c $ \pointer -> renderCairo c r = withManagedPtr c $ \pointer ->
runReaderT (runRender r) (Cairo (castPtr pointer)) runReaderT (runRender r) (Cairo (castPtr pointer))
-- TODO Add type signature getXandY
:: MonadIO f
=> Gdk.EventButton -> f (Double, Double)
getXandY event = getXandY event =
(\x y -> (x, y)) <$> get event #x <*> get event #y (\x y -> (x, y)) <$> Gdk.getEventButtonX event <*> Gdk.getEventButtonY event
-- updateCanvas :: WidgetClass widget => widget -> PangoLayout -> Render () _drawLine :: (Double, Double) -> (Double, Double) -> Render ()
drawLine (fromX, fromY) (toX, toY) = do _drawLine (fromX, fromY) (toX, toY) = do
setSourceRGB 0 1 0 setSourceRGB 0 1 0
setLineWidth 5 setLineWidth 5
@ -67,7 +77,8 @@ drawLine (fromX, fromY) (toX, toY) = do
lineTo toX toY lineTo toX toY
stroke stroke
drawCircle (x, y) = do _drawCircle :: (Double, Double) -> Render ()
_drawCircle (x, y) = do
-- setSourceRGB 1 0 0 -- setSourceRGB 1 0 0
setLineWidth 1 setLineWidth 1
-- moveTo x y -- moveTo x y
@ -76,6 +87,7 @@ drawCircle (x, y) = do
arc x y radius 0 tau arc x y radius 0 tau
stroke stroke
drawNode :: Element -> Render ()
drawNode Element{..} = do drawNode Element{..} = do
let let
(x, y) = _elPosition (x, y) = _elPosition
@ -86,15 +98,21 @@ drawNode Element{..} = do
rectangle x y width height rectangle x y width height
stroke stroke
updateBackground canvas state = do updateBackground :: p -> IORef AppState -> Render (IntMap.IntMap ())
width <- realToFrac <$> (liftIO $ Gtk.widgetGetAllocatedWidth canvas) updateBackground _canvas state = do
height <- realToFrac <$> (liftIO $ Gtk.widgetGetAllocatedHeight canvas) -- width <- (realToFrac <$> (liftIO $ Gtk.widgetGetAllocatedWidth canvas)
-- :: Render Double)
-- height <- (realToFrac <$> (liftIO $ Gtk.widgetGetAllocatedHeight canvas)
-- :: Render Double)
-- TODO This should be moved into the setup phase -- TODO This should be moved into the setup phase
setSourceRGB 0 0 0 setSourceRGB 0 0 0
paint paint
stateVal <- liftIO $ readIORef state stateVal <- liftIO $ readIORef state
setSourceRGB 1 1 1
moveTo 10 10
showText ("fps=" <> show (_asFPSr stateVal))
setSourceRGB 1 0 0 setSourceRGB 1 0 0
traverse drawNode (_asElements stateVal) traverse drawNode (_asElements stateVal)
@ -114,53 +132,64 @@ startApp app = do
, Gdk.EventMaskButtonPressMask] , Gdk.EventMaskButtonPressMask]
#add window backgroundArea #add window backgroundArea
geometry <- new Gdk.Geometry [ #minWidth := 500, #minHeight := 500] -- geometry <- new Gdk.Geometry [ #minWidth := 500, #minHeight := 500]
-- screen <- get window #screen -- screen <- get window #screen
-- rgbaVisual <- #getRgbaVisual screen -- rgbaVisual <- #getRgbaVisual screen
-- #setVisual window rgbaVisual -- #setVisual window rgbaVisual
-- No noticable change with setting this to GLib.PRIORITY_DEFAULT -- surfaceRef <- newIORef (Nothing)
-- GLib.timeoutAdd GLib.PRIORITY_LOW 1 (#queueDraw backgroundArea >> pure True)
surfaceRef <- newIORef (Nothing)
_ <- on backgroundArea #draw (\context -> do _ <- on backgroundArea #draw (\context -> do
mSurface <- readIORef surfaceRef -- mSurface <- readIORef surfaceRef
surface <- case mSurface of -- surface <- case mSurface of
Nothing -> do -- Nothing -> do
(width, height) <- #getSize window -- (width, height) <- #getSize window
surf <- createImageSurface -- surf <- createImageSurface
FormatARGB32 -- FormatARGB32
(fromIntegral width) -- (fromIntegral width)
(fromIntegral height) -- (fromIntegral height)
writeIORef surfaceRef $ Just $ surf -- writeIORef surfaceRef $ Just $ surf
pure surf -- pure surf
Just surface -> pure surface -- Just surface -> pure surface
renderCairo context (updateBackground backgroundArea state) _ <- renderCairo context (updateBackground backgroundArea state)
pure True) pure True)
let #showAll window
timeoutCallback :: IO Bool
timeoutCallback = do
gdkWindow <- fromJust <$> #getWindow window gdkWindow <- fromJust <$> #getWindow window
display <- fmap fromJust Gdk.displayGetDefault -- TODO unsafe display <- fmap fromJust Gdk.displayGetDefault -- TODO unsafe
deviceManager <- fromJust <$> Gdk.displayGetDeviceManager display -- TODO deprecated deviceManager <- fromJust <$> Gdk.displayGetDeviceManager display -- TODO deprecated
device <- Gdk.deviceManagerGetClientPointer deviceManager device <- Gdk.deviceManagerGetClientPointer deviceManager
let
timeoutCallback :: IO Bool
timeoutCallback = do
newTime@(MkSystemTime seconds nanoseconds) <- getSystemTime
oldState <- readIORef state
let
(MkSystemTime oldSeconds oldNanoseconds) = _asTime oldState
secondsDiff = seconds - oldSeconds
nanosecondDiff = nanoseconds - oldNanoseconds
fps = if secondsDiff == 0
then fromIntegral (div (10^(9 :: Int)) nanosecondDiff)
else 1 / (fromIntegral secondsDiff)
truncatedFps = if fps >= 200
then fromIntegral $ (div (truncate fps) 100) * (100 :: Int)
else fps
gdkDevicePosition <- Gdk.windowGetDevicePositionDouble gdkWindow device gdkDevicePosition <- Gdk.windowGetDevicePositionDouble gdkWindow device
let (_, x, y, _) = gdkDevicePosition let (_, x, y, _) = gdkDevicePosition
modifyIORef' state modifyIORef' state
(\s@AppState{_asMouseXandY} (\s@AppState{_asMouseXandY}
-> s{_asMouseXandY=(x, y)} -> s{_asMouseXandY=(x, y)
, _asTime=newTime
, _asFPSr=truncatedFps}
) )
-- print (x, y)
#queueDraw backgroundArea #queueDraw backgroundArea
pure True pure True
GLib.timeoutAdd GLib.PRIORITY_LOW 1 (timeoutCallback) _ <- GLib.timeoutAdd GLib.PRIORITY_DEFAULT 1 (timeoutCallback)
let let
backgroundPress eventButton = do backgroundPress eventButton = do
@ -187,7 +216,7 @@ startApp app = do
putStrLn "backgroundPressed" putStrLn "backgroundPressed"
pure True pure True
on backgroundArea #buttonPressEvent backgroundPress _ <- on backgroundArea #buttonPressEvent backgroundPress
#showAll window #showAll window
pure () pure ()
@ -196,6 +225,6 @@ main :: IO ()
main = do main = do
app <- new Gtk.Application [] app <- new Gtk.Application []
_ <- on app #activate (startApp app) _ <- on app #activate (startApp app)
status <- Gio.applicationRun app Nothing appStatus <- Gio.applicationRun app Nothing
putStrLn ("Application status is " <> show status) putStrLn ("Application status is " <> show appStatus)
pure () pure ()