Development Guides: Simple Applications
Developing a Basic C/C++ Hildon Application
Sample Code

Introduction
Developing applications that run on Moblin is easy if you are familiar with Linux, GTK+ development, and you have already created a target device's build-environment with Moblin Image Creator.

Once you have created a target, make sure you have the right libraries and tools installed so that you can easily develop applications. First, add the developers-tools functional set (fsest) to your target with Moblin Image Creator. You should also have the libosso-dev package installed. For more information about necessary libraries, and adding fsets, click here. Now you can begin developing C/C++ applications that use the Hildon Application Framework.

There are several things to do to create an application for Moblin:
  1. Use HildonProgram and HildonWindow in your main function.
  2. Use Hildon functions to set a menu and add a toolbar.
  3. Add the application icon to the home screen.
  4. Register the application to the D-Bus.

This guide helps you create a simple application that displays "Hello World" and a timestamp of when the application was started. We will then add a File menu containing the Quit option and a toolbar with a Quit button. Next, we will show you how to integrate this application with the Hildon desktop environment. We will show you how to add it to the Home Screen menu and use the libraries that allow only one instance of the program to be running. After that we will modify the menubar and the toolbar for a better fit with the Hildon desktop environment.


Our "Hello World!" Samples
You can download all of our hello-world samples, compile them, and see how they work. To learn how to build them step-by-step, you can skip to the next section.


Chroot into the target environment, and make sure you have installed the LibOSSO library, by typing the following command:
# apt-get install libosso-dev

You can download all of our completed hello-world samples from here. To compile and install the files type:
# make

Once this finishes, you should start the Xephyr window by typing:
# ume-xephyr-start

Here is a screenshot of the Moblin Home Screen running in the Xephyr window:

Figure 2-1: Moblin Home Screen

You should see "Hello World! v 1.0", "Hello World! v 2.0", and "Hello World! v 3.0" entries on the Home Screen in categories Mobile and All. Launch the applications from the Home Screen and see what functionality and differences they have.

Close the Xephyr window, uninstall, and clean by typing:

# make clean
# make uninstall

Now, let's take the step-by-step approach.


Compiling a Basic C GTK+ Application

Start the terminal session from Moblin Image Creator for your target by selecting it in the Targets section and clicking the Terminal button. You can create a directory for your source wherever you would like. For this example, we are using the directory /usr/src/hello.
# cd /usr/src
# mkdir hello
# cd hello

Create a file called hello-world-1.c and add the following code:
/* File: hello-world-1.c */
  #include <gtk/gtk.h>

static gboolean quit1(GtkWidget *widget, GdkEvent *event)
{
  g_print("quit1\n");
  gtk_main_quit();
}

static gboolean quit2(GtkWidget *widget, GdkEvent *event)
{
  g_print("quit2\n");
  gtk_main_quit();
}

static void destroy(GtkWidget *widget, gpointer data)
{
  g_print("destroy\n");
  gtk_main_quit();
}

static void add_menu(GtkWidget *window, GtkWidget *box)
{
  GtkWidget *menubar = gtk_menu_bar_new();
  gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0);

  GtkAccelGroup *accel = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel);

  GtkWidget *fileitem = gtk_menu_item_new_with_mnemonic("_File");
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileitem);

  GtkWidget *filemenu = gtk_menu_new();
  gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileitem), filemenu);

  GtkWidget *q = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), q);
  g_signal_connect_swapped(G_OBJECT(q), "activate", G_CALLBACK(quit1), NULL);
}

static void add_toolbar(GtkWidget *window, GtkWidget *box)
{
  GtkWidget *toolbar = gtk_toolbar_new();
  gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, FALSE, 0);
  GtkToolItem *q = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  g_signal_connect_swapped(G_OBJECT(q), "clicked", G_CALLBACK(quit2), NULL);

  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), q, -1);
}

int main(int argc, char *argv[])
{
  static char hello[] = "Hello World! (v 1.0)";

  gtk_init (&argc, &argv);
  GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_size_request(window, 250, -1);

  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  gtk_window_set_title(GTK_WINDOW(window), hello);
  g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);

  add_menu(window, vbox);
  add_toolbar(window, vbox);

  GtkWidget *label1 = gtk_label_new(hello);
  gtk_box_pack_start_defaults(GTK_BOX(vbox), label1);

  time_t current_time;
  struct tm *local_time;
  time(&current_time);
  local_time = localtime(&current_time);
  GtkWidget *label2 = gtk_label_new(asctime(local_time));
  gtk_box_pack_start_defaults(GTK_BOX(vbox), label2);

  gtk_widget_show_all(window);
  gtk_main();
  return 0;
}

So far, we haven't introduced anything new besides GTK+. It is a generic Hello World application with a simple GTK+ menubar and toolbar. You can compile it and copy its binary to /usr/bin with the following commands:
# gcc -o hello-world-1 hello-world-1.c `pkg-config --cflags --libs gtk+-2.0` # cp hello-world-1 /usr/bin/hello-world-1

Start the Xephyr window:
# ume-xephyr-start

Launch the terminal from the Xephyr Window and run our Hello World by typing:
# /usr/src/hello/hello-world-1

Your first Hello World application should look like this:

Figure 3-1: The ideal Hello World application made with this tutorial.

Adding the Application Icon to the Home Screen
To add the Hello World icon to the Moblin home screen, you must create a desktop entry file for your application. The file, which we will call hello-world-1.desktop, has all the information necessary to display the application's icon as a point of entry on the home screen. The name of the file must end with the extension ".desktop", and the file must be placed in the /usr/share/mobile-basic-flash/applications/ directory on the target file-system.


Create a file called hello-world-1.desktop with the following contents, and save it to /usr/share/mobile-basic-flash/applications/ directory.
# File: hello-world-1.desktop
[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Hello World! v 1.0
Exec=hello-world-1
Icon=
OnlyShowIn=GNOME; Mobile

For more information about the desktop entry file, refer to the Desktop Entry Specification at http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html.

Start the Xephyr window:
# ume-xephyr-start

You should now see "Hello World! v 1.0" entry on the home screen in categories Mobile and All. The only problem is that every time you click the icon, a new instance of hello-world launches! You can verify this by comparing the timestamps in applications. For mobile devices, it is optimal for applications to have only one instance running. Let's fix this in the next section.


Registering An Application to the D-Bus

Create the hello-world-2.c File
The D-Bus is the message system that helps applications communicate with each other and the system by registering services offered to others. The Hildon framework includes LibOSSO, a wrapper layer that assists D-Bus for easier usage. By registering an application to the D-Bus service, the Hildon framework only allows a single instance of the application. For mobile devices, it is optimal for applications to have only one instance running.

Make sure the development library is installed on your target:
# apt-get install libosso-dev

For more information, refer to D-Bus and LibOSSO documents.

Now we are going to add LibOSSO functionality to our Hello World application. You can copy file hello-world-1.c to hello-world-2.c and modify it, or use hello-world-2.c from the sample code.


First, let's add some LibOSSO code to the top of hello-world-2.c. The changes we made from hello-world-1.c are highlighted in blue:
/* File: hello-world-2.c */
#include <gtk/gtk.h>

#ifdef USE_LIBOSSO
#include <libosso.h>

struct _instance
{
  osso_context_t *osso;
  GtkWidget *window;
};

typedef struct _instance instance;

static gint handler(const gchar *interface, const gchar *method,
GArray *arguments, gpointer data, osso_rpc_t *retval)
{
  retval->type = DBUS_TYPE_INT32;
  retval->value.i = OSSO_ERROR;

  if (method != NULL && strcmp(method, "top_application") == 0)
  {
    instance *inst = (instance *) data;
    gtk_window_present(GTK_WINDOW(inst->window));
    retval->value.i = OSSO_OK;
  }
  return retval->value.i;
}

static instance g_inst;

static osso_init(GtkWidget *window)
{
  g_inst.osso = osso_initialize("org.moblin.helloworld2", "", TRUE, NULL);
  g_inst.window = window;
  if (g_inst.osso == NULL)
  g_print("osso_initialize error\n");
else
{
  osso_return_t r;
  r = osso_rpc_set_default_cb_f(g_inst.osso, (osso_rpc_cb_f *) handler,
                                                  &g_inst);
  if (r != OSSO_OK)
    g_print("osso_rpc_set_default_cb_f error\n");
  }
}

static osso_exit(void)
{
  if (g_inst.osso)
  {
    osso_deinitialize(g_inst.osso);
  }
}
#else
#define osso_init(a)
#define osso_exit()
#endif

static gboolean quit1(GtkWidget *widget, GdkEvent *event)
{
...

We wrote the handler function that will process the message "top_application" and make the application visible. To connect to D-Bus and receive the system messages, we must call the LibOSSO API functions osso_initialize(...) and osso_rpc_set_default_cb_f(...). See the libosso.h file or http://maemo.org for more information on all osso_ functions.

We will also add one line to our main(...) function:
int main(int argc, char *argv[])
{
...

  GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_size_request(window, 250, -1);
  osso_init(window);

  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

...
}

Now the application connects to the D-Bus session bus and will bring itself to the front when necessary by calling gtk_window_present(...).


Create D-Bus service file org.moblin.helloworld2.service
To launch the Hildon application and connect it to D-Bus services, you need to create the D-Bus service file. This file should be placed in /usr/share/dbus-1/services/ directory in the target file-system and end with the extension ".service" For more information, you can search http://maemo.org for the X-Osso-Service.

Create a file called org.moblin.helloworld2.service with the following content, and save it to the /usr/share/dbus-1/services/ directory:
# File: org.moblin.helloworld2.service
[D-Bus Service]
Name=org.moblin.helloworld2
Exec=/usr/bin/hello-world-2

Name The application's D-Bus service name. This needs to be a unique value for each application that matches the value used in the application initialization code in the call to the osso_initialize(...) function. Usually the name starts with the application provider URL following the application name to minimize the possibility of conflicts with other service files. No dashes - or underscores _ are allowed in this name. That is why we changed this name to helloworld2 from the application name hello-world-2. The name of the D-Bus service file should match this name with the extension ".service", which is why it became org.moblin.helloworld2[1]
Exec The full path of application binary to launch[1].

Update Desktop File
So that our D-Bus registered application behaves correctly, our desktop entry file needs to have one more name/value tag added.

The syntax of this tag is as follows:
X-Osso-Service= <my application's D-Bus service name>

We need to create a new desktop entry file for hello-world-2. You can copy the file that we used for hello-world-1, and add the following line to the bottom of our desktop entry file:
# File: hello-world-2.desktop
[Desktop Entry]
...
X-Osso-Service=org.moblin.helloworld2

Compile hello-world-2 and copy its binary to /usr/bin with the following commands:
# gcc -o hello-world-2 hello-world-2.c `pkg-config --cflags --libs gtk+-2.0 libosso` -DUSE_LIBOSSO
# cp hello-world-2 /usr/bin/hello-world-2

Start the Xephyr window:
# ume-xephyr-start

You should now see the "Hello World! v 2.0" icon on the home screen. Launch the application and take note of the timestamp.

Here's the cool part: Switch back to the home screen without closing the application, and click on "Hello World! v 2.0" icon again. The application launches with the same timestamp! We can now verify that adding LibOSSO causes the system to bring forward our already running instance of hello-world-2.


Figure 4-1.

See! Same timestamp!

Hildon Program, Window, Menu and Toolbar
You have probably already noticed that applications written or modified for mobile devices have no menubars and have bigger icons on toolbars. Let's copy our hello-world-2.c to hello-world-3.c and modify it to take advantage of the Hildon library specially created for mobile devices.

Below is the hello-world-3 .c example with highlighted changes to hello-world-2.c:
/* File: hello-world-3.c */
#include <gtk/gtk.h>

#ifdef USE_HILDON
#include <hildon/hildon-program.h>
#include <hildon/hildon-window.h>
#define USE_LIBOSSO
#endif

#ifdef USE_LIBOSSO
#include <libosso.h>

We need to include the Hildon libraries hildon-program and hildon-window:
static osso_init(GtkWidget *window)
{
  g_inst.osso = osso_initialize("org.moblin.helloworld3", "", TRUE, NULL);
  g_inst.window = window;

...

static void add_menu(GtkWidget *window, GtkWidget *box)
{
#ifdef USE_HILDON
  GtkWidget *menubar = gtk_menu_new();
#else
  GtkWidget *menubar = gtk_menu_bar_new();
  gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0);
#endif
  GtkAccelGroup *accel = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel);
  GtkWidget *fileitem = gtk_menu_item_new_with_mnemonic("_File");
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileitem);

  GtkWidget *filemenu = gtk_menu_new();
  gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileitem), filemenu);

  GtkWidget *q = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), q);
  g_signal_connect_swapped(G_OBJECT(q), "activate", G_CALLBACK(quit1), NULL);
#ifdef USE_HILDON
  hildon_window_set_menu((HildonWindow *) window, GTK_MENU(menubar));
#endif
}

In the Hildon-based application you can attach the menu (but not menubar) and toolbar to HildonProgram as a common menu which will appear in all the HildonWindow registered to this HIldonProgram. Or you can set the menu or toolbar to one particular HildonWindow. So, we assigned menu type GtkWidget pointer to menubar variable in our Hildon version and added that menu/menubar to HildonWindow at the end, when all menu elements were already created.
static void add_toolbar(GtkWidget *window, GtkWidget *box)
{
  GtkWidget *toolbar = gtk_toolbar_new();
#ifdef USE_HILDON
  hildon_window_add_toolbar((HildonWindow *) window, GTK_TOOLBAR(toolbar));
#else
  gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, FALSE, 0);
#endif

  GtkToolItem *q = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  g_signal_connect_swapped(G_OBJECT(q), "clicked", G_CALLBACK(quit2), NULL);

  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), q, -1);

Here, we added our toolbar pointer to HildonWindow instead of adding it to a GtkBox container.
int main(int argc, char *argv[])
{
  static char hello[] = "Hello World! (v 3.0)";

  gtk_init (&argc, &argv);
#ifdef USE_HILDON
  HildonProgram *program = hildon_program_get_instance();
  HildonWindow *hildon_window = HILDON_WINDOW(hildon_window_new());
  hildon_program_add_window(program, hildon_window);
  GtkWidget *window = GTK_WIDGET(hildon_window);
#else
  GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#endif
  gtk_widget_set_size_request(window, 250, -1);
  osso_init(window);

...
}

HildonProgram is the main instance of a Hildon-based application. It is inherited from GObject and is the base widget in the Hildon Framework. HildonWindow is the top main window of a Hildon-based application. It is derived from the GtkWindow and provides additional commodities specific to the Hildon framework.

Create D-Bus service file org.moblin.helloworld3.service

Make sure you update the D-Bus service file to reflect the new name of hello-world-3. This file should be placed in the /usr/share/dbus-1/services/ directory in the target file-system:
# File: org.moblin.helloworld3.service
[D-Bus Service]
Name=org.moblin.helloworld3
Exec=/usr/bin/hello-world-3

Create Desktop File hello-world-3.desktop

You also need a new desktop entry file. This file should be placed in /usr/share/mobile-basic-flash/applications/ directory in the target file-system:
# File: hello-world-3.desktop
[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Hello World! v 3.0
Icon=
Categories=Mobile
X-Osso-Service=org.moblin.helloworld3
OnlyShowIn=GNOME;Mobile

Compile and copy hello-world-3 with the next command:
# gcc -o hello-world-3 hello-world-3.c `pkg-config --cflags --libs gtk+-2.0 libosso hildon-1` -D USE_HILDON
# cp hello-world-3 /usr/bin/hello-world-3

Start the Xephyr window:
# ume-xephyr-start

You should see now "Hello World! v 3.0" entry on the Home Screen in categories Mobile and All. Launch the application from the Home Screen. Notice that it has a new type of menu and a bigger toolbar at the bottom.


Figure 5-1: Hello World v3.

If you click the application title, a menu with item "File" and the "Quit" appears. Shortcut Ctrl+Q works with the latest Hildon library update. On normal desktop systems, we can take advantages of mnemonic keyboard navigation, by, for example, typing "Alt-F" to open the File menu. This doesn't work like this in our Hildon app, but pressing F when the menu is activated opens the File submenu.

The Makefile

This is the Makefile that we use to compile all source files and install necessary files in proper locations:
# File: Makefile
BIN=/usr/bin/
APP=/usr/share/mobile-basic-flash/applications/
SER=/usr/share/dbus-1/services/

all: hello-world-1 hello-world-2 hello-world-3 install

hello-world-1: hello-world-1.c
    gcc -o hello-world-1 hello-world-1.c `pkg-config --cflags --libs gtk+-2.0`

hello-world-2: hello-world-2.c
    gcc -o hello-world-2 hello-world-2.c `pkg-config --cflags --libs gtk+-2.0 libosso` -DUSE_LIBOSSO

hello-world-3: hello-world-3.c
    gcc -o hello-world-3 hello-world-3.c `pkg-config --cflags --libs gtk+-2.0 libosso hildon-1` -DUSE_HILDON

clean:
    @rm -f -v *~ hello-world-1 hello-world-2 hello-world-3

install: install1 install2 install3

install1: $(BIN)hello-world-1 $(APP)hello-world-1.desktop

install2: $(BIN)hello-world-2 $(APP)hello-world-2.desktop $(SER)org.moblin.helloworld2.service

install3: $(BIN)hello-world-3 $(APP)hello-world-3.desktop $(SER)org.moblin.helloworld3.service

$(BIN)hello-world-1: hello-world-1
    install -D -m 755 $< $@

$(APP)hello-world-1.desktop: hello-world-1.desktop
    install -D -m 644 $< $@

$(BIN)hello-world-2: hello-world-2
    install -D -m 755 $< $@

$(APP)hello-world-2.desktop: hello-world-2.desktop
    install -D -m 644 $< $@

$(SER)org.moblin.helloworld2.service: org.moblin.helloworld2.service
    install -D -m 644 $< $@

$(BIN)hello-world-3: hello-world-3
    install -D -m 755 $< $@

$(APP)hello-world-3.desktop: hello-world-3.desktop
    install -D -m 644 $< $@

$(SER)org.moblin.helloworld3.service: org.moblin.helloworld3.service
    install -D -m 644 $< $@

uninstall:
    @rm -f -v $(BIN)hello-world-1 $(BIN)hello-world-2 $(BIN)hello-world-3 $(APP)hello-world-1.desktop $(APP)hello-world-2.desktop $(APP)hello-world-3.desktop $(SER)org.moblin.helloworld2.service $(SER)org.moblin.helloworld3.service

Cool Trick!
If the Name parameter in the desktop entry file matches the application title exactly (set by gtk_window_set_title(...) or g_set_application_name(...)) then we can write an application that will only launch a singleton instance without using LibOSSO.

In the file /usr/share/mobile-basic-flash/applications/hello-world-1.desktop, change the line:
Name=Hello World! v 1.0

to
Name=Hello World! (v 1.0)

Start the Xephyr window:
# ume-xephyr-start

You should now see the "Hello World! (v 1.0)" icon on the home screen. Launch the application and take note of the timestamp. Switch back to the home screen without closing the application, and click on "Hello World! v 1.0" icon again. Our application launches with the same timestamp. We have a singleton instance.

Reference Documents
The following documents were used as references in the writing of this document. Refer to these documents for further details.

  1. D-Bus Guide - http://maemo.org/community/wiki/dbusguide/