Maemo desktop plugin tutorial: task navigator and Home plugins

This document is released under the GPL license.

Goal: To show how to create plugins for maemo-af-desktop's task navigator and Home

Target audience: Maemo application/applet developers

Author: Karoliina Salminen karoliina.t.salminen (at)>

Introduction to maemo-af-desktop architecture

maemo-af-desktop consists of three subcomponents: task navigator, Home and status bar. They support additional plugins that are displayed on each these components and which extend the functionality of the host application. The plugins are opened by the host application with dlopen and the plugin has to implement an API, which the host application (TN, Home or SB) then calls. This document is a tutorial, it explains how to use the plugin API and how to write plugins that work well with the maemo-af-desktop.

Figure 1. maemo-af-desktop architecture

Task navigator plugins

The task navigator implements a modular plugin architecture the allows binary plugins to be loaded into the application. This extends the functionality of the task navigator and is achieved with task buttons. The license for the task navigator plugin can be an open source or closed source; in this way task navigator is versatile for ideal for use in many kinds of devices and applications.

Task navigator plugin API:

Task navigator plugins can have any widget as their representation on the task navigator plugin area. The plugin representation, which is statically visible in the task navigator, can be, for example, GtkButton. Task navigator loads the library and displays the main widget of the plugin in the plugin area of TN.

The plugins are responsible for their own signals, for example, clicked/toggle and related and event and so on, and implementing the callback functions for them. Task navigator only provides a container for the plugins, the plugins are responsible for implementing their own UI.

Desktop file location:

Task navigator plugins must provide a desktop file. It is placed in the following directory: usr/share/applications/hildon-navigator/

Desktop file contents:

A description of the .desktop file's required contents follows:

file name: the name and full file path of the plugin library file.
plugin logical name: the_plugin_logical_name
localized plugin names: The plugin localized names for the configurator applet.

The Task Navigator Configuration Control Panel applet is the only one which is involved with the contents of these .desktop files, so task navigator does not read them and the absence of the file does not prevent the plugin form working if the plugin configuration file (described later) is in correct condition.

The following is an example .desktop file:

Name: hildon-task-navigator-gps.desktop

The following is an Example .desktop file for the task navigator applet:

[Desktop Entry]

The Encoding, Version and Comment fields are optional fields, the others mandatory.


You must create/init applet's data in the hildon_navigator_lib_create function. Returning it as a pointer to the task navigator, which then passes it back to you, means you avoid using global variables if you so wish.

The following is an example implementation:


/* Functions called by Task Navigator */

/* This is called for TN to create the plugin. Use this place to allocate your data */
void* hildon_navigator_lib_create ()
  plugin_private_data_t *privdata;
  privdata = g_new0(plugin_private_data_t, 1);
  return privdata;


The function creates the widget,which is displayed on the task navigator. As mentioned earlier, this is typically a GtkButton. The task navigator automatically sets the button to the correct size.

The con file is located at: /usr/share/icons/hicolor

The icon size is: 64x64

The icon name is: the icon filename without the file extension

If you want to have the button skinned properly, set the name for the button as "hildon-navigator-button-one". (GtkWidgetSetName-function).

The following is an example implementation:

GtkWidget *
hildon_navigator_lib_get_button_widget ( void *data )
    GtkWidget *img;
    GtkWidget *button;
    img = gtk_image_new_from_icon_name ("my_own_icon_name_without_extension", GTK_ICON_SIZE_DIALOG); /* GTK_ICON_SIZE_DIALOG happens to be proper size, it is used for that reason here  */
    button = gtk_button_new_with_label("");
    gtk_button_set_image(GTK_BUTTON(button), img);
    return button; /* your widget is returned to Task Navigator */

The hildon_navigator_lib_initialize_menu The task navigator calls this to initialise the menu. This is called when all plugins are loaded to the task navigator. This is the place to initialise your plugin's UI, which is not visible when your button is not pressed. The reason for this being a separate function from other initilisation functions is that the TN UI is shown as ready before all UI's are initialised and therefore speeds the startup time (because it already takes some time before the end user taps the first time some of the task buttons).

The following is an example implementation:


/* This is called by Task Navigator to initialize the menu */
void hildon_navigator_lib_initialize_menu (void *data)
    GtkWidget *menuitem0 = NULL;
    plugin_private_data_t *privptr;

    privptr = (plugin_private_data_t *) data;
    privptr->menu0 = gtk_menu_new();
    menuitem0 = gtk_image_menu_item_new_with_label("Hello world");
    gtk_menu_shell_append(GTK_MENU_SHELL(privptr->menu0), menuitem0);

    menuitem0 = gtk_image_menu_item_new_with_label("Perform some magic");
    gtk_menu_shell_append(GTK_MENU_SHELL(privptr->menu0), menuitem0);

    /* You can use either button-press-event or toggled or clicked depending 
        on which suits your use the best, the framework doesn't limit your choices */
    g_signal_connect (G_OBJECT(privptr->button0), "toggled",
                    G_CALLBACK(my_task_button_toggled), data);
    /* Listen to the menu going away */
    g_signal_connect (G_OBJECT(privptr->menu0), "hide",
                    G_CALLBACK(my_task_menu_hidden), data);  

/* Signal listener callback functions, see hildon_navigator_lib_initialize_menu implementation */
gboolean my_task_button_toggled(GtkToggleButton *widget,
                                   gpointer data)
   plugin_private_data_t *privptr = (plugin_private_data_t *) data;

   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(privptr->button0)))
   } else {
        gtk_menu_shell_select_first(GTK_MENU_SHELL(privptr->menu0), TRUE);

   return TRUE;

/* Since the button doesn't get an release event, we'll tell it to
* unactivate itself explicitly when the menu is hidden
void my_task_menu_hidden (GtkWidget *widget, gpointer data)
    plugin_private_data_t *privptr;   
    privptr = (plugin_private_data_t *) data;
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(privptr->button0), FALSE);



You must free your data on the hildon_navigator_lib_destroy -function. If you have chosen to not to implement this as global variable, the task navigator passes the pointer back at you.

The following is an example implementation:

/* This is called for TN to remove the plugin. Use this place to free your data. */
void hildon_navigator_lib_destroy (void *data)
    plugin_private_data_t *privptr;
    privptr = (plugin_private_data_t *)data;

Task Navigator Configuration Control Panel Applet Configuration:

The system configuration file is: /etc/hildon-navigator/plugins.conf

This file has factory plugin configuration. It can contain mandatory flags, which prevent the removal of the plugin by Task Navigator Configuration Control Panel Applet.

The user configuration file: /home/user/.osso/hildon-navigator/plugins.conf

The file format specification is as follows:

Library = 
Position = 0
Mandatory = false

Library = 
Position = 1
Mandatory = false


Library = 
Position = 1
Mandatory = false

The amount of plugins is not limited in the configuration file. However, in the form factor of Nokia 770 there are places for only two plugins to be displayed at a time. If the screen in the maemo-device was longer, then more places could be used at the same time, therefore justifying the possibility of having more than two plugins configured in the configuration file.

Task Navigator Configuration Control Panel applet reads the .desktop files of the plugins. Task Navigator Control Panel configuration applet reads the applet information from the .desktop files. The task navigator does not use these files, so they are made solely for the purpose of the Task Navigator Configuration Applet. If you want to make your plugin interact correctly with the Task Navigator Configuration Applet, the desktop file needs to be in place. It can be implemented in that way, so that your postinstall script etc. could change the contents of the plugin configuration file automatically so then the applet usage would not be required to make your plugin visible. If however, you are doing applet for a maemo-system that is not a built on top of a Nokia commercial rootstrap but rather on a development rootstrap, there is no concern over the Task Navigator Configuration Control Panel applet compatibility, you need only edit the config files mentioned directly.

Home plugins

Each home applet needs to provide a .desktop file. The .desktop files must be placed in the /usr/share/applications/hildon-home directory. This is new to the IT2006 software version. To convert your applets for work that you did for IT2005, you need to add the .desktop file. Another change to the IT2006 API is that the applets must not implement hildon_home_applet_lib_properties, but implement instead hildon_home_applet_lib_settings (described later in this tutorial). Because now the applet size can be defined freely, and because Home applets are no longer "hardcoded" to Home - as they were in IT2005 version where their size was not definable - applets must no longer implement the hildon_home_applet_lib_get_requested_width function.

The Home applet .desktop file contents are as follows:

File name: home.desktop
Location: /usr/share/applications/hildon-home

The contents of the .desktop file are:

[Desktop Entry]

The following is an example metar-applet.desktop

[Desktop Entry]
Comment=Aviation Weather Home Applet

API required to implement for a simple applet with no information to state save

A simple applet must implement the following functions:

void hildon_home_applet_lib_deinitialize

Other API functions can remain empty and return NULL when a pointer return value is required.

The required #include statement is:



* @hildon_home_applet_lib_initialize
* @param state_data Statesaved data as returned by applet_save_state.
*                   NULL if applet is to be loaded in initial state.
* @param state_size Size of the state data.
* @param widget Return parameter the Applet should fill in with
*                   it's main widget.
* @returns A pointer to applet specific data
* Applet initialization. 
* This is called when Home loads the applet. It may load it self
* in initial state or in state given. It creates a GtkWidget that
* Home will use to display the applet. 
void *hildon_home_applet_lib_initialize(void *state_data, 
int *state_size, 
GtkWidget **widget);

The following is an example implementation:

hildon_home_applet_lib_initialize (void *state_data,
int *state_size, 
GtkWidget **widget)
    osso_context_t *osso;
    /* Initialize libosso */
    osso = osso_initialize ("METAR_APP", "0.1", FALSE, NULL);
    if (!osso) {
        g_debug ("Error initializing the osso" 
        "maemo metar applet");
        return NULL;
    *  Call private function metar_ui_init which does the initialization
    *  for this applet. It returns the GtkWidget which is then returned
    *  to Home. The function content could be implemented here too, but for
    *  maintainability reasons it has been moved to different function in different
    *  source file.
    (*widget) = metar_ui_init (osso);
    g_debug ("Initialized maemo metar applet");
    /* Libosso context is returned to Home */
    return (void*)osso;

Related functions in the accompanying metar-ui.c in this example applet are as follows:

/* Test button */
static void test_button_clicked (GtkButton* button, gpointer data)
    printf("Hello world. You clicked the test button!\n");

/* Create the widget for the applet that is returned to the caller (maemo-af-desktop) */
static void 
metar_ui_create_home_applet (void)

    /* Create the main widget of the applet */
    app->frame1 = gtk_frame_new(NULL);

    gtk_widget_set_size_request ( GTK_WIDGET (app->frame1), APPLET_X_SIZE, APPLET_Y_SIZE );

    /* Create one vbox */
    app->vbox0  = gtk_vbox_new (TRUE, 2);

    /* Hello world label */
    app->label0 =  gtk_label_new ("Hello world");

    /* Test button */
    app->button0 = gtk_button_new_with_label (("Test button"));
      gtk_signal_connect (GTK_OBJECT (app->button0), "clicked",
              GTK_SIGNAL_FUNC (test_button_clicked), (gpointer) 1);

    /* Set the main widget frame1 to contain vbox0 */
    gtk_container_add (GTK_CONTAINER (app->frame1), app->vbox0);
    /* Add first the label */
    gtk_box_pack_start(GTK_BOX(app->vbox0), app->label0, FALSE, FALSE, 0);
    /* The button comes below the label */
    gtk_box_pack_start(GTK_BOX(app->vbox0), app->button0, FALSE, FALSE, 0);
    /* Hack needed to get the applet themed correctly. This
    string can be found from gtkrc.maemo-af-desktop -file. Please look
    at it for more information. With string osso-rss-feed-reader it is possible
    to achieve propably best results for a simple applet */

    /* NOTE: FIXME: The skin is drawn inside the applet area so you can't use 
    the border area to draw widgets on it, otherwise your widgets will appear on
    top of the skin graphics like in this quick'n'dirty example */
    gtk_widget_set_name (GTK_WIDGET (app->frame1), "osso-rss-feed-reader");
    /* Finally show the thing */
    gtk_widget_show_all (GTK_WIDGET (app->frame1));

/* Init UI */

metar_ui_init (osso_context_t* osso) 
    if (!app) {
        /* Create the struct for storing the globals nicely inside a namespace */
        app = g_new0 (MetarApp, 1);
        app->osso = osso;

        /* The UI initialization could be inside this function, but I
           did choose to put it to another function */
        metar_ui_create_home_applet ();

    /* IMPORTANT! This returns the main widget of the applet (frame1) to maemo-af-desktop */
    return app->frame1;


* @hildon_home_applet_lib_deinitialize
* @param applet_data Applet data as returned by applet_initialize.
* Called when Home unloads the applet from memory.
* Applet should deallocate all the resources needed.
void hildon_home_applet_lib_deinitialize(void *applet_data);

The following is an example implementation:


hildon_home_applet_lib_deinitialize (void *applet_data)
    osso_context_t *osso = (osso_context_t*)applet_data;

    /* Call private function which deinitializes your UI. Not necessary required to be
       separate, but for maintainability reasons it may be cleaner to place it to different file
       than this API implementation */
    metar_ui_quit ();
    /* Deinitialize libosso */
    osso_deinitialize (osso);

Accompanying functions in this example in metar-ui.c are:


metar_ui_quit (void) {
    /* This is for freeing the stuff allocated earlier */
    if (app) {
        g_free (app);
        app = NULL;


This function is called when your applet is backgrounded. In other words, the clock applet, for example, does not need to update its display when it is not shown. Home calls hildon_home_applet_lib_foreground again when it is necessary to continue updating the display of the applet again periodically.

* @hildon_home_applet_lib_background
* @param applet_data Applet data as returned by applet_initialize.
* Called when Home goes to background. 
* Applet should stop all timers when this is called.
void hildon_home_applet_lib_background(void *applet_data);

You do not need to implement this for a simple applet, so it can be left empty as follows:

hildon_home_applet_lib_background (void *applet_data)


This function can be used to start, for example, timers in the applet if it has been backgrounded and is then brought up. For example, a clock applet that updates itself periodically does not need updates when it is on background - to do so in an embedded system wastes resources and the battery. Restart your periodic updates when Home calls this function.

The prototype of hildon_home_applet_lib_foreground is:

* @hildon_home_applet_lib_foreground
* @param applet_data Applet data as returned by applet_initialize.
* Called when Home goes to foreground. 
* Applet should start periodic UI updates again if needed.
void hildon_home_applet_lib_foreground(void *applet_data);

For a simple applet this can be also left empty:

hildon_home_applet_lib_foreground (void *applet_data)

Prototype of hildon_home_applet_lib_save_state
* @hildon_home_applet_lib_save_state
* @param applet_data Applet data as returned by applet_initialize.
* @param state_data Applet allocates memory for state data and
*                   stores pointer here. 
*                   Must be freed by the calling application
* @param state_size Applet stores the size of the state data
*                   allocated here.  The size is measured in
*                   sizeof(void) units.
* @returns 0 if successful.
* Method called to save the UI state of the applet 

hildon_home_applet_lib_save_state (void *applet_data,
void **state_data, 
int *state_size);

If you do not need the saving state, leave it empty with the following implementation (just set the state data to NULL and state size 0).

hildon_home_applet_lib_save_state (void *applet_data,
void **state_data, 
int *state_size)
    /* we don't want to save state in a simple weather applet */
    (*state_data) = NULL;
    if (state_size) {
        (*state_size) = 0; 
    } else {
        g_debug ("State_size pointer was not pointing to right place");
    return 1;

If your applet needs the saving state, just fill up the **state_data with your data. Home stores the data and initialises the applet next time with the saved data in hildon_home_applet_lib_initialize. Assign state data size in *state_size return value. The return value (int) is an error code. Return 0 if successful.


The following is the prototype of hildon_home_applet_lib_settings

* @hildon_home_applet_lib_settings
* @param applet_data Applet data as returned by applet_initialize.
* @param parent The toplevel applet GTK window
* Called when the applet needs to open a settings dialog
* This allows applet to provide Home e.g. a menu item widget
* that is connected to an action specified by the applet
GtkWidget *hildon_home_applet_lib_settings(void *applet_data,
GtkWindow *parent);

An example implementation for a simple applet (can be left empty).

hildon_home_applet_lib_settings (void *applet_data,
GtkWindow *parent)
    return NULL;

How to create a makefiles and package for your applet

You now have enough information to create a simple Home applet. The applet basically just contains an eventbox whose content can be state saved. The building of applets makes use of autotools., and are created in addition to the actual applet source code.

The directory structure of the package is:


Firstly, the file (this file does not contain anything applet-specific.):

set -x
libtoolize --automake
aclocal-1.7 || aclocal
automake-1.7 --add-missing --foreign || automake --add-missing --foreign

The file is as follows:

AC_INIT(maemo-metar-applet, 0.1,


dnl ##################
dnl This script generates host names
dnl ##################

dnl ##################
dnl Check for installed programs that is needed
dnl ##################


AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes, no)
if test "x$HAVE_PKG_CONFIG" = "xno"; then
AC_MSG_ERROR([You need to install pkg-config tool])

dnl ##################
dnl Compiler flags, note that the -ansi flag is not used because
dnl in that case the rint fuction is not awailable in math.h
dnl ##################

CFLAGS="$CFLAGS -g -Wall -Werror -ansi -Wmissing-prototypes -Wmissing-declarations"

dnl ##################
dnl Check needed headers, like C standard headers, GLib, GStreamer etc.
dnl ##################


glib-2.0 >= $GLIB_REQUIRED,
gtk+-2.0 >= $GTK_REQUIRED,
hildon-lgpl >= $HILDON_LGPL_REQUIRED,
hildon-libs >= $HILDON_LIBS_REQUIRED,
hildon-home >= $HILDON_HOME,
gmodule-2.0 >= $GMODULE,
libxml-2.0 >= $LIBXML,
gnome-vfs-2.0 >= $GNOMEVFS_REQUIRED

PKG_CHECK_MODULES(GNOME_VFS, gnome-vfs-2.0 >= 2.8.3)

dnl ##################
dnl directories
dnl ##################

localedir=`pkg-config osso-af-settings --variable=localedir`


AC_DEFINE_UNQUOTED(METAR_PLUGIN_DIR, "${metarpluginlibdir}",[Dir where plugins are stored])




The package config file of Home is used to provide information about the installation directory of the compiled plugin by querying the value of pluginlibdir variable. All version numbers for various components above should be interpreted as illustrative only.

The master is:

SUBDIRS = src data

EXTRA_DIST= \ \ \ \ \
debian/rules \
debian/control \
debian/copyright \
debian/changelog \

deb_dir = $(top_builddir)/debian-build


deb:    dist
-mkdir $(deb_dir)
cd $(deb_dir) && tar xzf ../$(top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
cd $(deb_dir)/$(PACKAGE)-$(VERSION) && dpkg-buildpackage -rfakeroot
-rm -rf $(deb_dir)/$(PACKAGE)-$(VERSION)

Makefile of the src subdirectory directory:

pluginlib_LTLIBRARIES =

common_CFLAGS = \
-DPREFIX=\"$(prefix)\" \

common_LDADD = \

libmetar_applet_la_LDFLAGS= -module -avoid-version

libmetar_applet_la_CFLAGS = $(common_CFLAGS)

libmetar_applet_la_LIBADD = $(common_LDADD)

libmetar_applet_la_SOURCES = \
metar-ui.h \
metar-ui.c \
metar-loader.h \
metar-loader.c \
metar-applet.c of data directory:



The debian/control is as follows:

Source: maemo-metar-applet
Section: misc
Priority: optional
Maintainer: Karoliina Salminen 
Build-Depends: libgtk2.0-dev (>=2.4.0-1), pkg-config, libosso-dev, hildon-lgpl-dev, hildon-libs-dev, intltool (>= 0.21), hildon-home-dev (>=2.2.1), libxml2-dev (>=2.6), libosso-gnomevfs2-dev
Standards-Version: 3.6.1

Package: maemo-metar-applet
Section: user/internet
Architecture: any
Depends: ${shlibs:Depends},${launcher:Depends}
Description: Maemo Metar home panel applet 
Home panel applet for showing aviation weather.    

The debian/changelog is:

maemo-metar-applet (0.1) experimental; urgency=low

* Created package

-- Karoliina Salminen   Tue, 30 May 2006 10:04:45 +0200

The debian/rules are:

#!/usr/bin/make -f

# export DH_VERBOSE=1

# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)

CFLAGS = -Wall -g
PACKAGENAME = maemo-isearch-applet

ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))

# Add here commands to configure the package.
CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info

build: build-stamp

build-stamp: config.status

# Add here commands to compile the package.

touch build-stamp

rm -f build-stamp configure-stamp

# Add here commands to clean up after the build process.
-$(MAKE) clean


install: build
dh_clean -k 

# Add here commands to install the package
$(MAKE) install DESTDIR=$(CURDIR)/debian/tmp

# Build architecture-independent files here.
binary-indep: build install
# We have nothing to do by default.

# Build architecture-dependent files here.
binary-arch: build install
#   dh_installchangelogs 
#   dh_installexamples
dh_install -v --sourcedir=debian/build
#   dh_installmenu
#   dh_installdebconf   
#   dh_installlogrotate
#   dh_installemacsen
#   dh_installpam
#   dh_installmime
#   dh_installinit
#   dh_installcron
#   dh_installinfo
#   dh_installman
#   dh_perl
#   dh_python
dh_shlibdeps -V

binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure

The debian/maemo-metar-applet.install is:


[Desktop Entry]
Comment=Aviation Weather Home Applet

Improve this page