threadscope-0.1.1: EventsWindow.hs
module EventsWindow (
setupEventsWindow,
updateEventsWindow,
eventsWindowResize,
getCursorLine,
drawEvents
) where
import State
import ViewerColours
import Timeline
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Gdk.EventM
import Graphics.Rendering.Cairo
import GHC.RTS.Events as GHC
import Graphics.UI.Gtk.ModelView as New
import Control.Monad.Reader
import Data.Array
import Data.IORef
import Text.Printf
-------------------------------------------------------------------------------
setupEventsWindow :: ViewerState -> IO ()
setupEventsWindow state@ViewerState{..} = do
-- make the background white
widgetModifyBg eventsDrawingArea StateNormal (Color 0xffff 0xffff 0xffff)
adj <- rangeGetAdjustment eventsVScrollbar
adjustmentSetLower adj 0
adjustmentSetStepIncrement adj 4
widgetSetCanFocus eventsDrawingArea True
on eventsDrawingArea configureEvent $ eventsWindowResize state
on eventsDrawingArea exposeEvent $ updateEventsWindow state
on eventsDrawingArea buttonPressEvent $ tryEvent $ do
button <- eventButton
(_,y) <- eventCoordinates
liftIO $ do
widgetGrabFocus eventsDrawingArea
setCursor state y
on eventsDrawingArea focusInEvent $ liftIO $ do
f <- get eventsDrawingArea widgetHasFocus
when debug $ putStrLn ("focus in: " ++ show f)
-- set eventsDrawingArea [widgetHasFocus := True]
return False
on eventsDrawingArea focusOutEvent $ liftIO $ do
f <- get eventsDrawingArea widgetHasFocus
when debug $ putStrLn ("focus out: " ++ show f)
-- set eventsDrawingArea [widgetHasFocus := False]
return False
on eventsDrawingArea keyPressEvent $ do
key <- eventKeyName
when debug $ liftIO $ putStrLn ("key " ++ key)
return True
on eventsDrawingArea scrollEvent $ do
dir <- eventScrollDirection
liftIO $ do
val <- adjustmentGetValue adj
step <- adjustmentGetStepIncrement adj
case dir of
ScrollUp -> adjustmentSetValue adj (val - step)
ScrollDown -> adjustmentSetValue adj (val + step)
_ -> return ()
return True
onValueChanged adj $
widgetQueueDraw eventsDrawingArea
onToolButtonClicked eventsFirstButton $ do
putStrLn "eventsFirstButton"
adjustmentSetValue adj 0
onToolButtonClicked eventsLastButton $ do
upper <- adjustmentGetUpper adj
adjustmentSetValue adj upper
onToolButtonClicked eventsHomeButton $ do
cursorpos <- getCursorLine state
page <- adjustmentGetPageSize adj
adjustmentSetValue adj (fromIntegral (max 0 (cursorpos - round page `quot` 2)))
-- Button for adding the cursor position to the boomark list
onToolButtonClicked addBookmarkButton $ do
when debug $ putStrLn "Add bookmark\n"
cursorPos <- readIORef cursorIORef
New.listStoreAppend bookmarkStore cursorPos
queueRedrawTimelines state
-- Button for deleting a bookmark
onToolButtonClicked deleteBookmarkButton $ do
when debug $ putStrLn "Delete bookmark\n"
sel <- treeViewGetSelection bookmarkTreeView
selection <- treeSelectionGetSelected sel
case selection of
Nothing -> return ()
Just (TreeIter _ pos _ _) -> listStoreRemove bookmarkStore (fromIntegral pos)
queueRedrawTimelines state
-- Button for jumping to bookmark
onToolButtonClicked gotoBookmarkButton $ do
sel <- treeViewGetSelection bookmarkTreeView
selection <- treeSelectionGetSelected sel
case selection of
Nothing -> return ()
Just (TreeIter _ pos _ _) -> do
l <- listStoreToList bookmarkStore
when debug $ putStrLn ("gotoBookmark: " ++ show l++ " pos = " ++ show pos)
setCursorToTime state (l!!(fromIntegral pos))
queueRedrawTimelines state
exts <- withImageSurface FormatARGB32 0 0 $ \s -> renderWith s eventsFont
writeIORef eventsFontExtents exts
return ()
-------------------------------------------------------------------------------
eventsWindowResize :: ViewerState -> EventM EConfigure Bool
eventsWindowResize state@ViewerState{..} = liftIO $ do
(_,h) <- widgetGetSize eventsDrawingArea
win <- widgetGetDrawWindow eventsDrawingArea
exts <- readIORef eventsFontExtents
let page = fromIntegral (truncate (fromIntegral h / fontExtentsHeight exts))
mb_hecs <- readIORef hecsIORef
case mb_hecs of
Nothing -> return True
Just hecs -> do
let arr = hecEventArray hecs
let (_, n_events) = bounds arr
adjustmentSetPageIncrement eventsAdj page
adjustmentSetPageSize eventsAdj page
adjustmentSetUpper eventsAdj (fromIntegral n_events + 1)
-- printf "eventsWindowResize: %f" page
return True
-------------------------------------------------------------------------------
updateEventsWindow :: ViewerState -> EventM EExpose Bool
updateEventsWindow state@ViewerState{..} = liftIO $ do
value <- adjustmentGetValue eventsAdj
mb_hecs <- readIORef hecsIORef
case mb_hecs of
Nothing -> return True
Just hecs -> do
let arr = hecEventArray hecs
win <- widgetGetDrawWindow eventsDrawingArea
(w,h) <- widgetGetSize eventsDrawingArea
cursorpos <- getCursorLine state
when debug $ printf "cursorpos: %d\n" cursorpos
renderWithDrawable win $ do
drawEvents value arr w h cursorpos
return True
-------------------------------------------------------------------------------
getCursorLine :: ViewerState -> IO Int
getCursorLine state@ViewerState{..} = do
-- locate the cursor position as a line number
current_cursor <- readIORef cursorIORef
eventsCursor <- readIORef eventsCursorIORef
mb_hecs <- readIORef hecsIORef
case mb_hecs of
Nothing -> return 0
Just hecs -> do
let arr = hecEventArray hecs
case eventsCursor of
Just (cursort, cursorpos) | cursort == current_cursor ->
return cursorpos
_other -> do
let cursorpos = locateCursor arr current_cursor
writeIORef eventsCursorIORef (Just (current_cursor, cursorpos))
return cursorpos
-------------------------------------------------------------------------------
setCursor :: ViewerState -> Double -> IO ()
setCursor state@ViewerState{..} eventY = do
val <- adjustmentGetValue eventsAdj
mb_hecs <- readIORef hecsIORef
case mb_hecs of
Nothing -> return ()
Just hecs -> do
let arr = hecEventArray hecs
exts <- readIORef eventsFontExtents
let
line' = truncate (val + eventY / fontExtentsHeight exts)
arr_max = snd $ bounds arr
line = if line' > arr_max then arr_max else line'
t = time (ce_event (arr!line))
--
writeIORef cursorIORef t
writeIORef eventsCursorIORef (Just (t,line))
widgetQueueDraw eventsDrawingArea
-- find the line that corresponds to the next event after the cursor
locateCursor :: Array Int GHC.CapEvent -> Timestamp -> Int
locateCursor arr cursor = search l (r+1)
where
(l,r) = bounds arr
search !l !r
| (r - l) <= 1 = if cursor > time (ce_event (arr!l)) then r else l
| cursor < tmid = search l mid
| otherwise = search mid r
where
mid = l + (r - l) `quot` 2
tmid = time (ce_event (arr!mid))
eventsFont :: Render FontExtents
eventsFont = do
selectFontFace "Monospace" FontSlantNormal FontWeightNormal
setFontSize 12
fontExtents
-------------------------------------------------------------------------------
drawEvents :: Double -> Array Int GHC.CapEvent -> Int -> Int -> Int -> Render ()
drawEvents value arr width height cursor = do
let val = truncate value :: Int
exts <- eventsFont
let h = fontExtentsHeight exts
(_, upper) = bounds arr
lines = ceiling (fromIntegral height / h)
end = min upper (val + lines)
draw y ev = do moveTo 0 y; showText (ppEvent ev)
zipWithM_ draw [ h, h*2 .. ] [ arr ! n | n <- [ val .. end ] ]
when (val <= cursor && cursor <= end) $ do
setLineWidth 3
setOperator OperatorOver
setSourceRGBAhex blue 1.0
let cursory = fromIntegral (cursor - val) * h + 3
moveTo 0 cursory
lineTo (fromIntegral width) cursory
stroke
-------------------------------------------------------------------------------
ppEvent :: CapEvent -> String
ppEvent (CapEvent cap (GHC.Event time spec)) =
printf "%9d: " time ++
(case cap of
Nothing -> ""
Just c -> printf "cap %d: " c) ++
case spec of
UnknownEvent{ ref=ref } ->
printf "unknown event; %d" ref
Message msg -> msg
UserMessage msg -> msg
_other -> showEventTypeSpecificInfo spec