Maemo 3.x Porting Guide

This document is released under GPL license.

This document has been reviewed for maemo 3.x.

This document contains two parts:

  1. Porting to maemo 3.x from maemo 2.x.
  2. Porting existing applications to maemo 3.x

The first chapter explains what has changed between maemo 2.x and maemo 3.x and the second chapter explains how you can port an existing GTK application to maemo 3.x environment.

Porting to maemo 3.x from maemo 2.x

Overview

This document describes how maemo 2.x applications can be ported to the maemo 3.x environment. The document contains rootstrap updating, API changes, and a few other changes that software developers should know. The scope of this HOWTO is to explain how to port your application from maemo 2.x to maemo 3.x, not from maemo 1.x.

Quick Start for maemo 2.x users

Here's a short list of the major changes, which you have to take into an account, when developing on the maemo 3.x platform.

  1. Upgrade scratchbox and toolchains, you can download them from http://www.scratchbox.org/download/scratchbox-apophis/. For updating rootstraps see maemo 3.X tutorial. This is absolutely needed.

In many cases no changes to maemo 2.x -compliant applications are needed at all. For more information, read the details in this document.

Updated Development Libraries

Following libraries have been updated since maemo 2.x:

bluez-utils
conbtdialogs-dev
hildon-base-lib-dev
hildon-control-panel-dev
hildon-fm-dev
hildon-home-dev
hildon-libs-dev
hildon-navigator-dev
libasound2-dev
libconic0-dev
libcst-dev
libglib2.0-dev
libgstreamer-plugins-base0.10-dev
libgstreamer0.10-dev
libgtk2.0-dev
libgwobex-dev
libosso-certman-dev
libosso-dev
libosso-help-dev
libsdl-net1.2-dev
libsdl1.2-dev
maemo-games-startup-dev
osso-backup
osso-bttools-dev
osso-esd
osso-gwconnect-dev
osso-ic-dev
osso-mediaengine
osso-wlan-security

Library changes conserning all applications

Changes are made to the basic hildon libraries, libosso and GTK+ and glib. No API changes, only additions. Remark that these are not binary compatible so recompiling is necessary.

As a new feature there's a new set of widgets for selecting colors. There's also a new plugin widget API which the color chooser widget set uses. Please see maemo Plugin tutorial [12] for more information.

Desktop files for applications has two new fields, "comment" and "x-text-domain".

Libosso has some minor changes. The osso_time_set() now actually sends a notification about a time change (it does not change the time, however). Functions osso_display_state_on() and osso_display_blanking_pause() can return also OSSO_INVALID in case of invalid parameters.

Function _hildon_file_system_model_load_children() is removed from the Hildon FM header "hildon-file-system-model.h".

Multimedia changes

GStreamer has been upgraded.

SDL and SDL Net

Note, that the libsdl-net library is not included in the Internet Tablet sales image (product image). If you plan to develop games that use SDL Net make sure to provide instructions for the end-user how to get or install the libsdl-net library.

The libsdl is present in the actual product image.

Connectivity

There's a completely new C API for connectivity provided by libconic0 package. It's significally improved over the old osso-ic package. The libconic0 C API is the recommended way of using the maemo connectivity.

bluez-utils package has been upgraded to new version which provides DBUS API for BT discovery, BT pairing, getting/setting local BT name and setting aliases. It deprecates the osso-bttools package which provided a btname command line tool and a DBUS API for setting the BT name and BT pairing.

For more information see maemo Connectivity Architecture [13].

Plugins

Changes for Destkop Status bar, Desktop Home, Control panel and Desktop Navigator plugins.

Desktop Status bar plugins

Status Bar supports now three different kind of plugins: permanent, conditional and temporal. Permanent plugins are shown all the time. Conditional and temporal plugins are shown only when a condition is fulfilled.

The plugin API contains a new optional function [plugin_name]_set_conditional() which can be used by Status Bar to set the status of the plugin. There is also a new signal hildon_status_bar_update_conditional with a boolean parameter that the plugin can send to Status Bar to update the conditional status.

There is also a new function for the temporary and conditional plugins: hildon_status_bar_item_set_button(). These plugins can leave the button widget as NULL in the initialization and set it later with the new function.

Desktop Home plugins

No API changes, but Home now supports resizing of the plugins. The plugin can choose whether is is resizeable in X or Y direction and what is the minimum width and height of the plugin. Changes are made to the .desktop file.

Control panel plugins

The Control panel plugins are now divided into three categories: general, connectivity, personalisation and extras. Selecting category is done in .desktop file.

Desktop Navigator plugins

A new widget, HildonThumbMenuItem, is meant for implementing thumbable menu. The API has two main functions hildon_thumb_menu_item_new_with_labels and hildon_thumb_menu_item_set_images. There's also a funtion hildon_menu_set_thumb_mode, to set whole menu structure to thumb mode or back.

For more information about desktop plugins see Maemo desktop plugin tutorial.

Summary

As a summary the changes are not significant and mostly only recompiling is needed. Maemo 3.x introduces some new features which can be taken in account when porting applications.

Porting existing application to maemo 3.x

Describes how to port an existing application to maemo 3.x.

Overview

This section describes a porting process of an application to maemo platform [1]. When starting to port an application to maemo platform the first phase is to setup the development environment which is described in the chapter 2 at the Maemo SDK Tutorial [2]. The actual porting after that is described in this document.

Introduction

As an example application for porting is used Monkey Bubble [3] which is a GNOME/GTK+ based game. It has simple controls and supports network game. Monkey Bubble version 0.4.0 is used in this example.

Monkey Bubble interface consists main window, menus and a couple of dialogs.

Bubble game

Illustration 1 Monkey Bubble main window

The Illustration 1 is a screenshot of the main window with a ongoing game. Game is played with configurable keys, default arrows left, right and up. Target is to make all bubbles disappear. When three or more bubbles with the same color are grouped they disappear.

Network game dialog

Illustration 2 Network game window

The Illustration 2 is a screenshot of the network game window, where user can join a network game. It includes field for selecting server, status about players and game and control buttons.

Application file structure

Monkey Bubble already has a nicely structured source tree so there wasn't need to modify it. XML formatted Glade[4] files can be found from data directory. Help files can be found from help directory. Graphics can be found from pixmaps directory. Localization files are located in po directory. Audio files can be found from sounds directory. The source code itself is located under src directory in subdirectories like audio, input, monkey, net, ui, util and view.

Monkey Bubble directory tree

./po
./data
./pixmaps
./pixmaps/bubbles
./pixmaps/frozen-bubble
./pixmaps/snake
./pixmaps/number
./sounds
./src
./src/util
./src/input
./src/monkey
./src/view
./src/audio
./src/net
./src/ui
./help
./help/C
./help/fr
	  

Monkey Bubble uses already GNU autotools so needed Makefile.am and configure.in files were brought up with Monkey Bubble. Only autogen.sh was missing so it was created. More about GNU autotools usage can be read from GNU development tools tutorial [5]. In chapters 5, 6 and 7 are just specified changes needed in Makefile.am and configure.ac files.

autogen.sh

#!/bin/sh
set -x
glib-gettextize --copy --force
libtoolize --automake
intltoolize --copy --force --automake
aclocal-1.7
autoconf
autoheader
automake-1.7 --add-missing --foreign
	  

Remember to set this executeable with chmod a+x autogen.sh.

Requirements and Configure Changes

Maemo SDK has mainly most of the Monkey Bubble requirements. Biggest exceptions are libgnomeui, bonobo and librsvg. Monkey Bubbles also uses GTK+ version 2.10 when maemo offers 2.6. After looking the source, we're able to remove libgnomeui and bonobo dependencies. Some changes must be made to downgrade from GTK+ version 2.10. So we need install additionally librsvg and it's dependencies.

First start from librsvg installation. It has three dependency and three more from development package so you must obtain and install following (or corresponding) packages for example from Debian packages repositories [8] and install them inside the scratchbox.

libbz2-1.0
libbz2-dev
libcroco3
libcroco3-dev
libgsf-1
libgsf-1-dev
librsvg2-2
librsvg2-dev
	  

Then we need to modify the configure.in file. Depenency to libgnomeui-2.0 must be removed. And because we're going to hildonize the UI we must add hildon-libs and libosso to the list between PKG_CHECK_MODULES(UI,[ and following]). Line gtk+-2.0 >= 2.10 must be changed to gtk+-2.0 >= 2.6.

From Makefile.am remove the help from SUBDIRS list because the provided help is incompatible with maemo help framework. Remove line help/Makefile from configure.in AC_OUTPUT section as well. Additionally help can be implemented but it's out of the scope of this HOWTO.

After the changes autogen.sh must be executed to apply the changes.

Basic porting

Localization and src/ui/main.c

The very first thing is to get Monkey Bubble to work on maemo environment. For localization Monkey Bubble uses bonobo. It must be replaced with compatible localization described more precisiously in Localization HOWTO [9].

First localization must be initialized in src/ui/main.c. Following lines must be removed from it:

#include <bonobo/bonobo-i18n.h>
#include <libgnomeui/gnome-ui-init.h>
	  

These lines must be added:

#include <locale.h>

#include <libintl.h>
	  

And new lines in the main() function before bindtextdomain function call:


setlocale(LC_ALL, "");
bind_textdomain_codeset(PACKAGE, "UTF-8");
	  

Also remove the gnome_program_init function call.

For the localization to work you must remove #include <bonobo/bonobo-i18n.h> line. In order to make your strings localizable, you need to wrap the strings you want translated in gettext("String") calls. In practice, writing gettext() for every string is tedious. The common practice is to set the following #defines. The N_ is used for gettext_noop(), but it's not available so just do nothing. So the following lines to must be added to the beginning of every file which uses localization:

#include <libintl.h>
#define _(String) gettext(String)
#define N_(String) (String)
	  

Files using localization includes following:

src/ui/keyboard-properties.c
src/ui/ui-network-client.c
src/ui/ui-network-server.c
src/ui/ui-main.c
	  

Removing GNOME features

Next thing is to remove other GNOME functionality. File src/ui/ui-main.c is next to edit. Remove following include lines:

#include <libgnomeui/gnome-about.h>

#include <libgnome/gnome-sound.h>
#include <libgnome/gnome-help.h>
	  

Then uncomment contents of functions about and show_help_content and remove completely function show_error_dialog and it's prototype from the beginning.

GTK+ 2.10 is used for defining key accelerations. We can safely remove whole key defination functionality so comment out (or remove) lines from src/ui/keyboard-properties.c function edit_keys_dialog_new as follow (added #if 0 and #endif blocks):


  /* Column 2 */
#if 0
  cell_renderer = gtk_cell_renderer_accel_new();
  g_object_set(cell_renderer,
               "editable", TRUE,
               "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
               NULL);
  g_signal_connect (G_OBJECT (cell_renderer), "accel-edited",
                    G_CALLBACK (accel_edited_callback),
                    kp);
#endif

  PRIVATE(kp)->model = GTK_TREE_MODEL( tree );

#if 0
  g_object_set (G_OBJECT (cell_renderer),
                "editable", TRUE,
                NULL);
#endif
  column = gtk_tree_view_column_new ();
  gtk_tree_view_column_set_title (column, _("Shortcut _Key"));
#if 0
  gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func (column, cell_renderer, accel_set_func, NULL, NULL);
#endif
  gtk_tree_view_column_set_sort_column_id (column, KEYVAL_COLUMN);
  gtk_tree_view_append_column (GTK_TREE_VIEW (w), column);
	  

Also remove functions accel_set_func and accel_edited_callback.

Now you can try configuring and compiling the game with following commands:

./autogen.sh
./configure
make
	  

Everything should go fine and it should be compiled properly. You can also try to install and run the game and you should see following:

Monkey Bubble on maemo

Illustration 3 Monkey Bubble on maemo

It's fine, but you can see that the borders and menus are not hildonized. Also if you try some menu functionality they won't fit there nicely. So let's go to the next chapter for customization.

User interface changes

Hildonizing Main View

Monkey bubble uses Glade[4] for it's UI creation. Unfortunately Glade doesn't support Hildon so we have to make some changes.

First of all in data/monkey-bubble.glade the main_window type GtkWindow must be changed to GtkVBox. Secondly all window properties, except "visible" must be removed. Third the menu must be removed. That's done by removing completely the child containing GtkMenuBar and it's subchilds.

Next change the src/ui/ui-main.c function ui_main_new. Add following lines to the beginning of the function and remove the KeyboardProperties * kp; line:

HildonProgram * program;
GtkWidget * container;
GtkWidget * main_menu; 
	  

Then make following modifications, the old code is commented out and new lines added after it:

/*
PRIVATE(ui_main)->window = glade_xml_get_widget( PRIVATE(ui_main)->glade_xml, "main_window");
*/
container = glade_xml_get_widget( PRIVATE(ui_main)->glade_xml, "main_window");
program = HILDON_PROGRAM(hildon_program_get_instance());
PRIVATE(ui_main)->window = hildon_window_new();
g_signal_connect_swapped(PRIVATE(ui_main)->window ,"destroy",GTK_SIGNAL_FUNC(quit_program),ui_main);
hildon_program_add_window(program, HILDON_WINDOW(PRIVATE(ui_main)->window));
gtk_container_add(GTK_CONTAINER(PRIVATE(ui_main)->window),
	GTK_WIDGET(container));
g_set_application_name(_("Monkey Bubble"));
	  

Because we removed the menu from the Glade file we must construct it manually in Hildon compatible way. Also we do not need all functionality provided by the old menu so we can leave things out. What we need is a new game, join network game, pause and quit. Let's construct such a tiny menu after gtk_box_pack_end function call:

main_menu = gtk_menu_new();

item = gtk_menu_item_new_with_label(_("New game"));
g_signal_connect_swapped( item,"activate",GTK_SIGNAL_FUNC(new_1_player_game),ui_main);
gtk_menu_append(main_menu, item);

item = gtk_menu_item_new_with_label(_("Join network game"));
g_signal_connect_swapped( item,"activate",GTK_SIGNAL_FUNC(new_network_game),ui_main);
gtk_menu_append(main_menu, item);

item = gtk_menu_item_new_with_label(_("Pause"));
g_signal_connect_swapped( item,"activate",GTK_SIGNAL_FUNC(pause_game),ui_main);
gtk_menu_append(main_menu, item);

item = gtk_menu_item_new_with_label(_("Quit"));
g_signal_connect_swapped( item,"activate",GTK_SIGNAL_FUNC(quit_program),ui_main);
gtk_menu_append(main_menu, item);

hildon_window_set_menu(HILDON_WINDOW(PRIVATE(ui_main)->window), GTK_MENU(main_menu));

gtk_widget_show_all(GTK_WIDGET(main_menu));
	  

After that you can remove the old menu releated code starting from the next line and ending to g_signal_connect_swapped function call so that it's the last removed line. You have to remove also the unneeded functions and their prototypes:

ui_main_new_2_player_game
new_2_player_game
new_network_server
stop_game
about
show_help_content
show_preferences_dialog
	  

Remember to add proper include to the beginning of the file:

#include <hildon-widgets/hildon-program.h>
	  

Now the main view and menu should be hildonized properly and look like:

Hildonized Monkey Bubble on maemo

Illustration 4 Hildonized Monkey Bubble on maemo

Hildonizing Network Window

The Network game window shown in the Illustration 5 is still GtkWindow and is not hildonized properly. The solution is to make it GtkVBox and place it under a new GtkDialog.

Network game window

Illustration 5 Network game window

Let's start again with the glade file located at data/netgame.glade. Change the GtkWindow to GtkVBox and remove all properties except "visible". Change GtkScrolledWindow widget property "height_request" to the value 200 to make everything fit on the screen. We're also missing a close button now because Monkey Bubble relies on the close button of GtkWindow. There's a nice place between "quit game" and "ready" buttons. You may just copy-paste the whole child containing "quit_button" widget, rename it as "close_button", change the number values of the other widgets name under it for example to 9 to prevent collisions and change the GtkLabel "Quit game" to "Close". That's enough for the glade file.

Next src/ui/ui-network-client.c and ui_network_client_new function. Add this line to the beginning of the function:

GtkWidget * container;
	  

Make following changes to create a new GtkDialog and show network_window contents under it. Old code is commented out and the new lines added after it:

/*
PRIVATE(ngl)->window = glade_xml_get_widget( PRIVATE(ngl)->glade_xml, "network_window");
*/
PRIVATE(ngl)->window = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(PRIVATE(ngl)->window), _("Network game"));
container = glade_xml_get_widget( PRIVATE(ngl)->glade_xml, "network_window");
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(PRIVATE(ngl)->window)->vbox), container);
item = glade_xml_get_widget( PRIVATE(ngl)->glade_xml, "close_button");
g_signal_connect_swapped( item,"clicked",GTK_SIGNAL_FUNC(close_signal),ngl);
	  

And to the end of the ui_network_client_new function, before return add following line:

gtk_widget_show_all(GTK_WIDGET(PRIVATE(ngl)->window));
	  

There's need for a new function called close_signal which is called upon the close button is pressed, add this function before the ui_network_client_new function:

static gboolean close_signal(gpointer    callback_data,
                             guint       callback_action,
                             GtkWidget  *widget) {

    UiNetworkClient * self;
    self = UI_NETWORK_CLIENT(callback_data);

    quit_signal(callback_data, callback_action, widget);
    gtk_widget_hide_all(PRIVATE(self)->window);
    return FALSE;
}
	  

There's still one problem. The new close button is located in "connected_game_hbox" widget which is set unsensitive when not connected to the server. We would like to close the window always. So let's change sensitivity of the other widgets properly. Add this new function after previously added close_signal function:

void connected_set_sensitive(UiNetworkClient * ngl, gboolean sensitive) {
    set_sensitive( glade_xml_get_widget( PRIVATE(ngl)->glade_xml
        ,"scrolledwindow2"), sensitive);
    set_sensitive( glade_xml_get_widget( PRIVATE(ngl)->glade_xml
        ,"quit_button"), sensitive);
    set_sensitive( glade_xml_get_widget( PRIVATE(ngl)->glade_xml
        ,"ready_button"), sensitive);
}
	  

There is one gtk_widget_set_sensitive function call in ui_network_client_new and multiple set_sensitive calls for connected_game_hbox, replace these with:

connected_set_sensitive(ngl, FALSE);
	  

There's also one call in recv_network_xml_message function where the boolean value must be TRUE.

That's it, network game dialog should be now hildonized and contain new close button. The "quit game", "ready" and player list should change it's sensitivity properly upon connected and disconnected.

Hildonized network game dialog

Illustration 6 Hildonized network game dialog

State saving

Maemo supports state saving and background killing application. Application can later be loaded up again with the same state as before. This section describes the steps needed to make Monkey Bubble support state savings.

State saving changes in src/ui

We must create some new files, first file for global data src/ui/global.h:

#ifndef GLOBAL_H
#define GLOBAL_H

#include <libosso.h>

#include "game-1-player.h"

#define MONKEY_TEMP "/tmp/monkey_level_state"

struct GlobalData {
    osso_context_t *osso;
    Game1Player *game;
};

struct StateData {
    int game;
    int level;
    int score;
    int loadmap;
};

extern struct StateData state;
extern struct GlobalData global;

#endif
	

Second one is src/ui/state.h:

#ifndef STATE_H
#define STATE_H

#include <glib.h>

gboolean state_load(void);
gboolean state_save(void);
void state_clear(void);

#endif
	

Then src/ui/state.c which implements loading, saving and cleaning the state:

#include <libosso.h>

#include "state.h"
#include "global.h"

struct StateData state;

gboolean state_load(void)
{
    osso_state_t osso_state;
    osso_return_t ret;

    osso_state.state_size = sizeof(struct StateData);
    osso_state.state_data = &state;

    ret = osso_state_read(global.osso, &osso_state);
    if (ret != OSSO_OK)
        return FALSE;
    return TRUE;
}

gboolean state_save(void)
{
    osso_state_t osso_state;
    osso_return_t ret;

    osso_state.state_size = sizeof(struct StateData);
    osso_state.state_data = &state;

    ret = osso_state_write(global.osso, &osso_state);

    if (ret != OSSO_OK)
        return FALSE;
    return TRUE;
}

void state_clear(void)
{
    state.game = 0;
    state.level = 0;
    state.score = 0;
    state.loadmap = 0;

    state_save();
}
	

These must be added to src/ui/Makefile.am to monkey_bubble_SOURCES list before $(NULL):

        state.c state.h global.h \
	

Some changes to src/ui/main.c, new includes, definations and functions:

#include <libosso.h>

#include "state.h"
#include "global.h"

#include "game-1-player.h"

#define APPNAME "monkey_bubble"
#define APPVERSION "0.0.1"

struct GlobalData global;

static void _top_cb(const char *args, gpointer data)
{
}

static void _hw_cb(osso_hw_state_t * state, gpointer data)
{
       if(state->shutdown_ind)
       {
               state_save();
               gtk_main_quit();
       }
}

osso_context_t *osso_init(void)
{
    osso_context_t *osso =
        osso_initialize(APPNAME, APPVERSION, TRUE, NULL);

    if (OSSO_OK != osso_application_set_top_cb(osso, _top_cb, NULL))
        return NULL;

    if (OSSO_OK != osso_hw_set_event_cb(osso, NULL, _hw_cb, NULL))
        return NULL;

    return osso;
}
	

And change the main() function as follow:

  global.osso = osso_init();
  if (!global.osso) {
       perror("osso_init");
       exit(1);
  }
  global.game = NULL;

  gtk_init(); /* This line already exists */

  if (!state_load()) {
    perror("state_load");
  }

/* .... */

  if (state.game == 1) {
       continue_game();
  }

  gtk_main (); /* This line already exists */
	

Set the topmost callback in src/ui/ui-main.c. It saves the current level and state upon Monkey Bubble lost topmost status and sets hibernate flag properly. New continue_game function to continue after loading state:

#include "global.h"
#include "state.h"

static void ui_main_topmost_cb(GObject *self, GParamSpec *property_param, gpointer null)
{
       HildonProgram *program = HILDON_PROGRAM(self);

       if (program == NULL) return;

       if (hildon_program_get_is_topmost(program)) {
               hildon_program_set_can_hibernate(program, FALSE);
       } else {
               if (state.game == 1 && global.game!=NULL) {
                       game_1_player_save(global.game);
                       state.loadmap=1;
               }
               state_save();
               hildon_program_set_can_hibernate(program, TRUE);
       }
}

void continue_game(void) {
        UiMain * ui_main;
        ui_main = ui_main_get_instance();

        ui_main_new_1_player_game(ui_main);
}

	

Addition to the ui_main_new() function, after g_set_application_name function call:


g_signal_connect(G_OBJECT(program), "notify::is-topmost", G_CALLBACK(ui_main_topmost_cb), NULL);
	

To the new_1_player_game function add before ui_main_new_1_player_game function call:


state_clear();
	

Must add function prototype to src/ui/ui-main.h:

void continue_game(void);
	

To game-1-player-manager.c following changes to keep track of level and scores and continue from last level and score:

#include "global.h"

/* ... */

static gboolean startnew_function(gpointer data) {

  /* ... */

  PRIVATE(manager)->current_level++;
  state.level = PRIVATE(manager)->current_level;  /* New line */

  /* ... */

}

static void game_1_player_manager_state_changed(Game * game,
                                                Game1PlayerManager * manager) {

  /* ... */

    PRIVATE(manager)->current_score = game_1_player_get_score( GAME_1_PLAYER(game));
    state.score = PRIVATE(manager)->current_score;  /* New line */


  /* ... */

}

void game_1_player_manager_start(GameManager * g) {

  /* ... */

  /* Old code:
  PRIVATE(manager)->current_level = 0;
  PRIVATE(manager)->current_score = 0;

  Replace with:
  */
  if (state.game == 1 && state.level > 0 ) {
    PRIVATE(manager)->current_level = state.level;
    PRIVATE(manager)->current_score = state.score;
  } else {
    state.level = 0;
    PRIVATE(manager)->current_level = 0;
    PRIVATE(manager)->current_score = 0;
  }
  state.game = 1;

  /* ... */

}
	

The changes to src/ui/game-1-player.c to save and load the level state:

#include "global.h"

void game_1_player_save(Game1Player *game)
{
        monkey_save(PRIVATE(game)->monkey, MONKEY_TEMP);
}

Game1Player * game_1_player_new(GtkWidget * window,MonkeyCanvas * canvas, int level,gint score) {
	/* ... */

	/* Old code:
            PRIVATE(game)->monkey =
                monkey_new_level_from_file(DATADIR"/monkey-bubble/levels",
                                           level);
	  Replace with:
	*/ 
	if (state.loadmap==1) {
          PRIVATE(game)->monkey =
                monkey_new_level_from_file(MONKEY_TEMP,
                                           0);
          state.loadmap = 0;
        } else {
          PRIVATE(game)->monkey =
                monkey_new_level_from_file(DATADIR"/monkey-bubble/levels",
                                           level);
        }

	/* ... */
}
	

Add function prototype to src/ui/game-1-player.h:

void game_1_player_save(Game1Player *game);
	

State saving changes in src/monkey

Add save feature to src/monkey/board.c:

void
board_save_to_file (Board * board, const char *filename)
{
        #define MUL 4 
        GError *error = NULL;
        GIOChannel *channel;
        gint i, j, s=0;
        gchar buffer[COLUMN_COUNT*3+2];
        gsize written = 0;

        if (PRIVATE(board)->bubble_array==NULL) return;

        channel = g_io_channel_new_file (filename, "w+", &error);

        if (channel == NULL) return;

        for (i = 0; i < ROW_COUNT; i++)
        {
                for (j = 0; j < COLUMN_COUNT*MUL; j++)
                        buffer[j] = ' ';

                if (i%2==1) {
                        s = 2; 
                } else {
                        s = 0;
                }

                for (j = 0; j < COLUMN_COUNT; j++)
                {
                        Bubble *b;
                        Color c;

                        if (s>0 && (j+1==COLUMN_COUNT)) break;

                        b = PRIVATE(board)->bubble_array[i*COLUMN_COUNT+j];
                        if (b!=NULL) {
                                c = bubble_get_color(b);
                                buffer[j*MUL+s] = '0'+(int)c;
                        } else {
                                buffer[j*MUL+s] = '-';
                        }
                }
                buffer[COLUMN_COUNT*MUL]='\n';
                buffer[COLUMN_COUNT*MUL+1]=0;

                g_io_channel_write_chars(channel, (const gchar *)&buffer, -1, &written, &error);
        }

        g_io_channel_shutdown (channel, TRUE, &error);
        g_io_channel_unref (channel);
}
	

And function prototype to src/monkey/board.h:

void board_save_to_file (Board * board, const char *filename);
	

Provide a call to this function in src/monkey/playground.c:

void 
playground_save(Playground * self, const gchar * level_filename)
{
       board_save_to_file(PRIVATE(self)->board, level_filename);
}
	

And function prototype to src/monkey/playground.h:

void playground_save(Playground * self, const gchar * level_filename);
	

Finally a call to the save feature in src/monkey/monkey.c:

void monkey_save(Monkey *monkey, const gchar * filename)
{
       playground_save(PRIVATE(monkey)->playground, filename);
}
	

And function prototype to src/monkey/monkey.h:

void monkey_save(Monkey *monkey, const gchar * filename);
	

Now the game should be saving it's state and be background killable. It can be tested by setting Monkey Bubble to background (for example by starting another application) and issuing following commands (in scratchbox):

dbus-send --system /com/nokia/ke_recv/bgkill_on com.nokia.ke_recv.bgkill_on.bgkill_on
dbus-send --system /com/nokia/ke_recv/bgkill_off com.nokia.ke_recv.bgkill_off.bgkill_off
	

After changing back to Monkey Bubble it shoud show banner "Monkey Bubble - resuming".

Networking changes

To acquire a network connection on the target device all we have to do is to load libosso-ic-preload.so before starting the application. That is done by setting LD_PRELOAD environment variable before execution. For that we create a startup script src/ui/monkey-bubble.sh which is used in .desktop and .service file. This is however deprecated way, the recommended way is to use new libconic0 package.

The new file src/ui/monkey-bubble.sh to preload the library and start Monkey Bubble:

#!/bin/sh
case `uname -m` in
        arm*) export LD_PRELOAD=/usr/lib/libosso-ic-preload.so;;   # arm*
        *) ;;                                                      # some other architecture
esac

/usr/bin/monkey-bubble
	  

Set it executeable by chmod a+x src/ui/monkey-bubble.sh.

Add this line to src/ui/Makefile.am to install the script properly:

bin_SCRIPTS = monkey-bubble.sh
	  

Integration to menu

Application integration to menu needs desktop and service files. In this chapter is described needed additions and changes.

1) Monkey Bubble already has a monkey-bubble.desktop which is generated from monkey-bubble.desktop.in so we have to make changes to it. The Exec value must have full path and two new values must be added:

Exec=/usr/bin/monkey-bubble.sh
X-Osso-Type=application/x-executable
X-Osso-Service=monkey_bubble
	  

2) Create a service file com.nokia.monkey_bubble.service:

[D-BUS Service]
Name=com.nokia.monkey_bubble
Exec=/usr/bin/monkey-bubble.sh
	  

3) Add section of service file in Makefile.am:

dbusdir=$(prefix)/share/dbus-1/services
dbus_DATA=com.nokia.monkey-bubble.service
	  

4) Also change proper path for desktop file in Makefile.am:

applicationsdir = $(datadir)/applications/hildon
	  

5) These files must be in Makefile.am's EXTRA_DIST variable. The desktop file is there already so only service file needs to be added before $(NULL):

	com.nokia.monkey_bubble.service  \
	  

Application Packaging

This chapter describes how Monkey Bubble sources were modified to make .deb package building possible. Actual packaging operation after that is specified in another document.

1) Rename Monkey Bubble source directory to maemo-monkey-bubble-0.0.1:

mv monkey-bubble-0.4.0 maemo-monkey-bubble-0.0.1
	  

2) Package source dir maemo-monkey-bubble-0.0.1/ in maemo-monkey-bubble-0.0.1.tar.gz

tar czvf maemo-monkey-bubble-0.0.1.tar.gz maemo-monkey-bubble-0.0.1
	  

3) Go to the source directory

cd maemo-monkey-bubble-0.0.1
	  

4) Set full name environment variable:

export DEBFULLNAME="Mr maemo"
	  

Now we are ready to start actual work. Debian New Maintainers' Guide [6] might be useful in this phase.

5) Make initial debianization:

dh_make -e xxxxxxx.xxxxxx@maemo.org -f ../maemo-monkey-bubble-0.0.1.tar.gz
	

6) Next dh_make will ask a question and print a summary of the package:

Type of package: single binary, multiple binary, library, or kernel module?
 [s/m/l/k] s

Maintainer name : Mr maemo
Email-Address   : xxxxxxx.xxxxxx@maemo.org
Date            : Thu, 14 Dec 2006 10:42:18 +0200
Package Name    : maemo-monkey-bubble
Version         : 0.0.1
Type of Package : Single
Hit <enter> to confirm: 
	

7) Modify debian dir. Only files changelog, compat, control, copyright, docs and rules are needed. In the control file packages hildon-libs0 (>= 0.14.8), librsvg2-2 and libglade2 must be added to Depends. You should also set the Section field into user/games to make the package compatible with Application Manager. After that the package structure is ok. Then we need to start making changes in configuration files to be able to build the package.

Note: libglade2 must be installed from maemo repositories [10]. ARMEL Debian packages of librsvg2-2 and librsvg2-common is available at garage.maemo.org [11].

8) Because we disabled the key defination dialog we need to set them in debian/postinst:

#!/bin/sh

set -e

case "$1" in
    configure)

    gconftool-2 -s /apps/monkey-bubble/player_1_shoot -tstring Up
    gconftool-2 -s /apps/monkey-bubble/player_1_left -tstring Left
    gconftool-2 -s /apps/monkey-bubble/player_1_right -tstring Right
    ;;

    abort-upgrade|abort-remove|abort-deconfigure)

    ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

exit 0
	

And set it executable with chmod a+x debian/postinst.

9) A link to desktop file must be added to /etc/others-menu/extra_applications to show it in menu. Link is created by creating debian/maemo-monkey-bubble.links file with following contents:

usr/share/applications/hildon/monkey-bubble.desktop etc/others-menu/extra_applications/monkey-bubble.desktop
	

10) Add following lines in configure.ac:

PKG_CHECK_MODULES(HILDON, hildon-libs >= 0.14.8)
AC_SUBST(HILDON_LIBS)
AC_SUBST(HILDON_CFLAGS)
	

11) Make following two changes in Makefile.am (remember to use tab to indent):

Add items in EXTRA_DIST before $(NULL):

	autogen.sh \
	debian/changelog \
	debian/compat \
	debian/copyright \
	debian/control \
	debian/rules \
	debian/docs \
	

Add deb rule:

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

12) Now the source dir should be ready for packaging which is described in maemo SDK Tutorial [7].

If you want to create a package that is useable with the Application Manager, see document Making a package for the Application Manager.

Modified Monkey Bubble sources and binaries can be found from garage.maemo.org [11].



Improve this page