In this blog entry I will cover how I was able to cross compile GTK+ 3.0 applications for the Raspberry Pi. I will be assuming that you are running a Debian/Ubuntu flavored Linux distribution on your PC. I was successfully able to cross compile GTK+ 3.0 applications on my PC under both CrashBang Linux 11(Debian based) & Kubuntu 12.04 (Ubuntu based) . The sample code and makefile discussed in this blog entry are available here.

GTK3 application

Figure 1. Simple Cross compiled GTK3 application running on the RPi

I was not able to set up "pkg-config" properly for cross compilation. Instead I'm using a makefile that lists all of the necessary include and library files/directories as well as the library dependency directories. Some of the GTK+ related libraries also required that the libc.so.6 library (and a bunch of others) be placed in a specific static directory (/lib/arm-linux-gnueabihf) . I got around that by creating two soft links; one in the /lib & one in the /usr/lib directories. So this approach may be considered a bit of hack, but it works very well. I'm also using the latest Raspbian OS image on my RPi (2013-07-26 && 2013-05-25 work fine)  and suggest that you do the same.

  • Install the Raspbian OS on an SD Card
  • Plug the SD Card into the Raspberry Pi
  • Connect the Raspberry Pi to the Network and power it
  • SSH into the RPi.
  • Configure the RPi using the raspi-config utility
  • Update all packages on the RPi with the "sudo apt-get update && sudo apt-get upgrade" commands.

The first six steps listed above are covered in detail here.

  • Create a $HOME/rpi directory on your Linux PC. Download the cross-compiling toolchain for the RPi into that directory on your Linux PC and add it's bin directory  to the PATH variable as instructedhere .
  • Now log in remotely onto the RPi via an SSH connection and type: "sudo apt-get install libgtk-3-dev". This will install the gtk3 libraries and utilities necessary onto the RPi's SD card.
  • Turn OFF the RPi board, remove the SD card and plug it back into the Linux PC.
  • Copy the /usr and /lib directories from the SD card (root file system partition) into the $HOME/rpi/ on the Linux PC. You can use the graphical file manager or the cp command.
  • Plug the SD card back into the RPi, connect it to the network and turn it on.
  • On the Linux PC, create two soft links in the /lib && /usr/lib directories :
    • sudo ln -s  $HOME/rpi/usr/lib/arm-linux-gnueabihf/ /usr/lib/arm-linux-gnueabihf
    • sudo ln -s**$HOME/rpi/lib/arm-linux-gnueabihf/ /lib/arm-linux-gnueabihf**
  • Verify that the directory soft links indeed exist with the following two commands:
    • ls -ld /lib/arm-linux-gnueabihf
    • ls -ld /usr/lib/arm-linux-gnueabihf
  • Download and extract the sample code and makefile  available here.
  • The code and makefile are shown below for reference:

gtktest.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <gtk/gtk.h>

gint count = 0;
char buf[5];

void increase(GtkWidget *widget, gpointer label)
{
  count++;

  sprintf(buf, "%d", count);
  gtk_label_set_text(GTK_LABEL(label), buf);
}

void decrease(GtkWidget *widget, gpointer label)
{
  count--;

  sprintf(buf, "%d", count);
  gtk_label_set_text(GTK_LABEL(label), buf);
}

int main(int argc, char** argv) {

  GtkWidget *label;
  GtkWidget *window;
  GtkWidget *frame;
  GtkWidget *plus;
  GtkWidget *minus;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 180);
  gtk_window_set_title(GTK_WINDOW(window), "+-");

  frame = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), frame);

  plus = gtk_button_new_with_label("+");
  gtk_widget_set_size_request(plus, 80, 35);
  gtk_fixed_put(GTK_FIXED(frame), plus, 50, 20);

  minus = gtk_button_new_with_label("-");
  gtk_widget_set_size_request(minus, 80, 35);
  gtk_fixed_put(GTK_FIXED(frame), minus, 50, 80);

  label = gtk_label_new("0");
  gtk_fixed_put(GTK_FIXED(frame), label, 190, 58);

  gtk_widget_show_all(window);

  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);

  g_signal_connect(plus, "clicked",
      G_CALLBACK(increase), label);

  g_signal_connect(minus, "clicked",
      G_CALLBACK(decrease), label);

  gtk_main();

  return 0;
}

Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
ARM_PREFIX= arm-linux-gnueabihf-
CC   = $(ARM_PREFIX)gcc
SRC += gtktest.c
TARGET = gtktest

LIBRARY += gtk-3
LIBRARY += gdk-3
LIBRARY += atk-1.0
LIBRARY += gio-2.0
LIBRARY += pangocairo-1.0
LIBRARY += gdk_pixbuf-2.0
LIBRARY += cairo-gobject
LIBRARY += pango-1.0
LIBRARY += cairo
LIBRARY += gobject-2.0
LIBRARY += glib-2.0

LIBRARYDIR += $(HOME)/rpi/lib/arm-linux-gnueabihf
LIBRARYDIR += $(HOME)/rpi/usr/lib/arm-linux-gnueabihf
LIBRARYDIR += $(HOME)/rpi/lib
LIBRARYDIR += $(HOME)/rpi/usr/lib

XLINK_LIBDIR += $(HOME)/rpi/lib/arm-linux-gnueabihf
XLINK_LIBDIR += $(HOME)/rpi/usr/lib/arm-linux-gnueabihf

INCLUDEDIR += $(HOME)/rpi/usr/include/gtk-3.0
INCLUDEDIR += $(HOME)/rpi/usr/include/pango-1.0
INCLUDEDIR += $(HOME)/rpi/usr/include/gio-unix-2.0/
INCLUDEDIR += $(HOME)/rpi/usr/include/atk-1.0
INCLUDEDIR += $(HOME)/rpi/usr/include/cairo
INCLUDEDIR += $(HOME)/rpi/usr/include/gdk-pixbuf-2.0
INCLUDEDIR += $(HOME)/rpi/usr/include/freetype2
INCLUDEDIR += $(HOME)/rpi/usr/include/glib-2.0
INCLUDEDIR += $(HOME)/rpi/usr/lib/arm-linux-gnueabihf/glib-2.0/include
INCLUDEDIR += $(HOME)/rpi/usr/include/pixman-1
INCLUDEDIR += $(HOME)/rpi/usr/include/libpng12

OPT = -O0
DEBUG = -g
WARN= -Wall
PTHREAD= -pthread

INCDIR  = $(patsubst %,-I%,$(INCLUDEDIR))
LIBDIR  = $(patsubst %,-L%,$(LIBRARYDIR))
LIB    = $(patsubst %,-l%,$(LIBRARY))
XLINKDIR = $(patsubst %,-Xlinker -rpath-link=%,$(XLINK_LIBDIR))

all:
    $(CC) $(OPT) $(DEBUG) $(WARN) $(LIBDIR) $(PTHREAD) $(INCDIR) $(XLINKDIR) $(LIB) $(SRC) -o $(TARGET)

clean:
    rm -rf $(TARGET)
  • In a terminal window on your Linux PC navigate into the "gtktest" directory, "cd ~/gtktest" and run "make".
  • If everything went well you should end up with a binary compiled for arm called "gtktest". Type the following command in the terminal "file gtktest". The output should be: gtktest: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x72cb5773b65f1dce50358edfebd8be2d44aef967, not stripped."
  • Copy the arm binary from the Linux PC to the  "home/pi/" directory on the RPi using scp. i.e. Run the following command in a terminal window on your Linux PC "scp $HOME/gtktest/gtktest pi@10.42.0.73:/home/pi" (replace 10.42.0.73 with the IP address of your RPi). You will be prompted for the a password to connect to the RPi (user pi). It should be "raspberry" unless it was changed.
  • To test the binary on the RPi we will need to start a VNC server on the RPi and connect to it with a VNC client on your Linux PC. Detailed instructions are provided here.
  • Once the VNC session is started, and you can access the RPi's desktop, open an LXTerminal window and type: "chmod +x gtktest" to make the gtktest binary executable and run it with "./gtktest".
  • I got the example code from Zetcode's GTK+ tutorial. I was able to build a few other GTK+ 3.0 examples as well.
  • You can use Eclipse as an IDE if you really want to. Simply move the gtktest directory into your eclipse workspace directory, start eclipse and when creating a new project, choose the "Makefile project with Existing Code" option.
  • Instead of using a VNC server to execute the GUI application remotely on the RPi, you can try X11 forwarding in SSH. I haven't covered this topic yet but intend to do so in the near future.