Archive for the ‘Development’ Category

Cabalized version of Gtk2Hs now up for testing.

Wednesday, April 21st, 2010

Dear all,

the repo at code.haskell.org now contains a cabal version of Gtk2Hs, well, at least of the core packages glib, cairo, pango and gtk. In the future the package gio should be part of this set as well. All other add-on libraries, however, are likely to move into their own darcs repositories. During this transitional period it is not possible to build the other libraries but I encourage people to get the new cabal version using

darcs get http://code.haskell.org/gtk2hs

and to add cabal files to the any of these add-on libraries. Notes on that below (”Creating a cabal file for a Gtk2Hs add-on library”).

Observe that updating your current darcs repo of the previous version of Gtk2Hs is not possible since I had to obliterate several patches in order to merge changes. Furthermore, there are quite a few patches in the previous repository that added a lot of new functions but which need further review. These patches have been merged into the new cabal version but for now remain in a separate repository which can be accessed at http://www2.in.tum.de/~simona/gtk2hs-2.18 .

Building
——–

The Gtk2Hs libraries need additional build tools. These live in tools/ and can be configured and installed in the usual Cabal way. The three tools have awkward names starting with gtk2hs and are thus not likely to clash with other programs. If you install as –user, then you need to add ~/.cabal/bin to your path. (I’ve filed a bug that cabal should look in it’s own /bin directory for build utilities. http://hackage.haskell.org/trac/hackage/ticket/663 )

When configuring the gtk package, the cabal file will look for Gtk+ 2.20 which you most likely don’t have. You need to specify -f-gtk_2_20 for Gtk+ 2.18 and -f-gtk_2_20 -f-gtk_2_18 for Gtk+ 2.16 and so on. (I have filed a feature request that Cabal should be able to infer the best possible choice of flags for pkg-config requirements. http://hackage.haskell.org/trac/hackage/ticket/662 )

When compiling the Setup.hs program, you will see a warning that the Cabal version is guessed. If compilation succeeds, this warning can be ignored. (Cabal should really define CPP macros with it’s version when it builds Setup.hs, see http://hackage.haskell.org/trac/hackage/ticket/326 )

Creating a cabal file for a Gtk2Hs add-on library
————————————————-

The Setup.hs file for a Gtk2Hs add-on library will be the same as that for glib, pango, cairo and gtk. Thus, simply copy one of these files to the subdirectory of the add-on library you wish to cabalize. Even better: replace all Setup.hs files by a hard link. It would be good if all setup files remain identical.

The cabal file for a new add-on library is pretty much standard. You need to depend on gtk-0.10.5 and possibly others such as glib-0.10.5. Note that the core packages glib, cairo, pango, gtk will all evolve with the same version.

The category for the core files at the moment is Graphics or Rendering. Note that you could use cairo and pango to create PDF or PNG files without ever touching any GUI stuff. Thus, the category for the other libraries need to be Graphics. It would be interesting to hear if people think that Gtk2Hs should have it’s own category or not.

There are two sets of special fields: x-Types-XXX to build a type hierarchy and x-Signals-XXX to build a callback definitions. You should only need the former. If your package requires callback with arguments not provided in the gtk package then we need to add them to the Gtk package. This is unfortunate, but the infrastructure is not yet adjusted to build package-specific callback definitions.

As to the type hierarchy: The type generator comes with a list of built-in types is located in tools/hierarchyGen/hierarchy.list .
Any of the x-Type-XXX directives are basically specifying arguments to be passed to the type-file generator. You can find out about the arguments it takes by running ~/.cabal/bin/gtk2hsTypeGen without arguments.

For starters, you need to specify where the Types module should live. Use the following two directives:

x-Types-Files: Graphics/UI/MyLib/Types.chs
x-Types-ModName: Graphics.UI.MyLib.Types.chs

Now there are two variants on how you might want to use the type generator:

(a) Building an add-on library that relies only on glib, cairo and/or pango. Specify:

x-Types-Forward: *System.Glib.GObject

(b) Building an add-on library that depends on the gtk package. Specify:

x-Types-Forward: *System.Glib.GObject *Graphics.UI.Gtk.Types
x-Types-Destructor: objectUnrefFromMainloop

The deeper meaning of the different is that any library building on Gtk is assumed to be GUI library that interacts with X11 or Win32 and, thus, any object that is destroyed may release an X11 or Win32 resource which, in turn, may not be done from arbitrary threads. The above option ensures that the freeing happens in the Gtk+ main loop which ensure that the objects are freed from the correct thread. Libraries building on glib, cairo and pango do not interact with X11 or Win32 nor do they have a main loop. Here, freeing objects is done directly by GHC’s garbage collector.

One note for gstreamer: This package requires two type hierarchies. This is currently not supported by the Setup.hs script but it should not be too hard to add this.

The files of each individual add-on libraries are often .chs, .chs.pp and .hsc files. No changes are normally necessary for .chs and .hsc files. Since Cabal is not able to run more than one pre-processor on a file (it cannot handle several extensions) every .chs.pp file must be renamed and a {-# LANGUAGE CPP #-} directive must be prepended to the file. I used the following bash script to do this:

#! /bin/bash
OLD=$1;
NEW=`dirname $1`/`basename $1 .pp`;
rm -f $NEW
darcs mv $OLD $NEW
mv $NEW “$NEW”.tmp
echo “{-# LANGUAGE CPP #-}” > $NEW
cat “$NEW”.tmp >> $NEW
rm “$NEW”.tmp

(save this in change.sh). In order to convert all files in the library myLib do:

find myLib -name “*.chs.pp” -exec ./change.sh “{}” \;

in the directory containing myLib.

You need to add any other language extension to each individual file that need it (e.g. replace “CPP” with “CPP, ScopedTypeVars” or whatever). Note that any file that is now called .chs and has a CPP directive in its first line is now run through cpp by our home-grown c2hs variant (making it even more incompatible with the mainstream c2hs).

A note on the new Setup
———————–

For those who are curious how the Gtk2Hs Setup.hs differs from Simple, the changes are as follows:

- during configuring, the signal and type files are generated. Both only happen if the appropriate x-Signals- and x-Types- fields are specified in the cabal file.

- our home grown c2hs is run on .chs files instead of the mainstream one

- our c2hs files produces .chi files that must be available to other packages, just like .hi files. These .chi files are therefore copied during installation of files

- before pre-processing the .chs files, we calculate the dependencies between the files and rearrange the list of files to build such that the dependencies are honored

That’s it!

Any feedback appreciated,
Axel (on behalf of the Gtk2Hs developers)

New Gtk2Hs pre-release in time for xmas

Sunday, December 24th, 2006

So we didn’t quite make our deadline of a 0.9.11 release before xmas but I think we’re pretty close. We are at least into the release phase.

You can now grab a tarball of Gtk2Hs version 0.9.10.3:
http://haskell.org/gtk2hs/gtk2hs-0.9.10.3.tar.gz
Try to build it, test it and report problems back to us.

As for the issue of the new list/tree widget system, we’ve decided to include the new api in parallel with the old. This means we’re not yet breaking anyone’s old code while providing access the new api. We’re not promising yet that the new api is perfect but that’s partly why we want to get it included in a release - so that people can try it and tell us what’s missing. To get at it just:

import qualified Graphics.UI.Gtk.ModelView as New

You really have to use import qualified because most of the names clash.

You can also check out the demos in the demo/treeList and demo/profileviewer directories.

After xmas we’ll follow up with some proper release candiate tarballs, lots more testing, and we’ll give you the details on what minimal api changes there are in this release.

In other news, after writing us a great glade tutorial, Hans van Thiel has been hard at work on an introductory tutorial. With any luck that’ll be available around the same time as the 0.9.11 release.

The Gtk2Hs code generator

Sunday, November 12th, 2006

Part 1: the original version

Originally Gtk2Hs was all written by hand. This was a pain as the Gtk+ API is really quite big and there’s a great deal of repetion. So I wrote a Haskell program to generate the api.

The C API

Of course it’s a good deal more complicated than that. I started with a bunch of scripts written by the Gtk# people. They have these perl scripts that scan the C header and implementation files to extract a highish level API for GObject-based C libs (like Gtk+ and many other GNOME modules). It produces an XML file that describes the name spaces, objects, signals, methods, parameters and types etc. It’s quite detailed. Crucially, it provides a description of the API at the GObject level, not the C level. This means we can translate into the way we bind the GObject features in Gtk2Hs.

Lots of XML

So I started by extracting info from the API XML file (using HaXml) and generated Haskell modules, one for each object/widget type. I tried fairly hard to match the existing hand written style. Of course it couldn’t be that close to the hand written modules because the order of declerations, extra imports, and many other things were different. So we needed more information. So the next step was to scan the existing .chs Haskell modules and glean them for information like the order of declerations and exports, the authors, creation date, copyrights and the file kind (.chs or .chs.pp). Then applying this info meant I could generate code much closer to the original hand written stuff.

Still not enough

Many types were not known because they are not defined in Gtk+, but in lower level libraries like Gdk and glib/gobject. That wasn’t too hard since we just generate the API XML files for those too and read them in and go through them to find the object types defined in them.

Too much

There are loads of deprecated or semi-internal functions that we do not want to bind. We already had a system for excluding functions we were not interested in, and a whole bunch of api.exclude files listing the ones we were not interested in. So I made the code generator read these in too and use them to remove the functions we’re not interested in.

And after lots of hacking…

With lots of tweaking, this was enough to generate reasonably sane code for most modules. There were places where the C API doesn’t provide enough information to write a Haskell binding so I had the code generator emit FIXMEs and those bits needed to be edited by hand. So Axel and I started merging changes from the generated code to the hand written version. This made our code much more regular rather than different modules having slightly different code styles. It also uncovered a large number of bugs where we’d made mistakes when doing it by hand, just because as humans we aren’t good at doing repetitive stuff without making mistakes (common examples included getting arguments in the wrong order).

Success!

Of course one of the main points of the code generator was to make it easier to bind new Gtk+ modules. Each major release of Gtk+ brings a slew of new APIs but with the code generator it became much quicker to bind the new stuff and to work on the backlog of stuff we’d never bound in the first place.

Documentation

Documentation is pretty important, especially for a big API like Gtk+. We didn’t want to make everyone use the C API documentation because it referrs to lots of C concepts you’d keep having to translate C names to Haskell names in your head. It would also prevent us from deviating much from the C api if we had to use the C docs. On the other hand, since the API is large and well documented it would be a massive task to translate it all. In fact translating the C docs would probably have been more work than binding the C code. So the idea was to automagically translate the docs from the C API to our Haskell API. This was pretty tricky but turns out to work well.

Yet more XML

Again, I started with someone else’s scripts. The Gtk+ devs already have an inline doc markup format and a gtk-doc program for generating docbook XML from the markup and scanning the C api and implementation. Again, it documents it at the level of GObjects rather than generic C. The docbook XML that it produces isn’t very nice, though it is at least fairly regular and only uses a subset of features. However it was still necessary somewhat to reverse engineer the GObject level documentation from the generated docbook we by looking for specially named sections etc. I did this using a complicated XSLT script. Then the code generator reads the XML file produced by the XSLT script. It then tries to match up the documentation to the modules, methods, parameters, signals, properties etc.

C -> Haskell

Then it also has to try to ‘Haskellise’ the documentation. So references to C types and C functions have to be translated into the equivalent Haskell types and functions. Other constants like TRUE, FALSE etc have to be converted. NULL is an interesting case, because if the documentation talks about NULL then it suggests that one of the function’s pointer-type parameters is allowed to be NULL (or the result might be NULL). So I had NULL translated into a FIXME message indicating that the function should be looked at carefully. Then I added a list of functions where we knew a specific parameters/result might be NULL. Then I used this list to generate code that wrapped that paremter/result in the Maybe type and also to suppress the FIXME message in the documentation. So as we went through and addressed each of the FIXMEs we could just add the function-parameter pair to the list and have the code generator give us the right code and docs.

And in conclusion…

So in the end the docs work out pretty well. Some bits still need editing by hand because they refer to C things like who is responsible for freeing what but much of the time hadly any editing is necessary. So we ended up with really pretty good reference documentation. The docs are still huge of course because the API is so big. How we plan to deal with that is another story.

In theory the code generator should work for other GObject-based libraries, like other things in the GNOME stack. In practice when I used it for some other GNOME libs it need a little tweaking, but it’s still much easier than binding everything from scratch, if only because it provides a great template. So I guess the moral of the story is, if the code is to big to write yourself then write a program to write your program, and of course Haskell is a great language for writing code generators. But then you already knew both of those things.

What’s next

So as you’ve gathered, the code generator is pretty complicated and full of tweaks to deal with various wierdnesses. It also grew over time to take in more and more sources of information. So it became pretty unweildy. So when I next find some time I’ll talk about restructuring it to be more maintainable so we can add yet more features! :-)

The code

You can see the current version of the code in the Gtk2Hs darcs repo here:
darcs.haskell.org/gtk2hs/tools/apiGen/

SVG Cairo goodness

Monday, January 9th, 2006

I’ve been hacking on binding the libsvg-cairo library as an add-on to the existing cairo bindings. It’s actually quite a nice small API.

[img]

Gtk2Hs can already load SVG files, but only as fixed size bitmaps. With this API we get a proper vector method. Since the drawing is done via cairo in the Render monad we can apply any 2D transformation. These screenshots are of a simple svg viewer. It’s only 30 lines of code overall. The following snippet is what does the actual drawing.

win <- drawingAreaGetDrawWindow canvas
let (width, height) = sizeSVG svg
(width', height') <- drawingAreaGetSize canvas
renderWithDrawable win $ do
  scale (realToFrac width' / realToFrac width)
        (realToFrac height' / realToFrac height)
  renderSVG svg

You can see that we setup the scaling transformation before rendering the svg picture. We scale the image so that it fits the size of the canvas. So as we resize the window the image scales nicely.

[img]