Search code examples
haskellgtkgtk2hs

Haskell GTK, double buffering with primitives


With an example like this. How can I do 2d double buffering with gtk and haskell. I want to render primitives to an offscreen buffer and flip. This code only renders a pixel/rectangle. I want to add movement using a double buffered approach.

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Gdk.GC
import Graphics.UI.Gtk hiding (Color, Point, Object)

defaultFgColor :: Color
defaultFgColor = Color 65535 65535 65535

defaultBgColor :: Color
defaultBgColor = Color 0 0 0

renderScene d ev = do
    dw     <- widgetGetDrawWindow d
    (w, h) <- widgetGetSize d
    gc     <- gcNew dw
    let fg = Color  (round (65535 * 205))
                    (round (65535 * 0))
                    (round (65535 * 0))
    gcSetValues gc $ newGCValues { foreground = fg }
    drawPoint dw gc (120, 120)
    drawPoint dw gc (22, 22)
    drawRectangle dw gc True 20 20 20 20
    return True

main :: IO ()   
main = do
    initGUI
    window  <- windowNew
    drawing <- drawingAreaNew
    windowSetTitle window "Cells"
    containerAdd window drawing
    let bg = Color  (round (65535 * 205))
                    (round (65535 * 205))
                    (round (65535 * 255))
    widgetModifyBg drawing StateNormal bg
    onExpose drawing (renderScene drawing)

    onDestroy window mainQuit
    windowSetDefaultSize window 800 600
    windowSetPosition window WinPosCenter
    widgetShowAll window
    mainGUI

Solution

  • This is what I'm using to paint with cairo in a drawing area and avoid flickering. Try adding this code to your renderScene function:

      -- Get the draw window (dw) and its size (w,h)
      -- ...
    
      regio <- regionRectangle $ Rectangle 0 0 w h
      drawWindowBeginPaintRegion dw regio
    
      -- Put paiting code here
      -- ..
    
      drawWindowEndPaint dw
    

    Your final code could look like this:

    import Graphics.UI.Gtk
    import Graphics.UI.Gtk.Gdk.GC
    import Graphics.UI.Gtk hiding (Color, Point, Object)
    import Data.IORef
    
    defaultFgColor :: Color
    defaultFgColor = Color 65535 65535 65535
    
    defaultBgColor :: Color
    defaultBgColor = Color 0 0 0
    
    renderScene pref d _ev = renderScene' pref d
    
    renderScene' :: IORef Int -> DrawingArea -> IO Bool
    renderScene' pref d = do
        dw     <- widgetGetDrawWindow d
        (w, h) <- widgetGetSize d
        regio <- regionRectangle $ Rectangle 0 0 w h
    
        pos <- readIORef pref
        -- Go around, CCW, in a circle of size 20, centered at (100,100)
        let x = 100 + round ( 20 * sin (fromIntegral pos * pi * 2 / 360) )
            y = 100 + round ( 20 * cos (fromIntegral pos * pi * 2 / 360) )
            pos' = (pos + 1) `mod` 360
        writeIORef pref pos'
    
        drawWindowBeginPaintRegion dw regio
        gc     <- gcNew dw
        let fg = Color  (round (65535 * 205))
                        (round (65535 * 0))
                        (round (65535 * 0))
        gcSetValues gc $ newGCValues { foreground = fg }
        drawPoint dw gc (120, 120)
        drawPoint dw gc (22, 22)
        drawRectangle dw gc True x y 20 20
        -- Paint an extra rectangle
        drawRectangle dw gc True 200 200 200 200
        drawWindowEndPaint dw
        return True
    
    main :: IO ()   
    main = do
        initGUI
        window  <- windowNew
        drawing <- drawingAreaNew
        windowSetTitle window "Cells"
        containerAdd window drawing
        let bg = Color  (round (65535 * 205))
                        (round (65535 * 205))
                        (round (65535 * 255))
        widgetModifyBg drawing StateNormal bg
    
        pref <- newIORef 0
    
        onExpose drawing (renderScene pref drawing)
        timeoutAdd (renderScene' pref drawing) 10
    
        onDestroy window mainQuit
        windowSetDefaultSize window 800 600
        windowSetPosition window WinPosCenter
        widgetShowAll window
        mainGUI