cairo-nickle

Mon, 02 Oct 2006 22:05:43 -0700

A Nickle Binding for cairo

Ok, so nickle isn't the worlds most popular language. So why would I spend a couple of days building a binding to cairo for it? Mostly because I like to code in nickle and I wanted some way to get simple graphics up on the screen for plotting and the like. However, it's also been a good way to get back into the heart of cairo and see how things look.
Instead of a "direct" binding to cairo, I've created a slightly higher level mechanism where nickle exposes none of the underlying window system details and instead simply provides a 'window' upon which things can be drawn. This means that the process of drawing is as simple as:

    autoimport Cairo;
    cairo_t cr = new ();
    scale_font (cr, 12);
    move_to (cr, 10, 20);
    show_text (cr, "Hello, World!");

The back end code uses gtk+ instead of raw Xlib because that way I get all of the ICCCM hints set and a whole lot of help with threading. I run the gtk+ code in a separate thread from nickle and have nickle just paint bits through cairo to a pixmap. Wake up the gtk+ thread and have it copy the pixmap to the window. Resize is handled by creating a new pixmap of the new size, copying the old data (or as much as will fit) to the new pixmap and repainting the screen.
The gtk+ thread also receives input, and writes simple string events to a pipe which can be read by the nickle code without a lot of hassle. Right now, it just does mouse input, keyboard input is a lot harder, so I'll leave that until I really need it. Probably I'll send key up/down events and also UTF-8 strings; that way apps can get what they need without a lot of work.
The other piece of this work is in learning how to implement a usable foreign function interface in nickle. I've put off this work in the hope that I'd have a sudden flash of brilliance about how to make this all just work without a lot of per-library stub writing. After doing all of that stub generation by hand, I learned that there is a lot of repetitive stuff, but also a huge amount of semantic knowledge about how both cairo and nickle work needed to successfully glue things together.
The biggest problem (no surprise here) was the difference of memory management methods. Nickle, like any sensible language, has automatic storage allocation with a garbage collector. cairo, like any sane C libary uses reference counts with no recursive data structures. It's not hard to get a finalizer called from nickle to dereference the cairo object. The trick is knowing what other places need references added or subtracted. And that takes an intimate knowledge of the cairo internals, something which can't be automatically abstracted from the headers. So, the goal of automatically gaining access to cairo from nickle without writing any cairo-specific code doesn't seem entirely possible.
Still, it seems like at least some of the work needed to generate a language binding can be automated; the busy work of creating nickle datatypes cooresponding to the exposed cairo structures and enums could surely be streamlined somehow. And, the constant nickle↔C datatype conversions should permit some automation. But, if that automated result needs to be hand-edited to make it usable, the utility of automatic generation is greatly reduced; it wouldn't aid maintenance of the resulting library at all. Future experimentation is clearly needed here.
For those with an experiemental bent, the bits needed for all of this are available from CVS on either nickle.org or cairographics.org.