+ let state = IS { window = undefined, buffer = undefined, usrProg = eventHandler, .. }
+ runGUI windowWidth windowHeight state
+
+runGUI :: Int -> Int -> IState -> IO ()
+runGUI w h (IS { .. }) = do
+ -- Init GTK.
+ Gtk.initGUI
+
+ -- Create a window, which will make the mainloop terminated when
+ -- it is closed.
+ window <- Gtk.windowNew
+
+ Gtk.set window [ Gtk.containerBorderWidth := 10
+ , Gtk.windowTitle := "FP Practicum"
+ , Gtk.windowDefaultWidth := w
+ , Gtk.windowDefaultHeight := h
+ ]
+ Gtk.onDestroy window Gtk.mainQuit
+
+ -- Create a buffer to draw on (name the actual buffer buffer', so we
+ -- can use IS { .. } syntax below to pack the state. Using a record update
+ -- wouldn't work, probably because Cairo.Surface contains an existential
+ -- type...
+ -- We put the buffer in an IORef, so we can change it for a new one
+ -- later on (on window resizes).
+ buffer' <- Cairo.createImageSurface Cairo.FormatARGB32 w h
+ buffer <- newIORef buffer'
+
+ -- Repack state
+ let state = IS { .. }
+
+ -- Register events
+ Gtk.on window Gtk.exposeEvent $ onExpose buffer
+ Gtk.on window Gtk.configureEvent $ onResize buffer
+ Gtk.on window Gtk.keyPressEvent $ onKeyDown state
+
+ -- Process any initial requests
+ processPostponed state
+
+ -- Show the window and start the Gtk mainloop.
+ Gtk.widgetShowAll window
+ Gtk.mainGUI
+
+-- | Called when a key is pressed.
+onKeyDown :: IState -> EventM.EventM EventM.EKey Bool
+onKeyDown s = do
+ keyval <- EventM.eventKeyVal
+ case Gtk.keyToChar keyval of
+ Just c -> liftIO $ post s (KeyIn c)
+ Nothing -> return ()
+ return True -- No clue what this means
+
+-- | Called when (part of) the window should be redrawn. Copy the given surface
+-- to the exposed window on an expose event.
+onExpose :: IORef Cairo.Surface -> EventM.EventM EventM.EExpose Bool
+onExpose buffer = do
+ current_buffer <- liftIO $ readIORef buffer
+ dw <- EventM.eventWindow
+ -- Copy the buffer to the window
+ liftIO $ Gtk.renderWithDrawable dw $ do
+ Cairo.setSourceSurface current_buffer 0 0
+ Cairo.paint
+ return True -- No clue what this means
+
+-- | Called when the window is resized. Resize the given buffer if needed.
+onResize :: IORef Cairo.Surface -> EventM.EventM EventM.EConfigure Bool
+onResize buffer = do
+ -- Get the current buffer and see if it's still big enough
+ current_buffer <- liftIO $ readIORef buffer
+ sw <- Cairo.imageSurfaceGetWidth current_buffer
+ sh <- Cairo.imageSurfaceGetHeight current_buffer
+ -- Get the current drawwindow and its size
+ dw <- EventM.eventWindow
+ (w, h) <- liftIO $ Gtk.drawableGetSize dw
+ when (w > sw || h > sh) $ liftIO $ do
+ -- Buffer is too small, expand it.
+ new_buffer <- Cairo.createImageSurface Cairo.FormatARGB32 w h
+ -- Fill it with white and copy the old buffer
+ Cairo.renderWith new_buffer $ do
+ Cairo.setSourceRGB 1 1 1
+ Cairo.paint
+ Cairo.setSourceSurface current_buffer 0 0
+ Cairo.paint
+ -- Clean up the old buffer
+ Cairo.surfaceFinish current_buffer
+ -- Store and return the new buffer
+ writeIORef buffer new_buffer
+ return True -- No clue what this means
+