Thursday, November 27, 2008

Fun with CFFI and wxWindows/wxWidgets

I've mentioned before that there isn't really a sensible way to write a multi-platform GUI app with a free Common Lisp implementation. There are GTK bindings, but there is no proper GTK for MacOS. There are very good Cocoa bindings in Clozure CL, but Cocoa is MacOS only. There's a tk-based library, but, well, you've seen tk, right? There's a QT library, but QT is dual commercial/GPL, and therefore unsuitable for most software that most people might want to write. And the list goes on.

So, I decided to have a shot at doing something about it. Now, it turns out that there is a binding for wxWidgets (previously wxWindows; changed after some words with Microsoft). And wxWindows works, and looks, relatively nice, on Windows (using Win32), MacOS (using Carbon), and various Unixy things (using GTK or a direct X11 interface). Sounds perfect, right?

The catch is that it hasn't been touched in years, and supports only an old version of wxWidgets, with no Unicode. The version that it supports is, in fact, too old to run on MacOS Leopard. However, it gave me the idea to look into doing my own.

The reason that it only works on old versions of wxWidgets is that it uses an old, unmaintained fork of wxc, a C interface to wxWidgets; wxWidgets itself is in C++, and foreign function interfaces to C++ things are notoriously tricky. There are various other users of forks of wxc, and the most active seems to be wxHaskell ; their fork (the version in their DARCS repository, not the release) actually works with the latest wxWidgets.

So, I took and compiled that against the version of wxWidgets which comes with Leopard. And some messing around and much cursing and swearing later, well, I've got enough of a Lisp interface to produce this:



(The Japanese is from Google.jp, and is there purely to demonstrate that it can handle Unicode.)

Biggest problem thus far was the text; I had vaguely expected it to accept ASCII cstrs, but in fact the Unicode build of wxWidgets uses null-terminated w_char arrays, which are 4 byte (UTF-32) on MacOS. This turns out to be easy to deal with; you can either change cffi:*default-foreign-encoding* or do it on a per string basis with allocate-foreign-string. Initially tricky to debug, though, as it's my first time working with passing UTF-32 to C things.

This should, by the way, work fine on all supported platforms (with UTF-16 instead of UTF-32 on Windows); I haven't had a chance to test this yet, though.

I do plan to continue working on this and eventually produce a usable library; it's quite a way from that at the moment, and only provides minimal functionality. I like it, though; it opens the possibility of making distributable, native-interface apps on all of the major platforms in Common Lisp, which is nice.

Oh, by the way, distributable? That app above works out to 9MB compressed when compiled with SBCL, if you include the wxc library. A little hefty, perhaps, but it shouldn't grow too much in size with some actual functionality; it's largely SBCL overhead...

I do, by the way, have an application in mind to use this for. Tell you later. :)

6 comments:

Victor said...

Great news!

You could also publish your code to the repository...

Robert Synnott said...

Oh, I plan to once it's a little closer to usable.

Anonymous said...

With TCL8.5 the bad looking argument is no longer valid. I Don't think LTk is directly compatible with it though.

Victor said...

I've upgraded to Tcl/Tk 8.5 and LTk works just fine.

Anonymous said...

I don't think you have acces to the new themeable widgets of Tcl/Tk 8.5 without reworking a bit LTk, but I can be wrong.

Robert Synnott said...

Besides that, last time I looked at it, LTK had a pile of weird little issues, especially on Windows.

Also, IMO distributing a separate TK/wish binary is more problematic than distributing a GUI DLL from an installation point of view.