Documentation/Maemo 5 Developer Guide/Porting Software/Porting Existing GTK+ Application to Maemo 5

(Mention hildon_gtk_window_enable_zoom_keys())
(Portrait Mode)
 
(5 intermediate revisions not shown)
Line 1: Line 1:
-
= Porting Existing GTK+ Application to Maemo 5.0 =
+
This section describes key aspects of the process of porting an application to the Maemo platform. When starting to port an application to Maemo platform, the first step is to set up the development environment. The actual porting after that is described in this section.
-
 
+
-
This section describes key aspects of the process of porting an application to the maemo platform. When starting to port an application to maemo platform, the first step is to set up the development environment. The actual porting after that is described in this section.
+
== Introduction ==
== Introduction ==
-
Application that is used as an example for porting is [http://wingtk.sourceforge.net/ishan/sliders.html Sliders], a GTK+ based game.
+
Application that is used as an example for porting is [http://wingtk.sourceforge.net/ishan/sliders.html Sliders], a GTK+-based game.
The Sliders interface consists of the main window with board, menu and a couple of dialogs. See source mentioned above for game history and description.
The Sliders interface consists of the main window with board, menu and a couple of dialogs. See source mentioned above for game history and description.
Line 11: Line 9:
== Autotools Usage ==
== Autotools Usage ==
-
Sliders doesn't use GNU autotools originally, so we need to add `configure.ac', `Makefile.am' and `autogen.sh' for simplicity. See [http://wiki.maemo.org/Documentation/Maemo_5_Developer_Guide/GNU_Build_System GNU Build System] for corresponding information.
+
Sliders does not use GNU autotools originally, so we need to add <code>configure.ac</code>, <code>Makefile.am</code> and <code>autogen.sh</code> for simplicity. See [[Documentation/Maemo 5 Developer Guide/GNU Build System|GNU Build System]] for corresponding information.
-
== User Interface Changes ==
+
== User Interface Changes ==
Sliders game is not designed for usage in mobile devices, so we need to hildonize it in order to build effective touch interface for this simple game.
Sliders game is not designed for usage in mobile devices, so we need to hildonize it in order to build effective touch interface for this simple game.
-
=== Hildonizing Main View ===
+
=== Hildonizing Main View ===
Before using Hildon we need to initialize it. maemo-sliders/sliders.c
Before using Hildon we need to initialize it. maemo-sliders/sliders.c
-
<tt>  <span>''<span><font color="#9A1900">/* Initialize the GTK+ and hildon libraries */</font></span>''</span>
+
<source lang="c">
-
  <span>'''<span><font color="#000000">hildon_gtk_init</font></span>'''</span> <span><font color="#990000">(&amp;</font></span>argc<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>argv<span><font color="#990000">);</font></span></tt>
+
/* Initialize the GTK+ and hildon libraries */
 +
hildon_gtk_init (&argc, &argv);
 +
</source>
-
After that we can create main window with all necessary content. maemo-sliders/sliders.c
+
After that we can create a main window with all necessary content. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000000">create_main_window</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">)</font></span>
+
static GtkWidget*
-
<span><font color="#FF0000">{</font></span>
+
create_main_window (void)
-
  <span><font color="#008080">GtkWidget</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>main_window<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
{
-
  <span>''<span><font color="#9A1900">/* Create the main window */</font></span>''</span>
+
  GtkWidget *main_window;
-
  main_window <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_stackable_window_new</font></span>'''</span> <span><font color="#990000">();</font></span>
+
  /* Create the main window */
-
  <span>'''<span><font color="#000000">gtk_window_set_title</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WINDOW</font></span>'''</span> <span><font color="#990000">(</font></span>main_window<span><font color="#990000">),</font></span> <span><font color="#FF0000">"Welcome to Sliders"</font></span><span><font color="#990000">);</font></span>
+
  main_window = hildon_stackable_window_new ();
-
  <span>''<span><font color="#9A1900">/* Create and set application menu */</font></span>''</span>
+
  gtk_window_set_title (GTK_WINDOW (main_window), "Welcome to Sliders");
-
  <span><font color="#008080">HildonAppMenu</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>menu <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">create_menu</font></span>'''</span> <span><font color="#990000">();</font></span>
+
  /* Create and set application menu */
-
  <span>'''<span><font color="#000000">hildon_window_set_app_menu</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">HILDON_WINDOW</font></span>'''</span> <span><font color="#990000">(</font></span>main_window<span><font color="#990000">),</font></span> menu<span><font color="#990000">);</font></span>
+
  HildonAppMenu *menu = create_menu ();
-
  <span>''<span><font color="#9A1900">/* Create and pack table, that contains sliders */</font></span>''</span>
+
  hildon_window_set_app_menu (HILDON_WINDOW (main_window), menu);
-
  appdata<span><font color="#990000">.</font></span>table <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">create_table</font></span>'''</span> <span><font color="#990000">();</font></span>
+
  /* Create and pack table, that contains sliders */
-
  <span>'''<span><font color="#000000">gtk_container_add</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_CONTAINER</font></span>'''</span> <span><font color="#990000">(</font></span>main_window<span><font color="#990000">),</font></span> appdata<span><font color="#990000">.</font></span>table<span><font color="#990000">);</font></span>
+
  appdata.table = create_table ();
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> main_window<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  gtk_container_add (GTK_CONTAINER (main_window), appdata.table);
-
<span><font color="#FF0000">}</font></span>
+
  return main_window;
-
</tt>
+
}
 +
</source>
-
We have replaced original Sliders menu and toolbar with hildon application menu.
+
We have replaced the original Sliders menu and toolbar with a Hildon application menu.
 +
 
 +
[[Image:maemo-sliders_app_menu.png|frame|center|alt=Screenshot of menu|Maemo Sliders menu]]
-
{| summary="Maemo Sliders Menu"
 
-
|+ align="BOTTOM" |'''Figure 16.12:''' Maemo Sliders Menu
 
-
|-
 
-
|
 
-
[[Image:maemo-sliders_app_menu.png|400px]]
 
-
|}
 
maemo-sliders/sliders.c
maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> HildonAppMenu <span><font color="#990000"><nowiki>*</nowiki></font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000000">create_menu</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">)</font></span>
+
static HildonAppMenu *
-
<span><font color="#FF0000">{</font></span>
+
create_menu (void)
-
  <span>''<span><font color="#9A1900">/*</font></span>''</span>
+
{
-
<span>''<span><font color="#9A1900">  * Create menu buttons one by one, connect to appropriate callbacks and</font></span>''</span>
+
  /*
-
<span>''<span><font color="#9A1900">  * add to the menu.</font></span>''</span>
+
  * Create menu buttons one by one, connect to appropriate callbacks and
-
<span>''<span><font color="#9A1900">  */</font></span>''</span>
+
  * add to the menu.
-
  <span><font color="#008080">HildonSizeType</font></span> button_size <span><font color="#990000"><nowiki>=</nowiki></font></span> HILDON_SIZE_FINGER_HEIGHT <span><font color="#990000"><nowiki>|</nowiki></font></span> HILDON_SIZE_AUTO_WIDTH<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  */
-
  <span><font color="#008080">HildonAppMenu</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>menu <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">HILDON_APP_MENU</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">hildon_app_menu_new</font></span>'''</span> <span><font color="#990000">());</font></span>
+
  HildonSizeType button_size = HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH;
-
  <span><font color="#008080">GtkWidget</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>button<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  HildonAppMenu *menu = HILDON_APP_MENU (hildon_app_menu_new ());
-
  button <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_gtk_button_new</font></span>'''</span> <span><font color="#990000">(</font></span>button_size<span><font color="#990000">);</font></span>
+
  GtkWidget *button;
-
  <span>'''<span><font color="#000000">gtk_button_set_label</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"New"</font></span><span><font color="#990000">);</font></span>
+
  button = hildon_gtk_button_new (button_size);
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"clicked"</font></span><span><font color="#990000">,</font></span>
+
  gtk_button_set_label (GTK_BUTTON (button), "New");
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>on_new_clicked<span><font color="#990000">),</font></span> NULL<span><font color="#990000">);</font></span>
+
  g_signal_connect (G_OBJECT (button), "clicked",
-
  <span>'''<span><font color="#000000">hildon_app_menu_append</font></span>'''</span> <span><font color="#990000">(</font></span>menu<span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">));</font></span>
+
                    G_CALLBACK (on_new_clicked), NULL);
-
  button <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_gtk_button_new</font></span>'''</span> <span><font color="#990000">(</font></span>button_size<span><font color="#990000">);</font></span>
+
  hildon_app_menu_append (menu, GTK_BUTTON (button));
-
  <span>'''<span><font color="#000000">gtk_button_set_label</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"Scores"</font></span><span><font color="#990000">);</font></span>
+
  button = hildon_gtk_button_new (button_size);
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"clicked"</font></span><span><font color="#990000">,</font></span>
+
  gtk_button_set_label (GTK_BUTTON (button), "Scores");
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>on_scores_clicked<span><font color="#990000">),</font></span> NULL<span><font color="#990000">);</font></span>
+
  g_signal_connect (G_OBJECT (button), "clicked",
-
  <span>'''<span><font color="#000000">hildon_app_menu_append</font></span>'''</span> <span><font color="#990000">(</font></span>menu<span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">));</font></span>
+
                    G_CALLBACK (on_scores_clicked), NULL);
-
  button <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_gtk_button_new</font></span>'''</span> <span><font color="#990000">(</font></span>button_size<span><font color="#990000">);</font></span>
+
  hildon_app_menu_append (menu, GTK_BUTTON (button));
-
  <span>'''<span><font color="#000000">gtk_button_set_label</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"Open"</font></span><span><font color="#990000">);</font></span>
+
  button = hildon_gtk_button_new (button_size);
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"clicked"</font></span><span><font color="#990000">,</font></span>
+
  gtk_button_set_label (GTK_BUTTON (button), "Open");
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>on_open_clicked<span><font color="#990000">),</font></span> NULL<span><font color="#990000">);</font></span>
+
  g_signal_connect (G_OBJECT (button), "clicked",
-
  <span>'''<span><font color="#000000">hildon_app_menu_append</font></span>'''</span> <span><font color="#990000">(</font></span>menu<span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">));</font></span>
+
                    G_CALLBACK (on_open_clicked), NULL);
-
  button <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_gtk_button_new</font></span>'''</span> <span><font color="#990000">(</font></span>button_size<span><font color="#990000">);</font></span>
+
  hildon_app_menu_append (menu, GTK_BUTTON (button));
-
  <span>'''<span><font color="#000000">gtk_button_set_label</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"Save"</font></span><span><font color="#990000">);</font></span>
+
  button = hildon_gtk_button_new (button_size);
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"clicked"</font></span><span><font color="#990000">,</font></span>
+
  gtk_button_set_label (GTK_BUTTON (button), "Save");
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>on_save_clicked<span><font color="#990000">),</font></span> NULL<span><font color="#990000">);</font></span>
+
  g_signal_connect (G_OBJECT (button), "clicked",
-
  <span>'''<span><font color="#000000">hildon_app_menu_append</font></span>'''</span> <span><font color="#990000">(</font></span>menu<span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">));</font></span>
+
                    G_CALLBACK (on_save_clicked), NULL);
-
  button <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_gtk_button_new</font></span>'''</span> <span><font color="#990000">(</font></span>button_size<span><font color="#990000">);</font></span>
+
  hildon_app_menu_append (menu, GTK_BUTTON (button));
-
  <span>'''<span><font color="#000000">gtk_button_set_label</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"About"</font></span><span><font color="#990000">);</font></span>
+
  button = hildon_gtk_button_new (button_size);
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">),</font></span> <span><font color="#FF0000">"clicked"</font></span><span><font color="#990000">,</font></span>
+
  gtk_button_set_label (GTK_BUTTON (button), "About");
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>on_about_clicked<span><font color="#990000">),</font></span> NULL<span><font color="#990000">);</font></span>
+
  g_signal_connect (G_OBJECT (button), "clicked",
-
  <span>'''<span><font color="#000000">hildon_app_menu_append</font></span>'''</span> <span><font color="#990000">(</font></span>menu<span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span> <span><font color="#990000">(</font></span>button<span><font color="#990000">));</font></span>
+
                    G_CALLBACK (on_about_clicked), NULL);
-
  <span>'''<span><font color="#000000">gtk_widget_show_all</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WIDGET</font></span>'''</span> <span><font color="#990000">(</font></span>menu<span><font color="#990000">));</font></span>
+
  hildon_app_menu_append (menu, GTK_BUTTON (button));
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> menu<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  gtk_widget_show_all (GTK_WIDGET (menu));
-
<span><font color="#FF0000">}</font></span></tt>
+
  return menu;
 +
}
 +
</source>
 +
Sliders table buttons (see figure 16.13) are maximized and wrapped with <code>hildon_gtk_button_new ()</code>. maemo-sliders/sliders.c
-
Sliders table buttons (see figure 16.13) are maximized and wrapped with 'hildon_gtk_button_new ()'. maemo-sliders/sliders.c
+
<source lang="c">
 +
static GtkWidget*
 +
create_table (void)
 +
{
 +
  GtkWidget *table;
 +
  gint x, y, c = 0;
 +
  appdata.vacant_pos = SLIDERS_NUMBER;
 +
  appdata.move_no = 0;
 +
  HildonSizeType button_size = HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH;
 +
  /* Create table for sliders */
 +
  table = gtk_table_new (4, 4, FALSE);
 +
  /*
 +
  * Create buttons, that represents sliders. Set "current_pos" key for each button
 +
  * and connect each button to appropriate callback.
 +
  */
 +
  for (x = 0; x < SLIDERS_NUMBER; x++)
 +
  {
 +
    gchar *temp = g_strdup_printf ("%i", x+1);
 +
    appdata.buttons[x] = hildon_gtk_button_new (button_size);
 +
    gtk_button_set_label (GTK_BUTTON(appdata.buttons[x]), temp);
 +
    g_object_set_data (G_OBJECT(appdata.buttons[x]), "current_pos", (gpointer) x);
 +
    g_free (temp);
 +
    gtk_signal_connect(GTK_OBJECT(appdata.buttons[x]), "clicked", GTK_SIGNAL_FUNC(on_button_clicked), NULL);
 +
  }
 +
  /* Add sliders to table */
 +
  for (x = 0; x < 4; x++)
 +
  for (y = 0; y < 4; y++)
 +
    if (!(x == 3 && y == 3))
 +
      gtk_table_attach_defaults (GTK_TABLE (table), appdata.buttons[c++], y, y+1, x, x+1);
 +
  return table;
 +
}
 +
</source>
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span>
+
[[Image:maemo-sliders_table.png|frame|center|Screenshot of sliders table|Maemo Sliders table]]
-
<span>'''<span><font color="#000000">create_table</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">)</font></span>
+
-
<span><font color="#FF0000">{</font></span>
+
-
  GtkWidget <span><font color="#990000"><nowiki>*</nowiki></font></span>table<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gint x<span><font color="#990000">,</font></span> y<span><font color="#990000">,</font></span> c <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  appdata<span><font color="#990000">.</font></span>vacant_pos <span><font color="#990000"><nowiki>=</nowiki></font></span> SLIDERS_NUMBER<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  appdata<span><font color="#990000">.</font></span>move_no <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  HildonSizeType button_size <span><font color="#990000"><nowiki>=</nowiki></font></span> HILDON_SIZE_FINGER_HEIGHT <span><font color="#990000"><nowiki>|</nowiki></font></span> HILDON_SIZE_AUTO_WIDTH<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* Create table for sliders */</font></span>''</span>
+
-
  table <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_table_new</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#993399">4</font></span><span><font color="#990000">,</font></span> <span><font color="#993399">4</font></span><span><font color="#990000">,</font></span> FALSE<span><font color="#990000">);</font></span>
+
-
  <span>''<span><font color="#9A1900">/*</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">  * Create buttons, that represents sliders. Set "current_pos" key for each button</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">  * and connect each button to appropriate callback.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">  */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">for</font></span>'''</span> <span><font color="#990000">(</font></span>x <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> x <span><font color="#990000">&lt;</font></span> SLIDERS_NUMBER<span><font color="#990000"><nowiki>;</nowiki></font></span> x<span><font color="#990000">++)</font></span>
+
-
  <span><font color="#FF0000">{</font></span>
+
-
    gchar <span><font color="#990000"><nowiki>*</nowiki></font></span>temp <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_strdup_printf</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%i"</font></span><span><font color="#990000">,</font></span> x<span><font color="#990000">+</font></span><span><font color="#993399">1</font></span><span><font color="#990000">);</font></span>
+
-
    appdata<span><font color="#990000">.</font></span>buttons<span><font color="#990000">[</font></span>x<span><font color="#990000">]</font></span> <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_gtk_button_new</font></span>'''</span> <span><font color="#990000">(</font></span>button_size<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">gtk_button_set_label</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BUTTON</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>buttons<span><font color="#990000">[</font></span>x<span><font color="#990000">]),</font></span> temp<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">g_object_set_data</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>buttons<span><font color="#990000">[</font></span>x<span><font color="#990000">]),</font></span> <span><font color="#FF0000">"current_pos"</font></span><span><font color="#990000">,</font></span> <span><font color="#990000">(</font></span>gpointer<span><font color="#990000">)</font></span> x<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">g_free</font></span>'''</span> <span><font color="#990000">(</font></span>temp<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">gtk_signal_connect</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>buttons<span><font color="#990000">[</font></span>x<span><font color="#990000">]),</font></span> <span><font color="#FF0000">"clicked"</font></span><span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">GTK_SIGNAL_FUNC</font></span>'''</span><span><font color="#990000">(</font></span>on_button_clicked<span><font color="#990000">),</font></span> NULL<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Add sliders to table */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">for</font></span>'''</span> <span><font color="#990000">(</font></span>x <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> x <span><font color="#990000">&lt;</font></span> <span><font color="#993399">4</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> x<span><font color="#990000">++)</font></span>
+
-
  <span>'''<span><font color="#0000FF">for</font></span>'''</span> <span><font color="#990000">(</font></span>y <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> y <span><font color="#990000">&lt;</font></span> <span><font color="#993399">4</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> y<span><font color="#990000">++)</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!(</font></span>x <span><font color="#990000"><nowiki>==</nowiki></font></span> <span><font color="#993399">3</font></span> <span><font color="#990000">&amp;&amp;</font></span> y <span><font color="#990000"><nowiki>==</nowiki></font></span> <span><font color="#993399">3</font></span><span><font color="#990000">))</font></span>
+
-
      <span>'''<span><font color="#000000">gtk_table_attach_defaults</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_TABLE</font></span>'''</span> <span><font color="#990000">(</font></span>table<span><font color="#990000">),</font></span> appdata<span><font color="#990000">.</font></span>buttons<span><font color="#990000">[</font></span>c<span><font color="#990000">++],</font></span> y<span><font color="#990000">,</font></span> y<span><font color="#990000">+</font></span><span><font color="#993399">1</font></span><span><font color="#990000">,</font></span> x<span><font color="#990000">,</font></span> x<span><font color="#990000">+</font></span><span><font color="#993399">1</font></span><span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> table<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span></tt>
+
-
 
+
-
 
+
-
 
+
-
{| summary="Maemo Sliders Table"
+
-
|+ align="BOTTOM" |'''Figure 16.13:''' Maemo Sliders Table
+
-
|-
+
-
|
+
-
[[Image:maemo-sliders_table.png|400px]]
+
-
|}
+
===  Implementing Scores Window ===
===  Implementing Scores Window ===
Line 140: Line 133:
We have replaced original Sliders win picture by scores window (figure 16.14), that appears in two cases. It's shown after "Scores" menu button clicked maemo-sliders/sliders.c
We have replaced original Sliders win picture by scores window (figure 16.14), that appears in two cases. It's shown after "Scores" menu button clicked maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000000">on_scores_clicked</font></span>'''</span> <span><font color="#990000">(</font></span>GtkMenuItem <span><font color="#990000"><nowiki>*</nowiki></font></span>menuitem<span><font color="#990000">,</font></span> gpointer user_data<span><font color="#990000">)</font></span>
+
static void
-
<span><font color="#FF0000">{</font></span>
+
on_scores_clicked (GtkMenuItem *menuitem, gpointer user_data)
-
  <span>'''<span><font color="#000000">scores_window</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#993399">0</font></span><span><font color="#990000">);</font></span>
+
{
-
<span><font color="#FF0000">}</font></span></tt>
+
  scores_window (0);
 +
}
 +
</source>
and when player has won. maemo-sliders/sliders.c
and when player has won. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000000">on_button_clicked</font></span>'''</span> <span><font color="#990000">(</font></span>GtkWidget <span><font color="#990000"><nowiki>*</nowiki></font></span>button<span><font color="#990000">,</font></span> gpointer user_data<span><font color="#990000">)</font></span>
+
static void
-
<span><font color="#FF0000">{</font></span>
+
on_button_clicked (GtkWidget *button, gpointer user_data)
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
{
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span> <span>'''<span><font color="#000000">has_player_won</font></span>'''</span><span><font color="#990000">()</font></span> <span><font color="#990000">)</font></span>
+
  /* ... */
-
    <span><font color="#FF0000">{</font></span>
+
    if ( has_player_won() )
-
      <span>'''<span><font color="#000000">scores_window</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#993399">1</font></span><span><font color="#990000">);</font></span>
+
    {
-
      <span>'''<span><font color="#000000">gtk_window_set_title</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WINDOW</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>main_window<span><font color="#990000">),</font></span> <span><font color="#FF0000">"Welcome to Sliders"</font></span><span><font color="#990000">);</font></span>
+
      scores_window (1);
-
      appdata<span><font color="#990000">.</font></span>move_no <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
      gtk_window_set_title (GTK_WINDOW (appdata.main_window), "Welcome to Sliders");
-
    <span><font color="#FF0000">}</font></span>
+
      appdata.move_no = 0;
-
    <span>'''<span><font color="#0000FF">else</font></span>'''</span>
+
    }
-
    <span><font color="#FF0000">{</font></span>
+
    else
-
      gchar <span><font color="#990000"><nowiki>*</nowiki></font></span>temp <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_strdup_printf</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"Move no.: %i"</font></span><span><font color="#990000">,</font></span> appdata<span><font color="#990000">.</font></span>move_no<span><font color="#990000">);</font></span>
+
    {
-
      <span>'''<span><font color="#000000">gtk_window_set_title</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WINDOW</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>main_window<span><font color="#990000">),</font></span> temp<span><font color="#990000">);</font></span>
+
      gchar *temp = g_strdup_printf ("Move no.: %i", appdata.move_no);
-
      <span>'''<span><font color="#000000">g_free</font></span>'''</span> <span><font color="#990000">(</font></span>temp<span><font color="#990000">);</font></span>
+
      gtk_window_set_title (GTK_WINDOW (appdata.main_window), temp);
-
    <span><font color="#FF0000">}</font></span>
+
      g_free (temp);
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
    }
-
<span><font color="#FF0000">}</font></span></tt>
+
  /* ... */
-
 
+
}
-
 
+
</source>
-
 
+
-
{| summary="Maemo Sliders Scores"
+
-
|+ align="BOTTOM" |'''Figure 16.14:''' Maemo Sliders Scores
+
-
|-
+
-
|
+
-
[[Image:maemo-sliders_scores_window.png|400px]]
+
-
|}
+
-
 
+
 +
[[Image:maemo-sliders_scores_window.png|frame|center|alt=Screenshot of scores window|Maemo sliders scores]]
Scores window contains pannable area with tree view maemo-sliders/sliders.c
Scores window contains pannable area with tree view maemo-sliders/sliders.c
-
<tt>  <span>''<span><font color="#9A1900">/* Create a tree view that represents scores table and an area for it */</font></span>''</span>
+
<source lang="c">
-
  area <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_pannable_area_new</font></span>'''</span> <span><font color="#990000">();</font></span>
+
/* Create a tree view that represents scores table and an area for it */
-
  tree_view <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">create_treeview</font></span>'''</span> <span><font color="#990000">(</font></span>HILDON_UI_MODE_EDIT<span><font color="#990000">);</font></span>
+
area = hildon_pannable_area_new ();
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
tree_view = create_treeview (HILDON_UI_MODE_EDIT);
-
  <span>''<span><font color="#9A1900">/* Pack tree view */</font></span>''</span>
+
/* ... */
-
  <span>'''<span><font color="#000000">gtk_container_add</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_CONTAINER</font></span>'''</span> <span><font color="#990000">(</font></span>area<span><font color="#990000">),</font></span> tree_view<span><font color="#990000">);</font></span>
+
/* Pack tree view */
-
  <span>'''<span><font color="#000000">gtk_container_add</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_CONTAINER</font></span>'''</span> <span><font color="#990000">(</font></span>window<span><font color="#990000">),</font></span> area<span><font color="#990000">);</font></span></tt>
+
gtk_container_add (GTK_CONTAINER (area), tree_view);
 +
gtk_container_add (GTK_CONTAINER (window), area);
 +
</source>
and edit toolbar. maemo-sliders/sliders.c
and edit toolbar. maemo-sliders/sliders.c
-
<tt>  <span>''<span><font color="#9A1900">/* Create a new edit toolbar */</font></span>''</span>
+
<source lang="c">
-
  toolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_edit_toolbar_new_with_text</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"Choose scores to delete"</font></span><span><font color="#990000">,</font></span>
+
/* Create a new edit toolbar */
-
                                                <span><font color="#FF0000">"Delete"</font></span><span><font color="#990000">);</font></span>
+
toolbar = hildon_edit_toolbar_new_with_text ("Choose scores to delete",
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
                                            "Delete");
-
  <span>''<span><font color="#9A1900">/* Add toolbar to the window */</font></span>''</span>
+
/* ... */
-
  <span>'''<span><font color="#000000">hildon_window_set_edit_toolbar</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">HILDON_WINDOW</font></span>'''</span> <span><font color="#990000">(</font></span>window<span><font color="#990000">),</font></span>
+
/* Add toolbar to the window */
-
                                  <span>'''<span><font color="#000000">HILDON_EDIT_TOOLBAR</font></span>'''</span> <span><font color="#990000">(</font></span>toolbar<span><font color="#990000">));</font></span>
+
hildon_window_set_edit_toolbar (HILDON_WINDOW (window),
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
                                HILDON_EDIT_TOOLBAR (toolbar));
-
  <span>''<span><font color="#9A1900">/* Set callback for "Delete" button */</font></span>''</span>
+
/* ... */
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span> <span><font color="#990000">(</font></span>toolbar<span><font color="#990000">,</font></span> <span><font color="#FF0000">"button-clicked"</font></span><span><font color="#990000">,</font></span>
+
/* Set callback for "Delete" button */
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>delete_selected_scores<span><font color="#990000">),</font></span>
+
g_signal_connect (toolbar, "button-clicked",
-
                    tree_view<span><font color="#990000">);</font></span>
+
                  G_CALLBACK (delete_selected_scores),
-
  <span>''<span><font color="#9A1900">/* Destroy scores window when upper corner arrow clicked */</font></span>''</span>
+
                  tree_view);
-
  <span>'''<span><font color="#000000">g_signal_connect_swapped</font></span>'''</span> <span><font color="#990000">(</font></span>toolbar<span><font color="#990000">,</font></span> <span><font color="#FF0000">"arrow-clicked"</font></span><span><font color="#990000">,</font></span>
+
/* Destroy scores window when upper corner arrow clicked */
-
                            <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span> <span><font color="#990000">(</font></span>gtk_widget_destroy<span><font color="#990000">),</font></span>
+
g_signal_connect_swapped (toolbar, "arrow-clicked",
-
                            window<span><font color="#990000">);</font></span></tt>
+
                          G_CALLBACK (gtk_widget_destroy),
 +
                          window);
 +
</source>
-
Win banner is shown if 'scores_window ()' called in case player has won. maemo-sliders/sliders.c
+
Win banner is shown if <code>scores_window ()</code> called in case player has won. maemo-sliders/sliders.c
-
<tt>  <span>''<span><font color="#9A1900">/* Additional actions for case, when player has won */</font></span>''</span>
+
<source lang="c">
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>win<span><font color="#990000">)</font></span>
+
/* Additional actions for case, when player has won */
-
  <span><font color="#FF0000">{</font></span>
+
if (win)
-
    <span>''<span><font color="#9A1900">/* Show appropriate banner */</font></span>''</span>
+
{
-
    <span>'''<span><font color="#000000">hildon_banner_show_informationf</font></span>'''</span> <span><font color="#990000">(</font></span>window<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> <span><font color="#FF0000">"Win! Score: %i"</font></span><span><font color="#990000">,</font></span> appdata<span><font color="#990000">.</font></span>move_no<span><font color="#990000">);</font></span>
+
  /* Show appropriate banner */
-
    <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
  hildon_banner_show_informationf (window, NULL, "Win! Score: %i", appdata.move_no);
-
  <span><font color="#FF0000">}</font></span></tt>
+
  /* ... */
 +
}
 +
</source>
-
Other scores related code is quite straightforward and doesn't use Hildon specific features, see 'scores_window ()' implementation for details.
+
Other scores related code is quite straightforward and does not use Hildon-specific features, see the <code>scores_window ()</code> implementation for details.
== State Saving ==
== State Saving ==
Line 223: Line 217:
Maemo supports state saving feature, that we have used for current Sliders game saving. The application can later open this game with the same state as before. This section describes most important steps needed to make Sliders support state savings.
Maemo supports state saving feature, that we have used for current Sliders game saving. The application can later open this game with the same state as before. This section describes most important steps needed to make Sliders support state savings.
-
<nowiki> 'state_save ()' is called when user presses menu's "Save" button. Saving data should be updated before it's called. </nowiki>maemo-sliders/sliders.c
+
<code>state_save ()</code> is called when the user presses the "Save" menu button. Saving data should be updated before it is called. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000000">on_save_clicked</font></span>'''</span> <span><font color="#990000">(</font></span>GtkMenuItem <span><font color="#990000"><nowiki>*</nowiki></font></span>menuitem<span><font color="#990000">,</font></span> gpointer user_data<span><font color="#990000">)</font></span>
+
static void
-
<span><font color="#FF0000">{</font></span>
+
on_save_clicked (GtkMenuItem *menuitem, gpointer user_data)
-
  <span>''<span><font color="#9A1900">/* Save moves number, vacant position and board layout to "state" */</font></span>''</span>
+
{
-
  state<span><font color="#990000">.</font></span>move_no <span><font color="#990000"><nowiki>=</nowiki></font></span> appdata<span><font color="#990000">.</font></span>move_no<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  /* Save moves number, vacant position and board layout to "state" */
-
  state<span><font color="#990000">.</font></span>vacant_pos <span><font color="#990000"><nowiki>=</nowiki></font></span> appdata<span><font color="#990000">.</font></span>vacant_pos<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  state.move_no = appdata.move_no;
-
  <span><font color="#009900">int</font></span> i<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  state.vacant_pos = appdata.vacant_pos;
-
  <span>'''<span><font color="#0000FF">for</font></span>'''</span> <span><font color="#990000">(</font></span>i <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span> i <span><font color="#990000">&lt;</font></span> SLIDERS_NUMBER<span><font color="#990000"><nowiki>;</nowiki></font></span> i<span><font color="#990000">++)</font></span>
+
  int i;
-
    state<span><font color="#990000">.</font></span>map<span><font color="#990000">[</font></span>i<span><font color="#990000">]</font></span> <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">(</font></span>gint<span><font color="#990000">)</font></span> <span>'''<span><font color="#000000">gtk_object_get_data</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>buttons<span><font color="#990000">[</font></span>i<span><font color="#990000">]),</font></span> <span><font color="#FF0000">"current_pos"</font></span><span><font color="#990000">);</font></span>
+
  for (i = 0; i < SLIDERS_NUMBER; i++)
-
  <span>''<span><font color="#9A1900">/* Call state saving method and process return value */</font></span>''</span>
+
    state.map[i] = (gint) gtk_object_get_data(GTK_OBJECT(appdata.buttons[i]), "current_pos");
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">state_save</font></span>'''</span> <span><font color="#990000">())</font></span>
+
  /* Call state saving method and process return value */
-
    <span>'''<span><font color="#000000">hildon_banner_show_information</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>main_window<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> <span><font color="#FF0000">"Saved current game"</font></span><span><font color="#990000">);</font></span>
+
  if (state_save ())
-
  <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span>'''<span><font color="#000000">hildon_banner_show_information</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>main_window<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> <span><font color="#FF0000">"Can't save current game"</font></span><span><font color="#990000">);</font></span>
+
    hildon_banner_show_information (appdata.main_window, NULL, "Saved current game");
-
<span><font color="#FF0000">}</font></span></tt>
+
  else hildon_banner_show_information (appdata.main_window, NULL, "Can't save current game");
 +
}
 +
</source>
-
Then 'osso_state_write ()' can be called for correct data. maemo-sliders/sliders.c
+
Then <code>osso_state_write ()</code> can be called for correct data. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#000000">state_save</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">)</font></span>
+
<source lang="c">
-
<span><font color="#FF0000">{</font></span>
+
state_save(void)
-
  osso_state_t osso_state<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
{
-
  osso_return_t ret<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  osso_state_t osso_state;
-
  osso_state<span><font color="#990000">.</font></span>state_size <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#0000FF">sizeof</font></span>'''</span><span><font color="#990000">(</font></span>StateData<span><font color="#990000">);</font></span>
+
  osso_return_t ret;
-
  osso_state<span><font color="#990000">.</font></span>state_data <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">&amp;</font></span>state<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  osso_state.state_size = sizeof(StateData);
-
  ret <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">osso_state_write</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>osso_context<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>osso_state<span><font color="#990000">);</font></span>
+
  osso_state.state_data = &state;
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>ret <span><font color="#990000"><nowiki>!=</nowiki></font></span> OSSO_OK<span><font color="#990000">)</font></span>
+
  ret = osso_state_write(appdata.osso_context, &osso_state);
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> FALSE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  if (ret != OSSO_OK)
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
    return FALSE;
-
<span><font color="#FF0000">}</font></span></tt>
+
  return TRUE;
 +
}
 +
</source>
-
<nowiki> 'state_load ()' is called when user clicks "Open" menu button. It uses 'osso_state_read ()' symmetric to `state_save ()'. </nowiki>maemo-sliders/sliders.c
+
<code>state_load ()</code> is called when user clicks the "Open" menu button. It uses <code>osso_state_read ()</code> symmetric to <code>state_save ()</code>. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#000000">state_load</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">)</font></span>
+
<source lang="c">
-
<span><font color="#FF0000">{</font></span>
+
state_load(void)
-
  osso_state_t osso_state<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
{
-
  osso_return_t ret<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  osso_state_t osso_state;
-
  osso_state<span><font color="#990000">.</font></span>state_size <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#0000FF">sizeof</font></span>'''</span><span><font color="#990000">(</font></span>StateData<span><font color="#990000">);</font></span>
+
  osso_return_t ret;
-
  osso_state<span><font color="#990000">.</font></span>state_data <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">&amp;</font></span>state<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  osso_state.state_size = sizeof(StateData);
-
  ret <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">osso_state_read</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>osso_context<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>osso_state<span><font color="#990000">);</font></span>
+
  osso_state.state_data = &state;
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>ret <span><font color="#990000"><nowiki>!=</nowiki></font></span> OSSO_OK<span><font color="#990000">)</font></span>
+
  ret = osso_state_read(appdata.osso_context, &osso_state);
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> FALSE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  if (ret != OSSO_OK)
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
    return FALSE;
-
<span><font color="#FF0000">}</font></span></tt>
+
  return TRUE;
 +
}
 +
</source>
-
Don't forget to initialize the library context before state saving usage. maemo-sliders/sliders.c
+
Do not forget to initialize the library context before state-saving usage. maemo-sliders/sliders.c
-
<tt>  <span>''<span><font color="#9A1900">/* Initialize Libosso */</font></span>''</span>
+
<source lang="c">
-
  appdata<span><font color="#990000">.</font></span>osso_context <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">osso_initialize</font></span>'''</span><span><font color="#990000">(</font></span>SL_SERVICE<span><font color="#990000">,</font></span> VERSION<span><font color="#990000">,</font></span> TRUE<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
/* Initialize Libosso */
-
</tt>
+
appdata.osso_context = osso_initialize(SL_SERVICE, VERSION, TRUE, NULL);
 +
</source>
-
The information saved by using state saving functions does not survive over power off of the device, so if you need to save some data more permanent you could use e.g. [http://xmlsoft.org/ libxml] as shown in next section for scores table.
+
The information saved by using state saving functions does not survive over power off of the device, so if you need to save some data more permanently you could use e.g. [http://xmlsoft.org/ libxml] as shown in the next section for the scores table.
-
== Scores Saving ==
+
== Scores Saving ==
-
Scores data store is created, when 'get_scores_store ()' called first. The store is populated from file saved under user's home directory. Document tree is parsed with node pointer starting from root element. maemo-sliders/sliders.c
+
Scores data store is created, when <code>get_scores_store ()</code> called first. The store is populated from a file saved under the user's home directory. The document tree is parsed with node pointer starting from root element. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> GtkListStore <span><font color="#990000"><nowiki>*</nowiki></font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000000">get_scores_store</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#009900">void</font></span><span><font color="#990000">)</font></span>
+
static GtkListStore *
-
<span><font color="#FF0000">{</font></span>
+
get_scores_store (void)
-
  <span>'''<span><font color="#0000FF">static</font></span>'''</span> GtkListStore <span><font color="#990000"><nowiki>*</nowiki></font></span>store <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
{
-
+
  static GtkListStore *store = NULL;
-
  <span>''<span><font color="#9A1900">/* Return store if it has been created before */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>store <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span>
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> store<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Create store with 2 columns of specified types */</font></span>''</span>
+
-
  store <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_list_store_new</font></span>'''</span> <span><font color="#990000">(</font></span>N_COLUMNS<span><font color="#990000">,</font></span> G_TYPE_STRING<span><font color="#990000">,</font></span> G_TYPE_INT<span><font color="#990000">);</font></span>
+
-
+
-
  xmlDocPtr doc<span><font color="#990000"><nowiki>;</nowiki></font></span> <span>''<span><font color="#9A1900">/* the resulting document tree */</font></span>''</span>
+
-
  xmlNodePtr cur<span><font color="#990000"><nowiki>;</nowiki></font></span> <span>''<span><font color="#9A1900">/* node pointer */</font></span>''</span>
+
-
  xmlChar <span><font color="#990000"><nowiki>*</nowiki></font></span>attribute<span><font color="#990000"><nowiki>;</nowiki></font></span> <span>''<span><font color="#9A1900">/* node attribute name */</font></span>''</span>
+
-
  <span><font color="#009900">char</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>row_name<span><font color="#990000">,</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>temp<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gint row_score<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Parse an XML file from the filesystem */</font></span>''</span>
+
-
  <span><font color="#009900">char</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>slidersdir <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_strdup_printf</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s%s%s"</font></span><span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">getenv</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"HOME"</font></span><span><font color="#990000">),</font></span> SLIDERS_DIR<span><font color="#990000">,</font></span> SCORES_XML<span><font color="#990000">);</font></span>
+
-
  doc <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">xmlReadFile</font></span>'''</span> <span><font color="#990000">(</font></span>slidersdir<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> XML_PARSE_NOERROR<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">g_free</font></span>'''</span> <span><font color="#990000">(</font></span>slidersdir<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>doc <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: Failed to parse document %s</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">,</font></span> SCORES_XML<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> store<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Parse root element */</font></span>''</span>
+
-
  cur <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">xmlDocGetRootElement</font></span>'''</span><span><font color="#990000">(</font></span>doc<span><font color="#990000">);</font></span> <span>''<span><font color="#9A1900">//scores</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>cur <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: Empty xml doc</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">xmlFreeDoc</font></span>'''</span> <span><font color="#990000">(</font></span>doc<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> store<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Parse first row tag */</font></span>''</span>
+
-
  cur <span><font color="#990000"><nowiki>=</nowiki></font></span> cur<span><font color="#990000">-&gt;</font></span>xmlChildrenNode<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>cur <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: No row tag</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">xmlFreeDoc</font></span>'''</span> <span><font color="#990000">(</font></span>doc<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> store<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Parse row tags one by one */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">while</font></span>'''</span> <span><font color="#990000">(</font></span>cur <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span>
+
-
  <span><font color="#FF0000">{</font></span>
+
-
    <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Add new row to the store */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">gtk_list_store_insert_with_values</font></span>'''</span> <span><font color="#990000">(</font></span>store<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> <span>'''<span><font color="#000000">gtk_tree_model_iter_n_children</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_TREE_MODEL</font></span>'''</span><span><font color="#990000">(</font></span>store<span><font color="#990000">),</font></span> NULL<span><font color="#990000">),</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> row_name<span><font color="#990000">,</font></span> <span><font color="#993399">1</font></span><span><font color="#990000">,</font></span> row_score<span><font color="#990000">,</font></span> <span><font color="#990000">-</font></span><span><font color="#993399">1</font></span><span><font color="#990000">);</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Parse next row element */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">g_free</font></span>'''</span> <span><font color="#990000">(</font></span>row_name<span><font color="#990000">);</font></span>
+
-
    cur <span><font color="#990000"><nowiki>=</nowiki></font></span> cur<span><font color="#990000">-&gt;</font></span>next<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Free the resulting tree */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">xmlFreeDoc</font></span>'''</span><span><font color="#990000">(</font></span>doc<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> store<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span></tt>
+
-
'scores_xml_save ()' is called before application quits. It uses xml text writer in order to save current data store to appropriate file. maemo-sliders/sliders.c
+
  /* Return store if it has been created before */
 +
  if (store != NULL)
 +
    return store;
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span>
+
  /* Create store with 2 columns of specified types */
-
<span>'''<span><font color="#000000">scores_xml_save</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#0000FF">const</font></span>'''</span> <span><font color="#009900">char</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>uri<span><font color="#990000">)</font></span>
+
  store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
-
<span><font color="#FF0000">{</font></span>
+
 
-
  <span><font color="#009900">int</font></span> rc<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  xmlDocPtr doc; /* the resulting document tree */
-
  xmlTextWriterPtr writer<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  xmlNodePtr cur; /* node pointer */
-
+
  xmlChar *attribute; /* node attribute name */
-
  <span>''<span><font color="#9A1900">/* Check if store have been created */</font></span>''</span>
+
  char *row_name, *temp;
-
  GtkListStore <span><font color="#990000"><nowiki>*</nowiki></font></span>store <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">get_scores_store</font></span>'''</span> <span><font color="#990000">();</font></span>
+
  gint row_score;
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span>store<span><font color="#990000">)</font></span> <span>'''<span><font color="#0000FF">return</font></span>'''</span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
 
-
+
  /* Parse an XML file from the filesystem */
-
  <span>''<span><font color="#9A1900">/* Create a new XmlWriter for uri, with no compression. */</font></span>''</span>
+
  char *slidersdir = g_strdup_printf ("%s%s%s", getenv("HOME"), SLIDERS_DIR, SCORES_XML);
-
  writer <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">xmlNewTextWriterFilename</font></span>'''</span><span><font color="#990000">(</font></span>uri<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">);</font></span>
+
  doc = xmlReadFile (slidersdir, NULL, XML_PARSE_NOERROR);
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>writer <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
  g_free (slidersdir);
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: Error creating the xml writer for %s</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">,</font></span> SCORES_XML<span><font color="#990000">);</font></span>
+
  if (doc == NULL) {
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
    g_print ("%s, %i: Failed to parse document %s\n", __PRETTY_FUNCTION__, __LINE__, SCORES_XML);
-
  <span><font color="#FF0000">}</font></span>
+
    return store;
-
+
  }
-
  <span>''<span><font color="#9A1900">/* Start the document with the xml default for the version,</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">  * encoding UTF-8 and the default for the standalone</font></span>''</span>
+
  /* Parse root element */
-
<span>''<span><font color="#9A1900">   * declaration. */</font></span>''</span>
+
   cur = xmlDocGetRootElement(doc); //scores
-
  rc <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">xmlTextWriterStartDocument</font></span>'''</span><span><font color="#990000">(</font></span>writer<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> <span><font color="#FF0000">"UTF-8"</font></span><span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
  if (cur == NULL) {
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>rc <span><font color="#990000">&lt;</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
    g_print ("%s, %i: Empty xml doc\n", __PRETTY_FUNCTION__, __LINE__);
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: Error at xmlTextWriterStartDocument</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">);</font></span>
+
    xmlFreeDoc (doc);
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
    return store;
-
  <span><font color="#FF0000">}</font></span>
+
  }
-
+
 
-
  <span>''<span><font color="#9A1900">/* Start an element named ROOT_ELEMENT. Since thist is the first</font></span>''</span>
+
  /* Parse first row tag */
-
<span>''<span><font color="#9A1900">   * element, this will be the root element of the document. */</font></span>''</span>
+
   cur = cur->xmlChildrenNode;
-
  rc <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">xmlTextWriterStartElement</font></span>'''</span><span><font color="#990000">(</font></span>writer<span><font color="#990000">,</font></span> BAD_CAST ROOT_ELEMENT<span><font color="#990000">);</font></span>
+
  if (cur == NULL) {
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>rc <span><font color="#990000">&lt;</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
    g_print ("%s, %i: No row tag\n", __PRETTY_FUNCTION__, __LINE__);
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: Error at xmlTextWriterStartElement</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">);</font></span>
+
    xmlFreeDoc (doc);
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
    return store;
-
  <span><font color="#FF0000">}</font></span>
+
  }
-
+
 
-
  <span>''<span><font color="#9A1900">/* Save each score row from store. */</font></span>''</span>
+
  /* Parse row tags one by one */
-
  <span>'''<span><font color="#000000">gtk_tree_model_foreach</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_TREE_MODEL</font></span>'''</span><span><font color="#990000">(</font></span>store<span><font color="#990000">),</font></span> scores_xml_save_score<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>writer<span><font color="#990000">);</font></span>
+
  while (cur != NULL)
-
+
  {
-
  <span>''<span><font color="#9A1900">/* Here we could close the element named ROOT_ELEMENT using the</font></span>''</span>
+
    /* ... */
-
<span>''<span><font color="#9A1900"* function xmlTextWriterEndElement, but since we do not want to</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">   * write any other elements, we simply call xmlTextWriterEndDocument,</font></span>''</span>
+
    /* Add new row to the store */
-
<span>''<span><font color="#9A1900">  * which will do all the work. */</font></span>''</span>
+
    gtk_list_store_insert_with_values (store, NULL, gtk_tree_model_iter_n_children (GTK_TREE_MODEL(store), NULL), 0, row_name, 1, row_score, -1);
-
  rc <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">xmlTextWriterEndDocument</font></span>'''</span><span><font color="#990000">(</font></span>writer<span><font color="#990000">);</font></span>
+
 
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>rc <span><font color="#990000">&lt;</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
    /* Parse next row element */
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#FF0000">"%s, %i: Error at xmlTextWriterEndDocument</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> __PRETTY_FUNCTION__<span><font color="#990000">,</font></span> __LINE__<span><font color="#990000">);</font></span>
+
    g_free (row_name);
-
  <span><font color="#FF0000">}</font></span>
+
    cur = cur->next;
-
+
   }
-
  <span>'''<span><font color="#000000">xmlFreeTextWriter</font></span>'''</span><span><font color="#990000">(</font></span>writer<span><font color="#990000">);</font></span>
+
 
-
<span><font color="#FF0000">}</font></span></tt>
+
   /* Free the resulting tree */
 +
  xmlFreeDoc(doc);
 +
  return store;
 +
}
 +
</source>
== Portrait Mode ==
== Portrait Mode ==
Line 396: Line 348:
This section explains how to use accelerometer D-Bus interface in order to switch your maemo application to portrait/landscape mode.
This section explains how to use accelerometer D-Bus interface in order to switch your maemo application to portrait/landscape mode.
-
First at all we need to include MCE headers and connect to appropriate dbus signal. maemo-sliders/sliders.c
+
First at all we need to include MCE headers and connect to appropriate D-Bus signal. maemo-sliders/sliders.c
 +
 
 +
<source lang="c">
 +
#include <dbus/dbus.h>
 +
#include <mce/mode-names.h>
 +
#include <mce/dbus-names.h>
 +
#define MCE_SIGNAL_MATCH "type='signal'," \
 +
        "sender='"    MCE_SERVICE    "'," \
 +
        "path='"      MCE_SIGNAL_PATH "'," \
 +
        "interface='" MCE_SIGNAL_IF  "'"
 +
 
 +
/* ... */
-
<tt><span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;dbus/dbus.h&gt;</font></span>
+
   /* Connect to session bus, add a match rule, install filter callback */
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;mce/mode-names.h&gt;</font></span>
+
  appdata.system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, NULL);
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;mce/dbus-names.h&gt;</font></span>
+
  if (appdata.system_bus) {
-
<span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> MCE_SIGNAL_MATCH <span><font color="#FF0000">"type='signal',"</font></span> <span><font color="#990000">\</font></span>
+
    dbus_bus_add_match (appdata.system_bus, MCE_SIGNAL_MATCH, NULL);
-
        <span><font color="#FF0000">"sender='"</font></span>    MCE_SERVICE    <span><font color="#FF0000">"',"</font></span> <span><font color="#990000">\</font></span>
+
    dbus_connection_add_filter (appdata.system_bus,
-
        <span><font color="#FF0000">"path='"</font></span>      MCE_SIGNAL_PATH <span><font color="#FF0000">"',"</font></span> <span><font color="#990000">\</font></span>
+
                                (DBusHandleMessageFunction) mce_filter_func,
-
        <span><font color="#FF0000">"interface='"</font></span> MCE_SIGNAL_IF   <span><font color="#FF0000">"'"</font></span>
+
                                &appdata, NULL);
-
+
  }
-
<span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
</source>
-
+
-
  <span>''<span><font color="#9A1900">/* Connect to session bus, add a match rule, install filter callback */</font></span>''</span>
+
-
  appdata<span><font color="#990000">.</font></span>system_bus <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">dbus_bus_get</font></span>'''</span> <span><font color="#990000">(</font></span>DBUS_BUS_SYSTEM<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>system_bus<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">dbus_bus_add_match</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>system_bus<span><font color="#990000">,</font></span> MCE_SIGNAL_MATCH<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">dbus_connection_add_filter</font></span>'''</span> <span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>system_bus<span><font color="#990000">,</font></span>
+
-
                                <span><font color="#990000">(</font></span>DBusHandleMessageFunction<span><font color="#990000">)</font></span> mce_filter_func<span><font color="#990000">,</font></span>
+
-
                                <span><font color="#990000">&amp;</font></span>appdata<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
</tt>
+
Second action point is rotation directly. We have used Hildon API for this purpose. maemo-sliders/sliders.c
Second action point is rotation directly. We have used Hildon API for this purpose. maemo-sliders/sliders.c
-
<tt><span>'''<span><font color="#0000FF">static</font></span>'''</span> DBusHandlerResult
+
<source lang="c">
-
<span>'''<span><font color="#000000">mce_filter_func</font></span>'''</span> <span><font color="#990000">(</font></span>DBusConnection <span><font color="#990000"><nowiki>*</nowiki></font></span> connection<span><font color="#990000">,</font></span>
+
static DBusHandlerResult
-
                  DBusMessage <span><font color="#990000"><nowiki>*</nowiki></font></span> message<span><font color="#990000">,</font></span> <span><font color="#008080">AppData</font></span> <span><font color="#990000"><nowiki>*</nowiki></font></span>data<span><font color="#990000">)</font></span>
+
mce_filter_func (DBusConnection * connection,
-
<span><font color="#FF0000">{</font></span>
+
                DBusMessage * message, AppData *data)
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
{
-
      <span>''<span><font color="#9A1900">/* Rotate main window */</font></span>''</span>
+
  /* ... */
-
      <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">strcmp</font></span>'''</span> <span><font color="#990000">(</font></span>rotation<span><font color="#990000">,</font></span> MCE_ORIENTATION_PORTRAIT<span><font color="#990000">))</font></span>
+
      /* Rotate main window */
-
        <span>'''<span><font color="#000000">hildon_gtk_window_set_portrait_flags</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>main_window<span><font color="#990000">),</font></span> HILDON_PORTRAIT_MODE_REQUEST<span><font color="#990000">);</font></span>
+
      if (!strcmp (rotation, MCE_ORIENTATION_PORTRAIT))
-
      <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span>'''<span><font color="#000000">hildon_gtk_window_set_portrait_flags</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span>appdata<span><font color="#990000">.</font></span>main_window<span><font color="#990000">),</font></span> <span><font color="#990000"> </font></span>~HILDON_PORTRAIT_MODE_REQUEST<span><font color="#990000">);</font></span>
+
        hildon_gtk_window_set_portrait_flags (GTK_WINDOW(appdata.main_window), HILDON_PORTRAIT_MODE_REQUEST);
-
    <span><font color="#FF0000">}</font></span>
+
      else hildon_gtk_window_set_portrait_flags (GTK_WINDOW(appdata.main_window),  ~HILDON_PORTRAIT_MODE_REQUEST);
-
  <span>''<span><font color="#9A1900">/* ... */</font></span>''</span>
+
    }
-
<span><font color="#FF0000">}</font></span>
+
  /* ... */
-
</tt>
+
}
 +
</source>
-
== Remapping volume keys ==
+
==Enabling volume/zoom keys==
-
Since hildon-2.2.2 you can just use
+
This section explains how to enable use of the device's Volume up/down keys.
-
void [http://maemo.org/api_refs/5.0/5.0-final/hildon/hildon-Additions-to-GTK+.html#hildon-gtk-window-enable-zoom-keys hildon_gtk_window_enable_zoom_keys] (GtkWindow *window, gboolean enable);
+
-
This section explains how to use device's Volume up/down keys to set application to fullsceen or unfullscreen mode.
+
Since hildon-2.2.2 you can just use [http://maemo.org/api_refs/5.0/5.0-final/hildon/hildon-Additions-to-GTK+.html#hildon-gtk-window-enable-zoom-keys hildon_gtk_window_enable_zoom_keys]:
 +
 
 +
<source lang="c">
 +
void hildon_gtk_window_enable_zoom_keys(GtkWindow *window, gboolean enable);
 +
</source>
 +
 
 +
Or if you are not using hildon you use the more complicated way:
First two needed libraries are included
First two needed libraries are included
-
#include <gdk/gdkx.h>
+
<source lang="c">
-
#include <X11/Xatom.h>
+
#include <gdk/gdkx.h>
 +
#include <X11/Xatom.h>
 +
</source>
By default, maemo-status-volume daemon grabs keys for volume control so they need to be released
By default, maemo-status-volume daemon grabs keys for volume control so they need to be released
-
static void
+
<source lang="c">
-
ungrab_volume_keys()
+
static void
-
{
+
ungrab_volume_keys()
-
    /* Tell maemo-status-volume daemon to ungrab keys */
+
{
-
    unsigned long val = 1; /* ungrab, use 0 to grab */
+
    /* Tell maemo-status-volume daemon to ungrab keys */
-
    Atom atom;
+
    unsigned long val = 1; /* ungrab, use 0 to grab */
-
    GdkDisplay *display = NULL;
+
    Atom atom;
-
    display = gdk_drawable_get_display (appdata.main_window->window);
+
    GdkDisplay *display = NULL;
-
    atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
+
    display = gdk_drawable_get_display (appdata.main_window->window);
-
    XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+
    atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
-
                      GDK_WINDOW_XID (appdata.main_window->window), atom, XA_INTEGER, 32,
+
    XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
-
                      PropModeReplace, (unsigned char *) &val, 1);
+
                    GDK_WINDOW_XID (appdata.main_window->window), atom, XA_INTEGER, 32,
-
}
+
                    PropModeReplace, (unsigned char *) &val, 1);
 +
}
 +
</source>
 +
=== Remapping volume keys ===
 +
 +
This section explains how to use device's Volume up/down keys to set application to fullsceen or unfullscreen mode.
-
Next event for key press is created. By returning TRUE when wanted key is pressed function tells to <code>GTK+</code> that keypress was handled. All other keys return FALSE so application lets <code>GTK+</code> decide what to do with them.
+
Next event for key press is created. By returning <code>TRUE</code> when the desired key is pressed function tells to GTK+ that keypress was handled. All other keys return <code>FALSE</code> so application lets GTK+ decide what to do with them.
-
static gboolean
+
<source lang="c">
-
on_key_pressed(GtkWidget* widget, GdkEventKey* event)
+
static gboolean
-
{
+
on_key_pressed(GtkWidget* widget, GdkEventKey* event)
-
  /* set application to fullscreen when volume up is pressed and vice versa */
+
{
-
  if (event->keyval == HILDON_HARDKEY_INCREASE){
+
  /* set application to fullscreen when volume up is pressed and vice versa */
-
    gtk_window_fullscreen(GTK_WINDOW(appdata.main_window));
+
  if (event->keyval == HILDON_HARDKEY_INCREASE){
-
    /* Tell that event was handled */
+
    gtk_window_fullscreen(GTK_WINDOW(appdata.main_window));
-
    return TRUE;
+
    /* Tell that event was handled */
-
  }
+
    return TRUE;
-
 
+
  }
-
  else if (event->keyval == HILDON_HARDKEY_DECREASE){
+
 
-
    gtk_window_unfullscreen(GTK_WINDOW(appdata.main_window));
+
  else if (event->keyval == HILDON_HARDKEY_DECREASE){
-
    return TRUE;
+
    gtk_window_unfullscreen(GTK_WINDOW(appdata.main_window));
-
  }
+
    return TRUE;
-
  /* tell that event wasn't handled */
+
  }
-
  return FALSE;
+
  /* tell that event wasn't handled */
-
}
+
  return FALSE;
 +
}
 +
</source>
-
Two more things to do, both inside main(). key_press_event signal is connected to newly created callback function
+
Two more things to do, both inside <code>main()</code>. The <code>key_press_event</code> signal is connected to newly created callback function
-
g_signal_connect(G_OBJECT(appdata.main_window), "key_press_event",
+
<source lang="c">
-
                  G_CALLBACK(on_key_pressed),  NULL);
+
g_signal_connect(G_OBJECT(appdata.main_window), "key_press_event",
 +
                G_CALLBACK(on_key_pressed),  NULL);
 +
</source>
-
And Volume keys ungrabbing function is called before gtk_main()
+
And Volume keys ungrabbing function is called before <code>gtk_main()</code>
-
ungrab_volume_keys();
+
<source lang="c">>
 +
ungrab_volume_keys();
 +
</source>
== Integration to Menu ==
== Integration to Menu ==
-
Application integration to menu needs .desktop file. This section describes the needed additions and changes.
+
Application integration in the application launcher menu needs a .desktop file. This section describes the needed additions and changes.
-
Sliders doesn't have appropriate .desktop file, so we need to generate it from 'maemo-sliders.desktop.in' with 'configure' script. maemo-sliders.desktop.in
+
Sliders does not have an appropriate .desktop file, so we need to generate it from <code>maemo-sliders.desktop.in</code> with a <code>configure</code> script. maemo-sliders.desktop.in
-
  <code>[Desktop Entry]
+
  [Desktop Entry]
  Encoding=UTF-8
  Encoding=UTF-8
  Version=@VERSION@
  Version=@VERSION@
  Type=Application
  Type=Application
  Name=Maemo Sliders
  Name=Maemo Sliders
-
  Exec=@prefix@/bin/maemo-sliders</code>
+
  Exec=@prefix@/bin/maemo-sliders
== Application Packaging ==
== Application Packaging ==
-
'dh_make' tool have been used for Sliders debianization
+
The <code>dh_make</code> tool has been used for Sliders Debianization.
[[Category:Development]]
[[Category:Development]]
[[Category:Documentation]]
[[Category:Documentation]]
[[Category:Fremantle]]
[[Category:Fremantle]]

Latest revision as of 10:46, 4 September 2010

This section describes key aspects of the process of porting an application to the Maemo platform. When starting to port an application to Maemo platform, the first step is to set up the development environment. The actual porting after that is described in this section.

Contents

[edit] Introduction

Application that is used as an example for porting is Sliders, a GTK+-based game.

The Sliders interface consists of the main window with board, menu and a couple of dialogs. See source mentioned above for game history and description.

[edit] Autotools Usage

Sliders does not use GNU autotools originally, so we need to add configure.ac, Makefile.am and autogen.sh for simplicity. See GNU Build System for corresponding information.

[edit] User Interface Changes

Sliders game is not designed for usage in mobile devices, so we need to hildonize it in order to build effective touch interface for this simple game.

[edit] Hildonizing Main View

Before using Hildon we need to initialize it. maemo-sliders/sliders.c

/* Initialize the GTK+ and hildon libraries */
hildon_gtk_init (&argc, &argv);

After that we can create a main window with all necessary content. maemo-sliders/sliders.c

static GtkWidget*
create_main_window (void)
{
  GtkWidget *main_window;
  /* Create the main window */
  main_window = hildon_stackable_window_new ();
  gtk_window_set_title (GTK_WINDOW (main_window), "Welcome to Sliders");
  /* Create and set application menu */
  HildonAppMenu *menu = create_menu ();
  hildon_window_set_app_menu (HILDON_WINDOW (main_window), menu);
  /* Create and pack table, that contains sliders */
  appdata.table = create_table ();
  gtk_container_add (GTK_CONTAINER (main_window), appdata.table);
  return main_window;
}

We have replaced the original Sliders menu and toolbar with a Hildon application menu.

Screenshot of menu
Maemo Sliders menu


maemo-sliders/sliders.c

static HildonAppMenu *
create_menu (void)
{
  /*
   * Create menu buttons one by one, connect to appropriate callbacks and
   * add to the menu.
   */
  HildonSizeType button_size = HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH;
  HildonAppMenu *menu = HILDON_APP_MENU (hildon_app_menu_new ());
  GtkWidget *button;
  button = hildon_gtk_button_new (button_size);
  gtk_button_set_label (GTK_BUTTON (button), "New");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (on_new_clicked), NULL);
  hildon_app_menu_append (menu, GTK_BUTTON (button));
  button = hildon_gtk_button_new (button_size);
  gtk_button_set_label (GTK_BUTTON (button), "Scores");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (on_scores_clicked), NULL);
  hildon_app_menu_append (menu, GTK_BUTTON (button));
  button = hildon_gtk_button_new (button_size);
  gtk_button_set_label (GTK_BUTTON (button), "Open");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (on_open_clicked), NULL);
  hildon_app_menu_append (menu, GTK_BUTTON (button));
  button = hildon_gtk_button_new (button_size);
  gtk_button_set_label (GTK_BUTTON (button), "Save");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (on_save_clicked), NULL);
  hildon_app_menu_append (menu, GTK_BUTTON (button));
  button = hildon_gtk_button_new (button_size);
  gtk_button_set_label (GTK_BUTTON (button), "About");
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (on_about_clicked), NULL);
  hildon_app_menu_append (menu, GTK_BUTTON (button));
  gtk_widget_show_all (GTK_WIDGET (menu));
  return menu;
}

Sliders table buttons (see figure 16.13) are maximized and wrapped with hildon_gtk_button_new (). maemo-sliders/sliders.c

static GtkWidget*
create_table (void)
{
  GtkWidget *table;
  gint x, y, c = 0;
  appdata.vacant_pos = SLIDERS_NUMBER;
  appdata.move_no = 0;
  HildonSizeType button_size = HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH;
  /* Create table for sliders */
  table = gtk_table_new (4, 4, FALSE);
  /*
   * Create buttons, that represents sliders. Set "current_pos" key for each button
   * and connect each button to appropriate callback.
   */
  for (x = 0; x < SLIDERS_NUMBER; x++)
  {
    gchar *temp = g_strdup_printf ("%i", x+1);
    appdata.buttons[x] = hildon_gtk_button_new (button_size);
    gtk_button_set_label (GTK_BUTTON(appdata.buttons[x]), temp);
    g_object_set_data (G_OBJECT(appdata.buttons[x]), "current_pos", (gpointer) x);
    g_free (temp);
    gtk_signal_connect(GTK_OBJECT(appdata.buttons[x]), "clicked", GTK_SIGNAL_FUNC(on_button_clicked), NULL);
  }
  /* Add sliders to table */
  for (x = 0; x < 4; x++)
  for (y = 0; y < 4; y++)
    if (!(x == 3 && y == 3))
      gtk_table_attach_defaults (GTK_TABLE (table), appdata.buttons[c++], y, y+1, x, x+1);
  return table;
}
Maemo Sliders table

[edit] Implementing Scores Window

We have replaced original Sliders win picture by scores window (figure 16.14), that appears in two cases. It's shown after "Scores" menu button clicked maemo-sliders/sliders.c

static void
on_scores_clicked (GtkMenuItem *menuitem, gpointer user_data)
{
  scores_window (0);
}

and when player has won. maemo-sliders/sliders.c

static void
on_button_clicked (GtkWidget *button, gpointer user_data)
{
  /* ... */
    if ( has_player_won() )
    {
      scores_window (1);
      gtk_window_set_title (GTK_WINDOW (appdata.main_window), "Welcome to Sliders");
      appdata.move_no = 0;
    }
    else
    {
      gchar *temp = g_strdup_printf ("Move no.: %i", appdata.move_no);
      gtk_window_set_title (GTK_WINDOW (appdata.main_window), temp);
      g_free (temp);
    }
  /* ... */
}
Screenshot of scores window
Maemo sliders scores

Scores window contains pannable area with tree view maemo-sliders/sliders.c

/* Create a tree view that represents scores table and an area for it */
area = hildon_pannable_area_new ();
tree_view = create_treeview (HILDON_UI_MODE_EDIT);
/* ... */
/* Pack tree view */
gtk_container_add (GTK_CONTAINER (area), tree_view);
gtk_container_add (GTK_CONTAINER (window), area);

and edit toolbar. maemo-sliders/sliders.c

/* Create a new edit toolbar */
toolbar = hildon_edit_toolbar_new_with_text ("Choose scores to delete",
                                             "Delete");
/* ... */
/* Add toolbar to the window */
hildon_window_set_edit_toolbar (HILDON_WINDOW (window),
                                HILDON_EDIT_TOOLBAR (toolbar));
/* ... */
/* Set callback for "Delete" button */
g_signal_connect (toolbar, "button-clicked",
                  G_CALLBACK (delete_selected_scores),
                  tree_view);
/* Destroy scores window when upper corner arrow clicked */
g_signal_connect_swapped (toolbar, "arrow-clicked",
                          G_CALLBACK (gtk_widget_destroy),
                          window);

Win banner is shown if scores_window () called in case player has won. maemo-sliders/sliders.c

/* Additional actions for case, when player has won */
if (win)
{
  /* Show appropriate banner */
  hildon_banner_show_informationf (window, NULL, "Win! Score: %i", appdata.move_no);
  /* ... */
}

Other scores related code is quite straightforward and does not use Hildon-specific features, see the scores_window () implementation for details.

[edit] State Saving

Maemo supports state saving feature, that we have used for current Sliders game saving. The application can later open this game with the same state as before. This section describes most important steps needed to make Sliders support state savings.

state_save () is called when the user presses the "Save" menu button. Saving data should be updated before it is called. maemo-sliders/sliders.c

static void
on_save_clicked (GtkMenuItem *menuitem, gpointer user_data)
{
  /* Save moves number, vacant position and board layout to "state" */
  state.move_no = appdata.move_no;
  state.vacant_pos = appdata.vacant_pos;
  int i;
  for (i = 0; i < SLIDERS_NUMBER; i++)
    state.map[i] = (gint) gtk_object_get_data(GTK_OBJECT(appdata.buttons[i]), "current_pos");
  /* Call state saving method and process return value */
  if (state_save ())
    hildon_banner_show_information (appdata.main_window, NULL, "Saved current game");
  else hildon_banner_show_information (appdata.main_window, NULL, "Can't save current game");
}

Then osso_state_write () can be called for correct data. maemo-sliders/sliders.c

state_save(void)
{
  osso_state_t osso_state;
  osso_return_t ret;
  osso_state.state_size = sizeof(StateData);
  osso_state.state_data = &state;
  ret = osso_state_write(appdata.osso_context, &osso_state);
  if (ret != OSSO_OK)
    return FALSE;
  return TRUE;
}

state_load () is called when user clicks the "Open" menu button. It uses osso_state_read () symmetric to state_save (). maemo-sliders/sliders.c

state_load(void)
{
  osso_state_t osso_state;
  osso_return_t ret;
  osso_state.state_size = sizeof(StateData);
  osso_state.state_data = &state;
  ret = osso_state_read(appdata.osso_context, &osso_state);
  if (ret != OSSO_OK)
    return FALSE;
  return TRUE;
}

Do not forget to initialize the library context before state-saving usage. maemo-sliders/sliders.c

/* Initialize Libosso */
appdata.osso_context = osso_initialize(SL_SERVICE, VERSION, TRUE, NULL);

The information saved by using state saving functions does not survive over power off of the device, so if you need to save some data more permanently you could use e.g. libxml as shown in the next section for the scores table.

[edit] Scores Saving

Scores data store is created, when get_scores_store () called first. The store is populated from a file saved under the user's home directory. The document tree is parsed with node pointer starting from root element. maemo-sliders/sliders.c

static GtkListStore *
get_scores_store (void)
{
  static GtkListStore *store = NULL;
 
  /* Return store if it has been created before */
  if (store != NULL)
    return store;
 
  /* Create store with 2 columns of specified types */
  store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
 
  xmlDocPtr doc; /* the resulting document tree */
  xmlNodePtr cur; /* node pointer */
  xmlChar *attribute; /* node attribute name */
  char *row_name, *temp;
  gint row_score;
 
  /* Parse an XML file from the filesystem */
  char *slidersdir = g_strdup_printf ("%s%s%s", getenv("HOME"), SLIDERS_DIR, SCORES_XML);
  doc = xmlReadFile (slidersdir, NULL, XML_PARSE_NOERROR);
  g_free (slidersdir);
  if (doc == NULL) {
    g_print ("%s, %i: Failed to parse document %s\n", __PRETTY_FUNCTION__, __LINE__, SCORES_XML);
    return store;
  }
 
  /* Parse root element */
  cur = xmlDocGetRootElement(doc); //scores
  if (cur == NULL) {
    g_print ("%s, %i: Empty xml doc\n", __PRETTY_FUNCTION__, __LINE__);
    xmlFreeDoc (doc);
    return store;
  }
 
  /* Parse first row tag */
  cur = cur->xmlChildrenNode;
  if (cur == NULL) {
    g_print ("%s, %i: No row tag\n", __PRETTY_FUNCTION__, __LINE__);
    xmlFreeDoc (doc);
    return store;
  }
 
  /* Parse row tags one by one */
  while (cur != NULL)
  {
    /* ... */
 
    /* Add new row to the store */
    gtk_list_store_insert_with_values (store, NULL, gtk_tree_model_iter_n_children (GTK_TREE_MODEL(store), NULL), 0, row_name, 1, row_score, -1);
 
    /* Parse next row element */
    g_free (row_name);
    cur = cur->next;
  }
 
  /* Free the resulting tree */
  xmlFreeDoc(doc);
  return store;
}

[edit] Portrait Mode

This section explains how to use accelerometer D-Bus interface in order to switch your maemo application to portrait/landscape mode.

First at all we need to include MCE headers and connect to appropriate D-Bus signal. maemo-sliders/sliders.c

#include <dbus/dbus.h>
#include <mce/mode-names.h>
#include <mce/dbus-names.h>
#define MCE_SIGNAL_MATCH "type='signal'," \
        "sender='"    MCE_SERVICE     "'," \
        "path='"      MCE_SIGNAL_PATH "'," \
        "interface='" MCE_SIGNAL_IF   "'"
 
/* ... */
 
  /* Connect to session bus, add a match rule, install filter callback */
  appdata.system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, NULL);
  if (appdata.system_bus) {
    dbus_bus_add_match (appdata.system_bus, MCE_SIGNAL_MATCH, NULL);
    dbus_connection_add_filter (appdata.system_bus,
                                (DBusHandleMessageFunction) mce_filter_func,
                                &appdata, NULL);
  }

Second action point is rotation directly. We have used Hildon API for this purpose. maemo-sliders/sliders.c

static DBusHandlerResult
mce_filter_func (DBusConnection * connection,
                 DBusMessage * message, AppData *data)
{
  /* ... */
      /* Rotate main window */
      if (!strcmp (rotation, MCE_ORIENTATION_PORTRAIT))
        hildon_gtk_window_set_portrait_flags (GTK_WINDOW(appdata.main_window), HILDON_PORTRAIT_MODE_REQUEST);
      else hildon_gtk_window_set_portrait_flags (GTK_WINDOW(appdata.main_window),  ~HILDON_PORTRAIT_MODE_REQUEST);
    }
  /* ... */
}

[edit] Enabling volume/zoom keys

This section explains how to enable use of the device's Volume up/down keys.

Since hildon-2.2.2 you can just use hildon_gtk_window_enable_zoom_keys:

void hildon_gtk_window_enable_zoom_keys(GtkWindow *window, gboolean enable);

Or if you are not using hildon you use the more complicated way:

First two needed libraries are included

#include <gdk/gdkx.h>
#include <X11/Xatom.h>

By default, maemo-status-volume daemon grabs keys for volume control so they need to be released

static void
ungrab_volume_keys()
{
    /* Tell maemo-status-volume daemon to ungrab keys */
    unsigned long val = 1; /* ungrab, use 0 to grab */
    Atom atom;
    GdkDisplay *display = NULL;
    display = gdk_drawable_get_display (appdata.main_window->window);
    atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
    XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
                     GDK_WINDOW_XID (appdata.main_window->window), atom, XA_INTEGER, 32,
                     PropModeReplace, (unsigned char *) &val, 1);
}

[edit] Remapping volume keys

This section explains how to use device's Volume up/down keys to set application to fullsceen or unfullscreen mode.

Next event for key press is created. By returning TRUE when the desired key is pressed function tells to GTK+ that keypress was handled. All other keys return FALSE so application lets GTK+ decide what to do with them.

static gboolean
on_key_pressed(GtkWidget* widget, GdkEventKey* event)
{
  /* set application to fullscreen when volume up is pressed and vice versa */
  if (event->keyval == HILDON_HARDKEY_INCREASE){
    gtk_window_fullscreen(GTK_WINDOW(appdata.main_window));
    /* Tell that event was handled */
    return TRUE;
  }
 
  else if (event->keyval == HILDON_HARDKEY_DECREASE){
    gtk_window_unfullscreen(GTK_WINDOW(appdata.main_window));
    return TRUE;
  }
  /* tell that event wasn't handled */
  return FALSE;
}

Two more things to do, both inside main(). The key_press_event signal is connected to newly created callback function

g_signal_connect(G_OBJECT(appdata.main_window), "key_press_event",
                 G_CALLBACK(on_key_pressed),  NULL);

And Volume keys ungrabbing function is called before gtk_main()

>
ungrab_volume_keys();

[edit] Integration to Menu

Application integration in the application launcher menu needs a .desktop file. This section describes the needed additions and changes.

Sliders does not have an appropriate .desktop file, so we need to generate it from maemo-sliders.desktop.in with a configure script. maemo-sliders.desktop.in

[Desktop Entry]
Encoding=UTF-8
Version=@VERSION@
Type=Application
Name=Maemo Sliders
Exec=@prefix@/bin/maemo-sliders

[edit] Application Packaging

The dh_make tool has been used for Sliders Debianization.