For some reason I got to playing with CLISP, the GNU Common Lisp implementation, and using its foreign function interface (FFI) to call C code from Lisp.
The result is a little GUI library, which can use GTK+ 2.0 (or presumably later versions) to open a window with a button in. That's about it so far, so it's only useful as an example. You can download it if you're interested: lisp-gui-0.tar.gz (contains a README file explaining how to build and run it)
It turns out to be quite easy to get this sort of thing going. You can call C functions by telling CLISP what arguments they take and binding them to a lisp function name, and then you can just call them. It's probably possible to bind GTK+ only using the FFI, although since I don't know much about this I found it easier to do a few bits in C. I was able to put them in a little DLL and load it dynamically.
The process of declaring all the C functions is a bit tedious, but of course you can define macros to help.
Here's my little example program:
#!/usr/bin/clisp (load "gui") (use-package 'gui) ; Prints a message, and demonstrates that a callback can be a closure. (let ((x 1)) (defun hello-world () (format t "hello world: ~d~%" x) (setq x (1+ x)))) ; Display a window with a button in. (open-window "Foo" (button "Hello world" :on-click #'hello-world) :on-close (lambda () (gui-quit) t)) ; Make events happen. Nothing ever happens beyond this point. (gui-main-loop)
The callbacks work very nicely. Somehow the FFI arranges for
closures to be translated into a C void*, which encodes both
the function and it's environment. At least I think that's what it's
doing.
Note that I've hardly done any work with lisp, so this stuff isn't necessarily a shining example of how it should be done, but it shows that it's not that hard to get started with.