haskellcairogtk2hs

What is the right way to render Cairo updates in Gtk2hs?


I'm writing a Haskell program with click-and-drag functionality, so with each mouse movement event an update gets painted to the window. At the moment I'm using

renderWithDrawable myCanvas update

However this is flickering a lot. My understanding is that I need to create a separate drawable (a "surface"?), render to that, and then blit it onto the screen window in a single operation. However I'm confused as to the right way to do this.

I've found drawWindowBeginPaintRegion, which talks about eliminating flicker. However it is removed in Gtk3 according to the Haddock docs. So I'm not sure if I should use this, as it seems to be deprecated.

I've also found renderWithSimilarSurface in Cairo, which seems to do something similar.

I'm also not sure how these functions relate to renderWithDrawable: do I have to use them inside that function, or what?

What is the right way to do this?

Edit

This seems to be a known thing in Cairo. I'm trying to figure out how to handle this in Haskell.


Solution

  • The right way to do this is to make sure all your drawing comes from within expose events, and operates on the draw window provided by the event. You can mark a region as "dirty" and trigger a synthetic expose event using drawWindowInvalidateRect, drawWindowInvalidateRegion, or widgetQueueDraw.

    A quick worked example of setting up the drawing pipeline follows. It is excerpted from a custom Viewport type, which does Google-maps style panning with smooth motions on drag-and-release operations, that I built for a side-project some time ago. To support that, it has to redraw on mouse motion events, so it addresses a similar use case to your described problem. I've elided irrelevant stuff with ... to highlight the important bits. I've uploaded the complete project to github just now, so you can browse the repo to see the full details of Viewport. (It's been years though, so there's probably a fair bit of bitrot -- don't expect the project to just build and run with modern GHCs/packages.)

    viewportNew :: Viewport -> IO DrawingArea
    viewportNew v = do
        da <- drawingAreaNew
        -- ...
        on da exposeEvent $ exposeViewport posRef (draw v)
        -- ...
    
    exposeViewport :: IORef Position -> RegionRenderer -> EventM EExpose Bool
    exposeViewport posRef draw = do
        dw      <- eventWindow
        region  <- eventRegion >>= liftIO . regionGetRectangles
        -- ...
        liftIO . renderWithDrawable dw $ do
            -- Cairo () action goes here
            -- can reference region to decide which things to draw
            draw region
        return True -- see documentation of exposeEvent for what this means
    

    This template should take advantage of gtk's built-in double-buffering and work with both the gtk and gtk3 packages.