X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=FPPrac.hs;h=14e8c4f872036b122449cf12242e3338936520c5;hb=25a54fccc78a8c85e3196428c677e213c1eab23e;hp=47b3cad30e0c80c06efa2445adeebc50b9ab7e62;hpb=b4799aaeb1d9b38300143aa3e70d3bd36307a781;p=matthijs%2Fprojects%2Ffpprac.git diff --git a/FPPrac.hs b/FPPrac.hs index 47b3cad..14e8c4f 100644 --- a/FPPrac.hs +++ b/FPPrac.hs @@ -30,13 +30,14 @@ data Rect = Rect data Point = Point !Int !Int deriving (Show, Eq) -type Color = Gtk.Color +-- A color, with RGB values from 0 to 1 +data Color = Color Double Double Double deriving (Show, Eq) -- Create a Color from Red, Green and Blue values. The inputs should be -- between 0 and 255 (inclusive). rgb :: Int -> Int -> Int -> Color -rgb r g b = Gtk.Color (conv r) (conv g) (conv b) - where conv = fromInteger . toInteger . (*256) +rgb r g b = Color (conv r) (conv g) (conv b) + where conv = (/256) . fromIntegral -- | Some predefined colours red = rgb 0xff 0 0 @@ -99,6 +100,7 @@ testProg = Main data IState = forall s. IS { window :: Gtk.Window + , buffer :: IORef Cairo.Surface , postponed :: IORef [Request] , usrState :: IORef s , usrProg :: TinaStep s @@ -132,11 +134,11 @@ runTina :: TinaProgram -> IO () runTina Main {..} = do usrState <- newIORef initialState postponed <- newIORef (GfxText (rgb 0 0 0) (pt 50 50) "foo" : GfxClear :initialRequests) - let state = IS { window = undefined, usrProg = eventHandler, .. } + let state = IS { window = undefined, buffer = undefined, usrProg = eventHandler, .. } runGUI windowWidth windowHeight state runGUI :: Int -> Int -> IState -> IO () -runGUI w h s = do +runGUI w h (IS { .. }) = do -- Init GTK. Gtk.initGUI @@ -150,14 +152,40 @@ runGUI w h s = do , Gtk.windowDefaultHeight := h ] Gtk.onDestroy window Gtk.mainQuit - - -- Add the window to the state - let state = s { window = window } + -- 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' + + -- Register the expose event + Gtk.on window Gtk.exposeEvent $ onExpose buffer + + -- Repack state + let state = IS { .. } + + -- Process any initial requests + processPostponed state + -- Show the window and start the Gtk mainloop. Gtk.widgetShowAll window Gtk.mainGUI + +-- | 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 {- runGUI s IS {..} = do @@ -208,6 +236,8 @@ transKey _ _ = return () -} +-- | Handlers for various requests. +miscHandler, winHandler, gfxHandler :: IState -> Request -> Maybe (IO ()) {- miscHandler s@IS {..} (FRead fn ) = Just $ readFile fn >>= post s . FileContents fn miscHandler IS {..} (FWrite fn cnts) = Just $ writeFile fn cnts @@ -233,6 +263,13 @@ winHandler s@IS {..} (WinMenu ms) = Just $ mkMenu >>= \ms' -> sFrame `set` -} winHandler _ _ = Nothing +gfxHandler s req = case gfxHandler' req of + Nothing -> Nothing + Just render -> Just $ do + buf <- readIORef (buffer s) + Cairo.renderWith buf render + Gtk.widgetQueueDraw (window s) + {- gfxHandler IS {..} (GfxLines col ps) = Just $ polyline buffer ps [penColor := col] >> dirtyPts sPanel ps gfxHandler IS {..} (GfxPolygon col ps) = Just $ polygon buffer ps [penColor := col, brushColor := col] >> dirtyPts sPanel ps @@ -241,10 +278,26 @@ gfxHandler IS {..} (GfxRectangle col rt) = Just $ drawRect buffer rt [penColo gfxHandler IS {..} (GfxEllipse col rt) = Just $ ellipse buffer rt [penColor := col, brushKind := BrushTransparent] >> dirtyRect sPanel rt gfxHandler IS {..} (GfxDisc col rt) = Just $ ellipse buffer rt [penColor := col, brushColor := col] >> dirtyRect sPanel rt gfxHandler IS {..} (GfxFont st sz) = Just $ buffer `set` [ fontSize := sz, fontFace := st ] -gfxHandler IS {..} GfxClear = Just $ dcClear buffer >> windowRefresh sPanel False gfxHandler IS {..} (GfxPicture fd pt) = Just $ bitmapCreateFromFile fd >>= \bm -> drawBitmap buffer bm pt False [] >> bitmapGetSize bm >>= dirtyRect' sPanel pt -} -gfxHandler _ _ = Nothing +-- | Helper function for gfxHanlder +gfxHandler' :: Request -> Maybe (Cairo.Render ()) +gfxHandler' (GfxText col (Point x y) st) = Just $ do + -- Set the source color, move to the requested position and draw the + -- text + setSourceColor col + Cairo.moveTo (fromIntegral x) (fromIntegral y) + Cairo.showText st +gfxHandler' GfxClear = Just $ do + -- Set the source to white and paint the entire surface with it + Cairo.setSourceRGB 1 1 1 + Cairo.paint +gfxHandler' _ = Nothing + +-- | Sets the source to a pattern fill of the given color +setSourceColor :: Color -> Cairo.Render () +setSourceColor (Color r g b) = + Cairo.setSourceRGB r g b {- dirtyPts :: Window a -> [Point] -> IO ()