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) nokia.com>
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
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] Name=Comment= Type=HildonHomeApplet X-home-applet=lib .so 
The following is an example metar-applet.desktop
[Desktop Entry] Name=metar-applet Comment=Aviation Weather Home Applet Type=HildonHomeApplet X-home-applet=libmetar_applet.so
API required to implement for a simple applet with no information to state save
A simple applet must implement the following functions:
hildon_home_applet_lib_initialize 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:
#include
hildon_home_applet_lib_initialize:
/** * @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:
      
void* 
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 */
GtkWidget*
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:   
/** * @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:
     
void 
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:
     
void
metar_ui_quit (void) {
    /* This is for freeing the stuff allocated earlier */
    if (app) {
        g_free (app);
        app = NULL;
    }
}
     hildon_home_applet_lib_background: 
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:
     
void
hildon_home_applet_lib_background (void *applet_data)
{
    return;
}
           hildon_home_applet_lib_foreground: 
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:
     
void 
hildon_home_applet_lib_foreground (void *applet_data)
{
    return;
}
      
     
hildon_home_applet_lib_save_state
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 
*/
int 
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).
     
int 
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.
     hildon_home_applet_lib_settings:
  
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).
     
GtkWidget*
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. autogen.sh, configure.ac and Makefile.am are created in addition to the actual applet source code.
The directory structure of the package is:
/metar_applet /metar_applet/debian /metar_applet/data /metar_applet/src
Firstly, the autogen.sh file (this file does not contain anything applet-specific.):
#!/bin/sh set -x libtoolize --automake aclocal-1.7 || aclocal autoconf autoheader automake-1.7 --add-missing --foreign || automake --add-missing --foreign
The configure.ac file is as follows:
AC_PREREQ(2.59)
AC_INIT(maemo-metar-applet, 0.1, karoliina.t.salminen@nokia.com)
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
dnl ##################
dnl This script generates host names
dnl ##################
AC_CANONICAL_HOST
dnl ##################
dnl Check for installed programs that is needed
dnl ##################
AC_PROG_CC
AM_PROG_CC_STDC
AC_PROG_INSTALL
AC_PROG_RANLIB
AC_PROG_INTLTOOL([0.21])
AC_PROG_LIBTOOL
AM_PATH_GLIB_2_0
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])
fi
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 ##################
AC_HEADER_STDC
GLIB_REQUIRED=2.6.0
GTK_REQUIRED=2.4.0
LIBOSSO_REQUIRED=0.8.3
HILDON_LGPL_REQUIRED=0.9.14
HILDON_LIBS_REQUIRED=0.9.15
HILDON_HOME=2.2
GMODULE=2.6
LIBXML=2.6
GNOMEVFS_REQUIRED=2.8.3
PKG_CHECK_MODULES(METAR, [
glib-2.0 >= $GLIB_REQUIRED,
gtk+-2.0 >= $GTK_REQUIRED,
libosso >= $LIBOSSO_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)
AC_SUBST(GNOME_VFS_CFLAGS)
AC_SUBST(GNOME_VFS_LIBS)
dnl ##################
dnl directories
dnl ##################
localedir=`pkg-config osso-af-settings --variable=localedir`
AC_SUBST(localedir)
metarpluginlibdir="/usr/lib/metar-applet-home"
AC_SUBST(metarpluginlibdir)
AC_DEFINE_UNQUOTED(METAR_PLUGIN_DIR, "${metarpluginlibdir}",[Dir where plugins are stored])
pluginlibdir="/usr/lib/hildon-home"
AC_SUBST(pluginlibdir)
AC_CONFIG_FILES([Makefile
src/Makefile
data/Makefile
])
AC_OUTPUT
  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 Makefile.am is:
SUBDIRS = src data EXTRA_DIST= \ autogen.sh \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ debian/rules \ debian/control \ debian/copyright \ debian/changelog \ debian/maemo-metar-applet.install deb_dir = $(top_builddir)/debian-build INCLUDES = $(DEPS_CFLAGS) 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 = libmetar_applet.la common_CFLAGS = \ $(METAR_CFLAGS) \ -DPREFIX=\"$(prefix)\" \ -DLOCALEDIR=\"$(localedir)\" common_LDADD = \ $(METAR_LIBS) 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 Makefile.am of data directory: homeapplet_desktopdir=/usr/share/applications/hildon-home homeapplet_desktop_DATA=metar-applet.desktop EXTRA_DIST=$(homeapplet_desktop_DATA)
The debian/control is as follows:
Source: maemo-metar-applet Section: misc Priority: optional Maintainer: Karoliina SalminenBuild-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 SalminenTue, 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)))
CFLAGS += -O0
else
CFLAGS += -O2
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
INSTALL_PROGRAM += -s
endif
config.status: 
dh_testdir
# 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
dh_testdir
# Add here commands to compile the package.
$(MAKE)
touch build-stamp
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
# Add here commands to clean up after the build process.
-$(MAKE) clean
dh_clean 
install: build
dh_testdir
dh_testroot
dh_clean -k 
dh_installdirs
# 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_testdir
dh_testroot
#   dh_installchangelogs 
dh_installdocs
#   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_link
dh_strip
dh_compress
dh_fixperms
#   dh_perl
#   dh_python
dh_makeshlibs
dh_installdeb
dh_shlibdeps -V
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure
  The debian/maemo-metar-applet.install is:
usr/lib/metar-applet-home/*.so usr/lib/hildon-home/*.so data/metar-applet.desktop [Desktop Entry] Name=metar-applet Comment=Aviation Weather Home Applet Type=HildonHomeApplet X-home-applet=libmetar_applet.so
Improve this page

