/GUI/Main.hs

https://github.com/kowey/ThreadScope · Haskell · 360 lines · 271 code · 73 blank · 16 comment · 4 complexity · 8eb646e0f8d560c00099d4e6ee6f0be2 MD5 · raw file

  1. {-# LANGUAGE CPP #-}
  2. -- ThreadScope: a graphical viewer for Haskell event log information.
  3. -- Maintainer: satnams@microsoft.com, s.singh@ieee.org
  4. module GUI.Main (runGUI) where
  5. -- Imports for GTK
  6. import Graphics.UI.Gtk as Gtk
  7. import System.Glib.GError (failOnGError)
  8. -- Imports from Haskell library
  9. import Text.Printf
  10. import Control.Monad
  11. #ifndef mingw32_HOST_OS
  12. import System.Posix
  13. #endif
  14. import Control.Concurrent
  15. import qualified Control.Concurrent.Chan as Chan
  16. import Data.Array
  17. import Paths_threadscope
  18. -- Imports for ThreadScope
  19. import GUI.MainWindow as MainWindow
  20. import GUI.Types
  21. import Events.HECs hiding (Event)
  22. import GUI.Dialogs
  23. import Events.ReadEvents
  24. import GUI.EventsView
  25. import GUI.Histogram
  26. import GUI.Timeline
  27. import GUI.TraceView
  28. import GUI.BookmarkView
  29. import GUI.KeyView
  30. import GUI.SaveAs
  31. import qualified GUI.ConcurrencyControl as ConcurrencyControl
  32. import qualified GUI.ProgressView as ProgressView
  33. -------------------------------------------------------------------------------
  34. data UIEnv = UIEnv {
  35. mainWin :: MainWindow,
  36. eventsView :: EventsView,
  37. histogramWin :: HistogramView,
  38. timelineWin :: TimelineView,
  39. traceView :: TraceView,
  40. bookmarkView :: BookmarkView,
  41. keyView :: KeyView,
  42. eventQueue :: Chan Event,
  43. concCtl :: ConcurrencyControl.ConcurrencyControl
  44. }
  45. data EventlogState
  46. = NoEventlogLoaded
  47. | EventlogLoaded {
  48. mfilename :: Maybe FilePath, --test traces have no filepath
  49. hecs :: HECs,
  50. cursorTs :: Timestamp,
  51. cursorPos :: Int
  52. }
  53. data FileSaveFormat = FormatPDF | FormatPNG
  54. postEvent :: Chan Event -> Event -> IO ()
  55. postEvent = Chan.writeChan
  56. getEvent :: Chan Event -> IO Event
  57. getEvent = Chan.readChan
  58. data Event
  59. = EventOpenDialog
  60. | EventAboutDialog
  61. | EventQuit
  62. | EventFileLoad FilePath
  63. | EventTestLoad String
  64. | EventFileReload
  65. | EventFileSave FileSaveFormat
  66. -- | EventStateClear
  67. | EventSetState (Maybe FilePath) HECs
  68. | EventShowSidebar Bool
  69. | EventShowEvents Bool
  70. | EventTimelineJumpStart
  71. | EventTimelineJumpEnd
  72. | EventTimelineJumpCursor
  73. | EventTimelineScrollLeft
  74. | EventTimelineScrollRight
  75. | EventTimelineZoomIn
  76. | EventTimelineZoomOut
  77. | EventTimelineZoomToFit
  78. | EventTimelineShowLabels Bool
  79. | EventTimelineShowBW Bool
  80. | EventCursorChangedIndex Int
  81. | EventCursorChangedTimestamp Timestamp
  82. | EventTracesChanged [Trace]
  83. | EventBookmarkAdd
  84. | EventBookmarRemove Int
  85. constructUI :: IO UIEnv
  86. constructUI = failOnGError $ do
  87. builder <- builderNew
  88. builderAddFromFile builder =<< getDataFileName "threadscope.ui"
  89. eventQueue <- Chan.newChan
  90. let post = postEvent eventQueue
  91. mainWin <- mainWindowNew builder MainWindowActions {
  92. mainWinOpen = post EventOpenDialog,
  93. mainWinSavePDF = post (EventFileSave FormatPDF),
  94. mainWinSavePNG = post (EventFileSave FormatPNG),
  95. mainWinQuit = post EventQuit,
  96. mainWinViewSidebar = post . EventShowSidebar,
  97. mainWinViewEvents = post . EventShowEvents,
  98. mainWinViewRefresh = post EventFileReload,
  99. mainWinAbout = post EventAboutDialog,
  100. mainWinJumpStart = post EventTimelineJumpStart,
  101. mainWinJumpEnd = post EventTimelineJumpEnd,
  102. mainWinJumpCursor = post EventTimelineJumpCursor,
  103. mainWinScrollLeft = post EventTimelineScrollLeft,
  104. mainWinScrollRight = post EventTimelineScrollRight,
  105. mainWinJumpZoomIn = post EventTimelineZoomIn,
  106. mainWinJumpZoomOut = post EventTimelineZoomOut,
  107. mainWinJumpZoomFit = post EventTimelineZoomToFit,
  108. mainWinDisplayLabels = post . EventTimelineShowLabels,
  109. mainWinViewBW = post . EventTimelineShowBW
  110. }
  111. histogramWin <- histogramViewNew builder HistogramViewActions {
  112. histogramViewCursorChanged = post . EventCursorChangedTimestamp
  113. }
  114. timelineWin <- timelineViewNew builder TimelineViewActions {
  115. timelineViewCursorChanged = post . EventCursorChangedTimestamp
  116. }
  117. eventsView <- eventsViewNew builder EventsViewActions {
  118. timelineViewCursorChanged = post . EventCursorChangedIndex
  119. }
  120. traceView <- traceViewNew builder TraceViewActions {
  121. traceViewTracesChanged = post . EventTracesChanged
  122. }
  123. bookmarkView <- bookmarkViewNew builder BookmarkViewActions {
  124. bookmarkViewAddBookmark = post EventBookmarkAdd,
  125. bookmarkViewRemoveBookmark = post . EventBookmarRemove,
  126. bookmarkViewGotoBookmark = \ts -> post (EventCursorChangedTimestamp ts)
  127. >> post EventTimelineJumpCursor
  128. }
  129. keyView <- keyViewNew builder
  130. concCtl <- ConcurrencyControl.start
  131. return UIEnv{..}
  132. -------------------------------------------------------------------------------
  133. data LoopDone = LoopDone
  134. eventLoop :: UIEnv -> EventlogState -> IO ()
  135. eventLoop uienv@UIEnv{..} eventlogState = do
  136. event <- getEvent eventQueue
  137. next <- dispatch event eventlogState
  138. case next of
  139. Left LoopDone -> return ()
  140. Right eventlogState' -> eventLoop uienv eventlogState'
  141. where
  142. dispatch :: Event -> EventlogState -> IO (Either LoopDone EventlogState)
  143. dispatch EventQuit _ = return (Left LoopDone)
  144. dispatch EventOpenDialog _ = do
  145. openFileDialog mainWin $ \filename ->
  146. post (EventFileLoad filename)
  147. continue
  148. dispatch (EventFileLoad filename) _ = do
  149. forkIO $ loadEvents (Just filename) (registerEventsFromFile filename)
  150. --TODO: set state to be empty during loading
  151. continue
  152. dispatch (EventTestLoad testname) _ = do
  153. forkIO $ loadEvents Nothing (registerEventsFromTrace testname)
  154. --TODO: set state to be empty during loading
  155. continue
  156. dispatch EventFileReload EventlogLoaded{mfilename = Just filename} = do
  157. forkIO $ loadEvents (Just filename) (registerEventsFromFile filename)
  158. --TODO: set state to be empty during loading
  159. continue
  160. -- dispatch EventClearState _
  161. dispatch (EventSetState mfilename hecs) _ =
  162. continueWith EventlogLoaded {
  163. mfilename = mfilename,
  164. hecs = hecs,
  165. cursorTs = 0,
  166. cursorPos = 0
  167. }
  168. dispatch (EventFileSave format)
  169. EventlogLoaded {hecs, mfilename = Just filename} = do
  170. viewParams <- timelineGetViewParameters timelineWin
  171. let viewParams' = viewParams {
  172. detail = 1,
  173. bwMode = False,
  174. labelsMode = False
  175. }
  176. case format of
  177. FormatPDF -> saveAsPDF filename hecs viewParams'
  178. FormatPNG -> saveAsPNG filename hecs viewParams'
  179. continue
  180. dispatch EventAboutDialog _ = do
  181. aboutDialog mainWin
  182. continue
  183. dispatch (EventShowSidebar visible) _ = do
  184. MainWindow.sidebarSetVisibility mainWin visible
  185. continue
  186. dispatch (EventShowEvents visible) _ = do
  187. MainWindow.eventsSetVisibility mainWin visible
  188. continue
  189. dispatch EventTimelineJumpStart _ = do
  190. timelineScrollToBeginning timelineWin
  191. eventsViewScrollToLine eventsView 0
  192. continue
  193. dispatch EventTimelineJumpEnd EventlogLoaded{hecs} = do
  194. timelineScrollToEnd timelineWin
  195. let (_,end) = bounds (hecEventArray hecs)
  196. eventsViewScrollToLine eventsView end
  197. continue
  198. dispatch EventTimelineJumpCursor EventlogLoaded{cursorPos} = do
  199. timelineCentreOnCursor timelineWin --TODO: pass cursorTs here
  200. eventsViewScrollToLine eventsView cursorPos
  201. continue
  202. dispatch EventTimelineScrollLeft _ = do
  203. timelineScrollLeft timelineWin
  204. continue
  205. dispatch EventTimelineScrollRight _ = do
  206. timelineScrollRight timelineWin
  207. continue
  208. dispatch EventTimelineZoomIn _ = do
  209. timelineZoomIn timelineWin
  210. continue
  211. dispatch EventTimelineZoomOut _ = do
  212. timelineZoomOut timelineWin
  213. continue
  214. dispatch EventTimelineZoomToFit _ = do
  215. timelineZoomToFit timelineWin
  216. continue
  217. dispatch (EventTimelineShowLabels showLabels) _ = do
  218. timelineSetShowLabels timelineWin showLabels
  219. continue
  220. dispatch (EventTimelineShowBW showBW) _ = do
  221. timelineSetBWMode timelineWin showBW
  222. continue
  223. dispatch (EventCursorChangedIndex cursorPos') EventlogLoaded{hecs} = do
  224. let cursorTs' = eventIndexToTimestamp hecs cursorPos'
  225. timelineSetCursor timelineWin cursorTs'
  226. eventsViewSetCursor eventsView cursorPos'
  227. continueWith eventlogState {
  228. cursorTs = cursorTs',
  229. cursorPos = cursorPos'
  230. }
  231. dispatch (EventCursorChangedTimestamp cursorTs') EventlogLoaded{hecs} = do
  232. let cursorPos' = timestampToEventIndex hecs cursorTs'
  233. timelineSetCursor timelineWin cursorTs'
  234. eventsViewSetCursor eventsView cursorPos'
  235. continueWith eventlogState {
  236. cursorTs = cursorTs',
  237. cursorPos = cursorPos'
  238. }
  239. dispatch (EventTracesChanged traces) _ = do
  240. timelineWindowSetTraces timelineWin traces
  241. continue
  242. dispatch EventBookmarkAdd EventlogLoaded{cursorTs} = do
  243. bookmarkViewAdd bookmarkView cursorTs
  244. timelineWindowSetBookmarks timelineWin =<< bookmarkViewGet bookmarkView
  245. continue
  246. dispatch (EventBookmarRemove n) _ = do
  247. bookmarkViewRemove bookmarkView n
  248. timelineWindowSetBookmarks timelineWin =<< bookmarkViewGet bookmarkView
  249. continue
  250. loadEvents mfilename registerEvents = do
  251. ConcurrencyControl.fullSpeed concCtl $
  252. ProgressView.withProgress mainWin $ \progress -> do
  253. (hecs, name, nevents, timespan) <- registerEvents progress
  254. MainWindow.setFileLoaded mainWin (Just name)
  255. MainWindow.setStatusMessage mainWin $
  256. printf "%s (%d events, %.3fs)" name nevents timespan
  257. eventsViewSetEvents eventsView (Just (hecEventArray hecs))
  258. traceViewSetHECs traceView hecs
  259. traces' <- traceViewGetTraces traceView
  260. timelineWindowSetHECs timelineWin (Just hecs)
  261. timelineWindowSetTraces timelineWin traces'
  262. histogramWindowSetHECs histogramWin (Just hecs)
  263. histogramWindowSetTraces histogramWin traces'
  264. post (EventSetState mfilename hecs)
  265. return ()
  266. post = postEvent eventQueue
  267. continue = continueWith eventlogState
  268. continueWith = return . Right
  269. -------------------------------------------------------------------------------
  270. runGUI :: FilePath -> String -> Bool -> IO ()
  271. runGUI filename traceName _debug = do
  272. Gtk.initGUI
  273. uiEnv <- constructUI
  274. let post = postEvent (eventQueue uiEnv)
  275. when (filename /= "") $
  276. post (EventFileLoad filename)
  277. -- Likewise for test traces
  278. when (traceName /= "") $
  279. post (EventTestLoad traceName)
  280. forkIO $ do
  281. eventLoop uiEnv NoEventlogLoaded
  282. Gtk.mainQuit
  283. #ifndef mingw32_HOST_OS
  284. installHandler sigINT (Catch $ post EventQuit) Nothing
  285. #endif
  286. -- Enter Gtk+ main event loop.
  287. Gtk.mainGUI