This uses some information from upstream. If you are feeling really brave do this first.

Objective

This shows how to port an application written in C (liferea) to Ubuntu Mobile. Liferea is an aggregator for online news feeds written in C utilizing GTK for its graphical interface.

Assumed Knowledge

You have set up the UME development environment as show here. Also that you can change the Flash UI as explained here. You should be familiar with or have the desire to learn C programming, including the 'auto' tools. A good tutorial on automake is here

Dependencies Needed

aptitude install libgtkhtml2-0 libgtkhtml2-dev libxml-perl libxslt1-dev libglade2-0 libglade2-dev libsqlite3-0 libsqlite3-dev libhildondesktop-dev xulrunner

Overview

Porting applications basically comes down to two things:

1. Use HildonProgram and HildonWindow classes instead of the GtkWindow class.

2. Use the HildonWindow menu bar instead of the GTKMenuBar

Get the source and try to compile

user@machine:/home/ian/Dev/Ume/ports/src# apt-get source liferea this downloads the source

liferea-1.4.2
liferea_1.4.2.orig.tar.gz
liferea_1.4.2-0ubuntu1.diff.gz
liferea_1.4.2-0ubuntu1.dsc

this gives some ./autoconf errors like:

No package 'libxslt' found
No package 'sqlite3' found
No package 'libglade-2.0' found

which are the applications dependencies, so install the packages needed if you have not already done so: aptitude install libgtkhtml2-0 libgtkhtml2-dev libxml-perl libxslt1-dev libglade2-0 libglade2-dev libsqlite3-0 libsqlite3-dev libhildondesktop-dev xulrunner

this passed autoconf but running make gave an error of:

gtkhtml2.c: In function 'gtkhtml2_launch_url':
gtkhtml2.c:458: error: too many arguments to function 'update_request_new'
gtkhtml2.c:458: warning: assignment from incompatible pointer type
gtkhtml2.c:459: error: dereferencing pointer to incomplete type
gtkhtml2.c:460: error: dereferencing pointer to incomplete type
gtkhtml2.c:461: error: dereferencing pointer to incomplete type
gtkhtml2.c:462: error: dereferencing pointer to incomplete type
gtkhtml2.c:463: error: dereferencing pointer to incomplete type
gtkhtml2.c:465: error: too few arguments to function 'update_execute_request'
make[4]: *** [liblihtmlg_la-gtkhtml2.lo] Error 1
make[4]: Leaving directory `/home/ian/Desktop/liferea-1.4.2/src/gtkhtml2'

It seems to be hitting by this bug.

So downloaded the latest bugfix stable release 1.4.2b and this compiled successfully and also ran make ok so now it is time to 'Hildonize' it.

HILDONIZING

It is useful to know that the default location for C header files is in /usr/include. Inside this directory we have another directory hildon-1 and inside this directory we have hildon, so the full path to the hildon header files is: /usr/include/hildon-1/hildon

First modify the file configure.ac Around line 42 add the line:

 AC_ARG_ENABLE(hildon,    AS_HELP_STRING([--enable-hildon],[compile for Hildon environment @<:@default=no@:>@]),,enable_hildon=no)

and then on lines 212 - 225 add:

dnl ********
dnl Hildon
dnl ********
 
if test "x$enable_hildon" = "xyes"; then
   dnl AC_MSG_CHECKING([for GtkHTML2 support])
   PKG_CHECK_MODULES([HILDON], hildon-1 >= 1.0.5,enable_hildon=yes,enable_hildon=no)
   AC_SUBST(HILDON_CFLAGS)
   AC_SUBST(HILDON_LIBS)
else
   enable_hildon=no
fi

AM_CONDITIONAL(WITH_HILDON, test "x$enable_hildon" = "xyes")

and then finally on line 548 echo the information back so that there is visual confirmation to the user that Hildon is enabled:

echo "Build Hildon support............ : $enable_hildon"

these changes enable the passing of the argument --enable-hildon on the command line like:

./configure --enable-hildon

After making these changes run autoreconf: user@machine:~/Dev/Ume/liferea-1.4.2b$ autoreconf

This step is necessary because autoreconf runs autoconf, autoheader, aclocal, automake, libtoolize, and autopoint (when appropriate) to update the Build System in the specified directories and their subdirectories. In other words it registers the changes made to configure.ac

Next change Makefile.am This is located in the liferea-1.4.2b/src directory. On line 67 add the Hildon libraries $(HILDON_LIBS) to liferea_bin_LDADD

liferea_bin_LDADD =     net/liblinet.a \
                        parsers/libliparsers.a \
                        fl_sources/libliflsources.a \
                        notification/liblinotification.a \
                        ui/libliui.a \
                        $(PACKAGE_LIBS) $(X_LIBS) $(X_PRE_LIBS) -lX11 $(X_EXTRA_LIBS) $(DBUS_LIBS) $(NM_LIBS) $(INTLLIBS) $(GNUTLS_LIBS) $(HILDON_LIBS)

this enables liferea to link against the Hildon libraries.

Next change the Makefile.am inside the liferea-1.4.2b/src/ui directory. On line 11 add: libliui_a_CFLAGS = $(PACKAGE_CFLAGS) $(DBUS_CFLAGS) $(HILDON_CFLAGS)

this passes the $(HILDON_CFLAGS) to the compiler

These are all the changes needed to be made to autotools. Next copy the liferea.glade file which is in the root liferea-1.4.2b directory and rename it to liferea_hildon.glade This means that if the --enable-hildon flag is passed at compile time the liferea_hildon.glade file is used and if not liferea.glade is used.

Here are the differences between the two files:

user@machine:/home/user/liferea-1.4.2b$ diff -Nu liferea.glade liferea_hildon.glade 
--- liferea.glade       2007-08-19 13:56:50.000000000 -0400
+++ liferea_hildon.glade        2007-09-25 14:17:38.000000000 -0400
@@ -2,11 +2,8 @@
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
 <!--*- mode: xml -*-->
 <glade-interface>
-  <widget class="GtkWindow" id="mainwindow">
-    <property name="title" translatable="yes">Liferea</property>
-    <property name="default_width">640</property>
-    <property name="default_height">480</property>
-    <property name="icon_name">liferea</property>
+  <widget class="GtkVBox" id="mainwindow">
+    <property name="visible">True</property>
     <child>
       <widget class="GtkVBox" id="vbox1">
         <property name="visible">True</property>

In short the mainwindow is a GtkVBox in the hildon version and a GtkWindow normally. This change is explained in this tutorial in the section 'Hildonizing Main View'

Now liferea needs to know to pull in the appropriate glade file depending on whether it is compiled for hildon or not.

In the file liferea-1.4.2b/src/ui/ui_shell.c inside the function static void liferea_shell_init (LifereaShell *ls) line 113 now looks like:

#ifdef MAEMO_CHANGES
        ls->priv->xml = glade_xml_new (PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "liferea_hildon.glade", "mainwindow", GETTEXT_PACKAGE);
#else
        ls->priv->xml = glade_xml_new (PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "liferea.glade", "mainwindow", GETTEXT_PACKAGE);
#endif

Also the file liferea-1.4.2b/src/ui/ui_dialog.c inside the function GtkWidget *liferea_dialog_new (const gchar *filename, const gchar *name) line 139 now looks like:

#ifdef MAEMO_CHANGES
                ld->priv->xml = glade_xml_new (PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "liferea_hildon.glade", name, GETTEXT_PACKAGE);
#else
                ld->priv->xml = glade_xml_new (PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "liferea.glade", name, GETTEXT_PACKAGE);
#endif

All the other changes to Liferea are in liferea-1.4.2b/src/ui/ui_mainwindow.c

On line 34 add the header file include:

#ifdef MAEMO_CHANGES
#  include <hildon/hildon-program.h>
#endif

On line 66 in the function static struct mainwindow create the HildonProgram structure like:

#ifdef MAEMO_CHANGES
        HildonProgram   *program;
        GtkWidget       *container;
        GtkWidget       *window;
#else
        GtkWindow       *window;
#endif

On line 536 in the function static struct mainwindow *ui_mainwindow_new(void) make an instance of a HildonProgram like:

#ifdef MAEMO_CHANGES
        mw->program = HILDON_PROGRAM(hildon_program_get_instance());
        mw->container = window;
        mw->window = hildon_window_new();
        gtk_container_add(GTK_CONTAINER(mw->window), GTK_WIDGET(mw->container));
        hildon_program_add_window(mw->program, HILDON_WINDOW(mw->window));
#else
        mw->window = GTK_WINDOW (window);
#endif

Also the maemo tutorial mentioned above notes 'Also remove functions accel_set_func and accel_edited_callback' so on line 1255 add:

#ifndef MAEMO_CHANGES   
        accel_group = gtk_ui_manager_get_accel_group (ui_manager);
        gtk_window_add_accel_group (mw->window, accel_group);
#endif
  • liferea.png

Now on to the menu functions.

This blog post provided the inspiration for this.

Trying to use the GtkUIManager caused a problem. The reason is that hildon_window_set_menu expects a GtkMenu as second argument, but the GtkUIManager gives a GtkMenuBar

Adding an utility function to the code that converts from a GtkMenuBar to a GtkMenu solves this. The very nice thing is that it still uses the definitions in the glade file. On line 1269 add:

#ifdef MAEMO_CHANGES
        GtkWidget *main_menu;
        GList *iter;

        /* Create new main menu */
        main_menu = gtk_menu_new();

        iter = gtk_container_get_children (GTK_CONTAINER (mw->menubar));
        while (iter) {
                GtkWidget *menu;

                menu = GTK_WIDGET (iter->data);
                gtk_widget_reparent(menu, main_menu);

                iter = g_list_next (iter);
        }

        hildon_window_set_menu(HILDON_WINDOW(mw->window), GTK_MENU(main_menu));
#endif

That is all the changes made so now move the project to the target filesystem and run:

./configure --enable-hildon --prefix=/usr/ --libdir=/usr/lib
make
make install

and then start the UI for the target filesystem. Click on the shell image and run

export DISPLAY=:2
 liferea
  • hildonized.png

Here it is running in the maemo environment too

UMEGuide/ApplicationDevelopment/PortingCAppsToUME (last edited 2008-07-24 17:11:12 by localhost)