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