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:
- Porting to maemo 3.x from maemo 2.x.
- 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.
- 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.
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.
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.
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:
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:
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.
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.
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
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].
References
[1] Maemo SDK Tutorial 2nd chapter
[2] Maemo SDK Tutorial 3rd chapter
[3] http://home.gna.org/monkeybubble/
[5] http://autotoolset.sourceforge.net/tutorial.html
[6] http://www.debian.org/doc/manuals/maint-guide/
[7] Maemo SDK Tutorial chapter 4
[8] Debian packages
[10] Maemo repository for libglade2
[11] garage.maemo.org
Improve this page