GLib callbacks

Authors of Gtk+/GLib/GObject are pretty clever in the way they coded these libraries to be usable from other languages. As an example, it gives a lot of freedom in the way callbacks are passed around. Callbacks in GUI libraries are everywhere – they're essential for event handling. In particular, GLib accepts not just pointers to C functions, but also a GClosure object.

Here's what I find convenient about GClosure:

  • Firstly, GClosure contains user data in a standardized way
  • Secondly, each GClosure can have its own function for marshaling data and calling external functions. This function makes it possible to accept a generic list of GValues and returns a GValue
  • Thirdly, when GClosure uses reference counting and when it gets destroyed, it calls its finalization function (which can be customized).

As a result of all of that, in order to pass Lisp closures as Gtk+ signal handlers, I need to write just two callback functions: one for marshaling and one for finalization. And those two functions are reusable for any kind of GClosure.

And here's what we get out of that:

  • CFFI gives us enough tools to create such callbacks. CFFI has a limitation in that it supports creating C callback only from free functions (i.e., closures are not supported);
  • Closures can be freely passed as signal handlers;
  • Closures can be freed by the garbage collector when the corresponding widget is deleted.

So it's quite convenient to use closures as signal handlers, and there are no memory leaks resulting from that.

Here's an example of what can be done:

(g-signal-connect-closure
  button
  "clicked"
  (bare-gtk::create-closure
    (let ((count 0))
      (lambda (widget) (format t "Нажал ~A раз~%" (incf count)))))
  +false+)

The two high-quality Gtk+ bindings, lgtk and clg, use SBCL/CMUCL/CLISP-specific features to handle callbacks while I try to stay within the bounds of CFFI for portability.