Maemo 4.0 Porting Guide

This document has been reviewed for maemo 4.0.

This document describes porting existing GTK+ applications to the maemo 4.0 environment.

This tutorial is best studied with the Maemo 4.0 Tutorial and How to write new applications in maemo 4.0

Porting existing application to maemo 4.0

Describes how to port an existing application to maemo 4.0.

Overview

This section describes the porting process of an application to maemo platform [1]. When starting to port an application to maemo platform, the first phase is to set up the development environment, described in the section 2 of the maemo SDK Tutorial [2]. The actual porting after that is described in this document.

Go to top

Introduction

Application that is used as an example for porting is Monkey Bubble [3], a GNOME/GTK+ based game. It has simple controls, and supports network play. Monkey Bubble version 0.4.1 is used in this example. The ported version for maemo 4.0 can be found (both source package and binaries) in maemo.org

Monkey Bubble interface consists of the 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 an ongoing game. Game is played with configurable keys, defaults are the arrow keys: left, right and up. Target is to make all the bubbles disappear. When three or more bubbles with the same color are grouped, they will 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.

Go to top

Application File Structure

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

./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
See also Creating Application Files Structure in How to write new applications in maemo 4.0.

Go to top

Requirements and Configure Changes

Monkey Bubble already uses GNU autotools, so the needed Makefile.am and configure.in files are there. Only autogen.sh was missing, so it was created. More about GNU autotools use can be read from GNU development tools tutorial [5]. Chapters 5, 6 and 7 discuss the 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 executable with chmod a+x autogen.sh.

Maemo SDK meets most of the Monkey Bubble requirements. Biggest exceptions are libgnomeui, bonobo and librsvg. After checking the sources, the libgnomeui and bonobo dependencies can be removed. So, librsvg and its dependencies are additionally needed to be installed.

Start from librsvg installation. One version is available in Chinook extras. Install from there the following packages:

librsvg2-2
librsvg2-common
librsvg2-dev

Then the configure.in file needs to be modified. Dependency to libgnomeui-2.0 must be removed. And because the UI is going to be hildonized, hildon-1 and libosso need to be added to the list between PKG_CHECK_MODULES(UI,[ and following ]).

From Makefile.am, remove the help from SUBDIRS list, because the provided help is incompatible with Hildon help framework. Remove line help/Makefile from configure.in AC_OUTPUT section as well. Additionally, help can be implemented but it is out of the scope of this HOW-TO.

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

Go to top

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 in more detail in Localization HOW-TO [9].

First, localization must be initialized in src/ui/main.c. The 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:

if(!g_thread_supported()) {
  g_thread_init(NULL);
}
setlocale(LC_ALL, "");
bind_textdomain_codeset(PACKAGE, "UTF-8");  

Remove also the gnome_program_init function call.

For the localization to work, the line #include <bonobo/bonobo-i18n.h> needs to be removed. In order to make the strings localizable, they need to be wrapped 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 is not available, so the macro does nothing. So the following lines must be added to the beginning of every file using localization:

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

Files using localization include the 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 the following include lines:

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

Then comment out contents of functions about and show_help_content, and remove completely function show_error_dialog and its prototype from the beginning.

Now the game can be configured and compiled with following commands:

./autogen.sh
./configure
make

Everything should go fine and the game should be compiled properly. When trying to install and run the game, the following should be seen:

Monkey Bubble on maemo

Illustration 3 Monkey Bubble on maemo

This is fine, but it can be seen that the borders and menus are not hildonized. Also, if menu functionality is tried, they will not fit there nicely. So the next step is customization.

Go to top

User Interface Changes

Hildonizing Main View

Monkey bubble uses Glade[4] for its UI creation. Unfortunately, Glade does not support Hildon, so some changes have to be made.

First of all, in data/monkey-bubble.glade the main_window type GtkWindow must be changed to GtkVBox. Then all window properties, except "visible" must be removed. After that, the menu must be removed. That is done by removing completely the child containing GtkMenuBar and its subchildren.

Next the src/ui/ui-main.c function ui_main_new should be changed. 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 the menu was removed from the Glade file, it must be constructed manually in Hildon-compatible way. Also, not all the functionality provided by the old menu is needed, so things can be left out. Needed parts are new game, join network game, pause and quit. Such a tiny menu can be constructed 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 the code related to the old menu can be removed, starting from the next line and ending to g_signal_connect_swapped function call, the latter being the last removed line. Also the unneeded functions and their prototypes need to be removed:

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/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

The starting point here is 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. A close button is also missing now, because Monkey Bubble relies on the close button of GtkWindow. There is a nice place between "quit game" and "ready" buttons. You can 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 is enough for the glade file.

Next, the src/ui/ui-network-client.c and ui_network_client_new functions. 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 the following line:

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

A new function is needed, called close_signal, which is called when 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 is still one problem: The new close button is located in "connected_game_hbox" widget, which is set insensitive when not connected to the server. The ability to close the window is always needed. The sensitivity of the other widgets has to be changed properly. Add this new function after the 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 is also one call in recv_network_xml_message function, where the boolean value must be TRUE.

That is all; network game dialog should be now hildonized and contain a new close button. The "quit game", "ready" and player list should change their sensitivity properly upon connected and disconnected states.

Hildonized network game dialog

Illustration 6 Hildonized network game dialog

Go to top

State Saving

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

State Saving Changes in src/ui

some new files need to be created, 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, implementing 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, definitions 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 follows:

  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 when Monkey Bubble loses its topmost status, and sets the 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);
}

In 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();

Function prototype must be added 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 its 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 should show banner "Monkey Bubble - resuming".

Go to top

Network Changes

For the networking code we do not need to do any specific changes to the existing code, but in order for the application to provide a network connection selection dialog or to automatically select a proper connection, the LibConIC library must be used.

The best place for network connection changes is before opening the join network dialog. So, let's make modifications to src/ui/ui-main.c file. First add a proper header:

#include <conic.h>

After that, we'll modify the new_network_game function which is called when a new network game is launched. Remove the old code and replace it with this:

        ConIcConnection *ic;
        ic = con_ic_connection_new();
        g_signal_connect(ic, "connection-event", (GCallback)network_connected, NULL);
        con_ic_connection_connect(ic, CON_IC_CONNECT_FLAG_NONE);

The code creates a new ConIcConnection and connects the connection-event signal. This signal is called when a connection event happens. After that, it calls connect signal in order to request a new connection. We have to do one more thing, specify what happens when a connection event comes. That is handled in new network_connected function, which is implemented as follows:

static void network_connected(ConIcConnection *cnx, ConIcConnectionEvent *event, gpointer user_data)
{
        UiNetworkClient  * ngl;

        switch (con_ic_connection_event_get_status(event)) {
                case CON_IC_STATUS_CONNECTED:
                  ngl = ui_network_client_new();
                  break;
                case CON_IC_STATUS_DISCONNECTED:
                case CON_IC_STATUS_DISCONNECTING:
                default:
                  break;
        }
}

This function simply checks which status the event gives us and if it is CON_IC_STATUS_CONNECTED, it creates a new client window, where the user is able to connect to a server.

There is still one more thing to do. We must add conic to configure.in into the list following PKG_CHECK_MODULES for UI.

After these changes, starting a new network game will ensure that there is a network connection available.

Go to top

Integration to Menu

Application integration to menu needs .desktop and .service files. This section describes the needed additions and changes. If you are not familiar with .desktop and .service files, see Maemo Tutorial first.

1) Monkey Bubble already has a monkey-bubble.desktop, which is generated from monkey-bubble.desktop.in, so the changes need to be made to it. First of all, the file should be moved under ./data/ directory, in order to make the common maemo application file structure; the path in Makefile.am should be changed accordingly:

applications_in_files = data/monkey-bubble.desktop.in

2. Then the .desktop.in file needs to be modified: The Exec value must have full path and two new values must be added:

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

3) Create a service file com.nokia.monkey_bubble.service in directory ./data/:

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

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

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

5) Change the proper path for desktop file in Makefile.am:

applicationsdir = $(datadir)/applications/hildon

6) 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):

data/com.nokia.monkey_bubble.service  \
      

Go to top

Application Packaging

This section 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 the actual work can be started. 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-1 (>= 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 the configuration files need to be changed to be able to build the package.

N.B. 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 the key definition dialog was disabled, definitions need to be set in debian/postinst:

#!/bin/sh

set -e

case "$1" in
    configure)

    gconftool-2 -s /apps/monkey-bubble/player_1_shoot --type=string Up
    gconftool-2 -s /apps/monkey-bubble/player_1_left --type=string Left
    gconftool-2 -s /apps/monkey-bubble/player_1_right --type=string Right
    ;;

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

    ;;

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

exit 0

It should be set executable with chmod a+x debian/postinst.

9) A link to the desktop file must be added to /etc/others-menu/extra_applications to show it in menu. Link is created by making a 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 the file maemo-monkey-bubble.files to list all the files that will be installed by the package.

11) Add following lines in configure.in:

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

12) 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)

Now the source dir should be ready for packaging, described in Maemo SDK Tutorial [7].

If you want to create a package that is usable 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].

References

[1] Maemo SDK Tutorial 2nd chapter

[2] Maemo SDK Tutorial 3rd chapter

[3] http://home.gna.org/monkeybubble/

[4] http://glade.gnome.org/

[5] http://autotoolset.sourceforge.net/tutorial.html

[6] http://www.debian.org/doc/manuals/maint-guide/

[7] Maemo SDK Tutorial chapter 4 #BuildingApps

[8] Debian packages

[9] Localization HOWTO

[10] Maemo repository for libglade2

[11] garage.maemo.org

[12] Maemo desktop plug-ins tutorial

[13] Maemo Connectivity Architecture



Improve this page