talkingCode

Archive for the c category

GTK, Glade, Haskell and gnome_program_init()

posted by codders in c, code, gtk, haskell

So. I finished the book (at length, and to be fair I mostly skimmed the last chapters), which means it’s time for me to start actually writing Haskell code. I thought I’d start with a simple GUI app, but it turned out not to be quite so simple.

I’ve put the code that I’m working on up on GitHub because that’s what all the cool kids are doing. My project is called GP3 for reasons that ought eventually to become clear. HEAD at time of writing is 7b01940

Glade
Glade is a GTK UI designer. I won’t go in to a lot of detail – there’s been plenty written about it. What I will say, though, is that at version 3, you can often find yourself creating unexpected dependencies for your program by using the more complex widgets. I unwittingly picked something from the “GNOME User Interface” toolbox, which has a Glade class of “GnomeApp”. This includes a “BonoboDock” and a “BonoboDockItem”.

Launching your app
Borrowing heavily from the book, here’s part of the code I was using to launch my app:

main :: FilePath -> IO ()
main gladepath =
  do
    unsafeInitGUIForThreadedRTS
    timeoutAddFull (yield >> return True) priorityDefaultIdle 100
    gui <- loadGlade gladepath
    connectGui gui
    windowPresent (mainApp gui)
    mainGUI

gnome_program_init()
Having made the mistake of using a GNOME-UI widget, I saw this when I ran my app:

GnomeUI-ERROR **: You must call gnome_program_init()
          before creating a GnomeApp

This is because my Glade UI requires libgnomeui to be initialised. To make matters worse, libgnomeui isn't linked by default in to Gtk2Hs. In my limited understanding of Haskell and Gnome, there are two options at this point. One is to import the gnome_program_init function from libgnomeui over FFI. The other is to write a C program to wrap a call to gnome_program_init and re-export a simpler function for you to import over FFI. I chose the latter option:

// gtk_docker.c
#include 

void do_gnome_init()
{
  static char **argv = NULL;
  if (argv == NULL)
  {
    argv = malloc(2);
    argv[0] = "gtk_docker";
    argv[1] = '\0';
  }
  gnome_init("my-app", "my-version", 1, argv);
}

... and a header file

// gtk_docker.h
void do_gnome_init(void);

Astute observers will see that this is a bit of a cheat. GnomeUI wants the command line arguments that were passed to the executable. It would be possible, but irritating, to arrange this. I couldn't easily divine how to pass an array of CStrings over FFI, so I wimped out. Also, I'm not technically calling gnome_program_init - this call appears to be deprecated in favour of gnome_init, and the latter call also silences the error message.

Compilation
We haven't solved the compilation problem yet. The compiler still needs to know where to find gnome.h and its included headers, and needs to know where to find the associated libraries for linking. There are good ways and bad ways to solve this problem... here's a bad way:

#Makefile
LDFLAGS = -lgnomeui-2 -lcairo -lglade-2.0
CFLAGS = -I/usr/include/libgnomeui-2.0 -I/usr/include/gtk-2.0/ \
              -I/usr/include/cairo/ -I/usr/include/glib-2.0/ \
              -I/usr/lib/glib-2.0/include/ \
              -I/usr/include/pango-1.0/ \
              -I/usr/lib/gtk-2.0/include/ \
              -I/usr/include/atk-1.0/ \
              -I/usr/include/libgnome-2.0/ \
              -I/usr/include/libbonobo-2.0/ \
              -I/usr/include/libgnomecanvas-2.0/ \
              -I/usr/include/libart-2.0/ \
              -I/usr/include/libbonoboui-2.0/ \
              -I/usr/include/gnome-vfs-2.0/ -Werror -Wall

GHCC=ghc

default: gp3

gp3: gtk_docker.o GP3Main.hs GP3GUI.hs
        $(GHCC) --make $(LDFLAGS) $^

clean:
  rm -f *.o *.hi GP3Main

The real answer probably involves GNU AutoTools for the C toolchain or some craziness with Cabal. I'm sure I'll get round to that :)

Calling the function
Now to clear that error message. We just need to...

{-# LANGUAGE ForeignFunctionInterface #-}

foreign import ccall unsafe "gtk_docker.h do_gnome_init"
      c_gnome_init :: IO ()

and

main :: FilePath -> IO ()
main gladepath =
  do
    unsafeInitGUIForThreadedRTS
    c_gnome_init
    ...

and we're done.

Portability
Linking libgnomeui probably makes my code a lot less portable. Hard-coding the include paths certainly does. Fortunately I don't have to care about other users just yet, and I'm unlikely ever to care about other platforms :)

Removing bytes from a file

posted by codders in c, code

Morning,

I copied and pasted a (Ruby) script from a PDF this morning, and on executing it I got a whole pile of:

webservice.rb:33: Invalid char `\240' in expression
webservice.rb:33: Invalid char `\302' in expression

which was annoying. For reasons best known to KPDF (or oowriter, or my window manager’s cut-and-paste buffer), the spaces in the script (” “) had been encoded as 0xc2 0xa0, which is sort of UTF16 if you look at it sideways, but essentially useless to me.

So how do you remove 200 instances of a 2-byte sequence from a file? I didn’t have a good way, but this bad way sufficed:

cat > rm.c << EOF
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(int arcg, char *argv[])
{
  unsigned char c;

  while (read(0, &c, 1)==1)
  {
    if (c != 0xc2 && c != 0xa0)
    {
      write(1, &c, 1);
    }
    else if (c == 0xc2)
    {
      write(1, " ", 1);
    }
  }
}
EOF
make rm
cat webservice.rb | ./rm > output.rb

So, my dearest lazyweb… better answers?

Recent Posts
Recent Comments
About Us
jp: works like a charm! thanks!...
Blake: Check this out: http://bugs.adobe.com/jira/browse/SDK-28016...
Boydell: Wow. That was it. You are the only one that had it figured out, and I looked at many...
mark van schaik: thanks! was using a beta SDK version for a production app, which stopped working over...
Sebastian: Steve, I find most asynchronous programming to be incredibly painful. Haskell's appro...

This is the personal blog of a professional software engineer. This site and the views expressed on it are in no way endorsed by the RIAA.