threadscope-0.1: Timeline/Render.hs
module Timeline.Render (
exposeTraceView,
renderTraces,
updateLabelDrawingArea,
calculateTotalTimelineHeight,
toWholePixels
) where
import Timeline.Render.Constants
import Timeline.Motion
import Timeline.Ticks
import Timeline.HEC
import Timeline.Activity
import Timeline.RenderBookmarks
import Timeline.WithViewScale
import State
import ViewerColours
import Traces
import GHC.RTS.Events hiding (Event)
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Gdk.Events as Old
import Graphics.Rendering.Cairo as C
import Data.Maybe
import Data.IORef
import Control.Monad
import Text.Printf
-------------------------------------------------------------------------------
-- |The 'updateProfileDrawingArea' function is called when an expose event
-- occurs. This function redraws the currently visible part of the
-- main trace canvas plus related canvases.
exposeTraceView :: ViewerState -> Region -> IO ()
exposeTraceView state@ViewerState{..} exposeRegion = do
maybeEventArray <- readIORef hecsIORef
-- Check to see if an event trace has been loaded
case maybeEventArray of
Nothing -> return ()
Just hecs -> renderView state exposeRegion hecs
renderView :: ViewerState -> Region -> HECs -> IO ()
renderView state@ViewerState{..} exposeRegion hecs = do
-- Get state information from user-interface components
bw_mode <- checkMenuItemGetActive bwToggle
labels_mode <- toggleToolButtonGetActive showLabelsToggle
(dAreaWidth,dAreaHeight) <- widgetGetSize timelineDrawingArea
when debug $ do
putStrLn ("\n=== updateCanvas")
putStrLn (show exposeRegion)
printf "width %d, height %d\n" dAreaWidth dAreaHeight
scaleValue <- checkScaleValue state
vadj_value <- adjustmentGetValue timelineVAdj
when debug $ liftIO $ printf "vadj_value: %f\n" vadj_value
totalHeight <- calculateTotalTimelineHeight state
let timelineHeight = max totalHeight dAreaHeight
-- render either the whole height of the timeline, or the window, whichever
-- is larger (this just ensure we fill the background if the timeline is
-- smaller than the window).
-- snap the view to whole pixels, to avoid blurring
hadj_value0 <- adjustmentGetValue timelineAdj
let hadj_value = toWholePixels scaleValue hadj_value0
when debug $ do
hadj_pagesize <- adjustmentGetPageSize timelineAdj
hadj_lower <- adjustmentGetLower timelineAdj
hadj_upper <- adjustmentGetUpper timelineAdj
ctx <- statusbarGetContextId statusBar "debug"
statusbarPush statusBar ctx $
printf "scale=%f win=(%d,%d) hadj: (val=%f, page=%f, l=%f u=%f)"
scaleValue dAreaWidth dAreaHeight
hadj_value hadj_pagesize hadj_lower hadj_upper
return ()
traces <- getViewTraces state
let params = ViewParameters {
width = dAreaWidth,
height = timelineHeight,
viewTraces = traces,
hadjValue = hadj_value,
scaleValue = scaleValue,
detail = 3, -- for now
bwMode = bw_mode,
labelsMode = labels_mode
}
prev_view <- readIORef timelinePrevView
cursor_t <- readIORef cursorIORef
rect <- regionGetClipbox exposeRegion
win <- widgetGetDrawWindow timelineDrawingArea
renderWithDrawable win $ do
let renderToNewSurface = do
new_surface <- withTargetSurface $ \surface ->
liftIO $ createSimilarSurface surface ContentColor
dAreaWidth timelineHeight
renderWith new_surface $ do
clearWhite
renderTraces state params traces hecs rect
return new_surface
surface <-
case prev_view of
Nothing -> do
when debug $ liftIO $ putStrLn "no old surface"
renderToNewSurface
Just (old_params, surface)
| old_params == params
-> do when debug $ liftIO $ putStrLn "using previously rendered view"
return surface
| width old_params == width params &&
height old_params == height params
-> do
if old_params { hadjValue = hadjValue params } == params
-- only the hadjValue changed
&& abs (hadjValue params - hadjValue old_params) <
fromIntegral (width params) * scaleValue
-- and the views overlap...
then do
when debug $ liftIO $ putStrLn "scrolling"
scrollView state surface old_params params traces hecs
else do
when debug $ liftIO $ putStrLn "using old surface"
renderWith surface $ do
clearWhite; renderTraces state params traces hecs rect
return surface
| otherwise
-> do when debug $ liftIO $ putStrLn "old surface no good"
surfaceFinish surface
renderToNewSurface
liftIO $ writeIORef timelinePrevView (Just (params, surface))
region exposeRegion
clip
setSourceSurface surface 0 (-vadj_value)
-- ^^ this is where we adjust for the vertical scrollbar
setOperator OperatorSource
paint
when (scaleValue > 0) $ do
renderBookmarks state params
drawCursor cursor_t params
-------------------------------------------------------------------------------
clearWhite :: Render ()
clearWhite = do
save
setOperator OperatorSource
setSourceRGBA 0xffff 0xffff 0xffff 0xffff
paint
restore
-------------------------------------------------------------------------------
drawCursor :: Timestamp -> ViewParameters -> Render ()
drawCursor cursor_t param@ViewParameters{..} = do
withViewScale param $ do
(threePixels, _) <- deviceToUserDistance 3 0
setLineWidth threePixels
setOperator OperatorOver
setSourceRGBAhex blue 1.0
moveTo (fromIntegral cursor_t) 0
lineTo (fromIntegral cursor_t) (fromIntegral height)
stroke
-------------------------------------------------------------------------------
-- This function draws the current view of all the HECs with Cario
renderTraces :: ViewerState -> ViewParameters -> [Trace] -> HECs -> Rectangle
-> Render ()
renderTraces state@ViewerState{..} params@ViewParameters{..}
traces hecs (Rectangle rx ry rw rh)
= do
let
scale_rx = fromIntegral rx * scaleValue
scale_rw = fromIntegral rw * scaleValue
scale_width = fromIntegral width * scaleValue
startPos :: Timestamp
startPos = fromIntegral (max 0 (truncate (scale_rx + hadjValue)))
-- hadj_value might be negative, as we leave a
-- small gap before the trace starts at the beginning
endPos :: Timestamp
endPos = minimum [
ceiling (max 0 (hadjValue + scale_width)),
ceiling (max 0 (hadjValue + scale_rx + scale_rw)),
hecLastEventTime hecs
]
when debug $ liftIO $ do
printf "rx = %d, scale_rx = %f, scale_rw = %f, hadjValue = %f, startPos = %d, endPos = %d scaleValue = %f\n" rx scale_rx scale_rw hadjValue startPos endPos scaleValue
-- Now render the timeline drawing if we have a non-empty trace
when (scaleValue > 0) $ do
withViewScale params $ do
save
-- First render the ticks and tick times
renderTicks startPos endPos scaleValue height
restore
-- This function helps to render a single HEC...
let
renderTrace trace y = do
save
translate 0 (fromIntegral y)
case trace of
TraceHEC c ->
renderHEC c params startPos endPos (hecTrees hecs !! c)
TraceActivity ->
renderActivity params hecs startPos endPos
_ ->
return ()
restore
-- Now rennder all the HECs.
zipWithM_ renderTrace traces (traceYPositions labelsMode traces)
when debug $ liftIO $ putStrLn "renderTraces done\n"
-------------------------------------------------------------------------------
-- parameters differ only in the hadjValue, we can scroll ...
scrollView :: ViewerState -> Surface
-> ViewParameters -> ViewParameters
-> [Trace] -> HECs
-> Render Surface
scrollView state surface old new traces hecs = do
-- scrolling on the same surface seems not to work, I get garbled results.
-- Not sure what the best way to do this is.
-- let new_surface = surface
new_surface <- withTargetSurface $ \surface ->
liftIO $ createSimilarSurface surface ContentColor
(width new) (height new)
renderWith new_surface $ do
let
scale = scaleValue new
old_hadj = hadjValue old
new_hadj = hadjValue new
w = fromIntegral (width new)
h = fromIntegral (height new)
off = (old_hadj - new_hadj) / scale
-- liftIO $ printf "scrollView: old: %f, new %f, dist = %f (%f pixels)\n"
-- old_hadj new_hadj (old_hadj - new_hadj) off
-- copy the content from the old surface to the new surface,
-- shifted by the appropriate amount.
setSourceSurface surface off 0
if old_hadj > new_hadj
then do rectangle off 0 (w - off) h -- scroll right.
else do rectangle 0 0 (w + off) h -- scroll left.
C.fill
let rect | old_hadj > new_hadj
= Rectangle 0 0 (ceiling off) (height new)
| otherwise
= Rectangle (truncate (w + off)) 0 (ceiling (-off)) (height new)
case rect of
Rectangle x y w h -> rectangle (fromIntegral x) (fromIntegral y)
(fromIntegral w) (fromIntegral h)
setSourceRGBA 0xffff 0xffff 0xffff 0xffff
C.fill
renderTraces state new traces hecs rect
surfaceFinish surface
return new_surface
------------------------------------------------------------------------------
toWholePixels :: Double -> Double -> Double
toWholePixels 0 x = 0
toWholePixels scale x = fromIntegral (truncate (x / scale)) * scale
-------------------------------------------------------------------------------
-- This function returns a value which can be used to scale
-- Timestamp event log values to pixels.
-- If the scale has previous been computed then it is resued.
-- An "uncomputed" scale value is represetned as -1.0 (defaultScaleValue)
-- We estimate the width of the vertical scrollbar at 20 pixels
checkScaleValue :: ViewerState -> IO Double
checkScaleValue state@ViewerState{..}
= do scaleValue <- readIORef scaleIORef
if scaleValue < 0.0
then do zoomToFit state
readIORef scaleIORef
else return scaleValue
-------------------------------------------------------------------------------
updateLabelDrawingArea :: ViewerState -> Event -> IO Bool
updateLabelDrawingArea state@ViewerState{..} (Expose { Old.eventArea=rect })
= do traces <- getViewTraces state
labels_mode <- toggleToolButtonGetActive showLabelsToggle
win <- widgetGetDrawWindow timelineLabelDrawingArea
vadj_value <- adjustmentGetValue timelineVAdj
gc <- gcNew win
let ys = map (subtract (round vadj_value)) $
traceYPositions labels_mode traces
zipWithM_ (drawLabel timelineLabelDrawingArea gc) traces ys
return True
updateLabelDrawingArea _ _ = error "updateLabelDrawingArea"
drawLabel :: DrawingArea -> GC -> Trace -> Int -> IO ()
drawLabel canvas gc trace y
= do win <- widgetGetDrawWindow canvas
txt <- canvas `widgetCreateLayout` (showTrace trace)
drawLayoutWithColors win gc 10 y txt (Just black) Nothing
--------------------------------------------------------------------------------
traceYPositions :: Bool -> [Trace] -> [Int]
traceYPositions labels_mode traces
= scanl (\a b -> a + (traceHeight b) + extra + tracePad) firstTraceY traces
where
extra = if labels_mode then hecLabelExtra else 0
traceHeight (TraceHEC _) = hecTraceHeight
traceHeight TraceActivity = activityGraphHeight
traceHeight _ = 0
--------------------------------------------------------------------------------
showTrace :: Trace -> String
showTrace (TraceHEC n) = "HEC " ++ show n
showTrace TraceActivity = "Activity"
showTrace _ = "?"
--------------------------------------------------------------------------------
calculateTotalTimelineHeight :: ViewerState -> IO Int
calculateTotalTimelineHeight state@ViewerState{..} = do
traces <- getViewTraces state
labels_mode <- toggleToolButtonGetActive showLabelsToggle
return $ last (traceYPositions labels_mode traces)
--------------------------------------------------------------------------------