gui

...now browsing by tag

 
 

A random note on programming with Gtk2Hs.

Monday, January 19th, 2009

Gtk has quite a few conventions that are entirely unintuitive and make for some very interesting bugs in your app if you’re not aware of them.  For instance, calling a redraw function inside an event handler is wrong.  You wouldn’t know that, because no-one really says it anywhere, but let’s say you have a Gtk window that holds an OpenGL scene or a Cairo scene.  If you call your render function at the end of a Gtk mouse button handler, then you stand a small chance, especially if your app is multithreaded of the whole thing coming crashing down with an unintelligible error.  Instead, you should so something like this:

mouseMotionHandler :: UIState a => MVar a -> Gtk.Event -> IO Bool
mouseMotionHandler uistate_mvar event = do
    state <- takeMVar uistate_mvar
    putMVar uistate_mvar
        $ setMousePosition (Point (Gtk.eventX event) (Gtk.eventY event)) state
    dwin <- Gtk.widgetGetDrawWindow . fromJust . drawing $ state
    Gtk.drawWindowGetPointer dwin
    Gtk.drawWindowInvalidateRect dwin (Gtk.Rectangle 0 0 (round . sizeX $ state) (round . sizeY $ state)) False
    return False

The important code here is in the next to last line, Gtk.drawWindowInvalidateRect.  This code tells the Gtk main loop that the window area in dwin is dirty and that an “expose” event needs to occur.  The sizeX and sizeY of the rectangle should be the width and height of the window.  The dwin is obtained by calling Gtk.widgetGetDrawWindow on any widget with a drawing window (this is a Window, a DrawingArea, or a GLDrawingArea). Then your expose handler will look like this:

Gtk.onExpose canvas $ evt -> renderer state scene >> return True

where renderer is the name of the function that actually draws your scene on the drawing window.  Once you’ve setup your event handlers like this, you can switch to a multithreaded app, and at least your event handlers won’t cause your program to die.