<!doctype book PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [ <!entity libgnome SYSTEM "libgnome.sgml"> <!entity libgnomeui SYSTEM "libgnomeui.sgml"> <!entity gnome-app-helper SYSTEM "gnome-app-helper.sgml"> <!entity gnome-canvas SYSTEM "gnome-canvas.sgml"> <!entity gnome-message-utils SYSTEM "gnome-message-utils.sgml"> <!entity x-concepts SYSTEM "x-concepts.sgml"> <!entity libgnorba SYSTEM "libgnorba.sgml"> <!entity gnome-mdi SYSTEM "gnome-mdi.sgml"> ]> <book> <bookinfo> <title>Gnome Developers' Information</title> <authorgroup> <author> <firstname>Horacio</firstname> <surname>Peña</surname> <affiliation> <address> <email>horape@compendium.com.ar</email> </address> </affiliation> </author> </authorgroup> <copyright> <year>1998</year> <holder>The GNOME Project - Horacio J. Peña (n.)</holder> </copyright> <legalnotice> <para>This documentation is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</para> <para>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</para> <para>You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</para> <para>For more details see the file COPYING in the source distribution of GNOME.</para> </legalnotice> </bookinfo> <toc></toc> <chapter id="intro"> <title>Introduction..</title> <sect1 id="feedback"> <title>Feedback..</title> <para>Well, my native language is Spanish, so you're probably going to find a lot of grammatical and spelling errors. Please tell me about them.</para> </sect1> <sect1 id="whatis"> <title>What is GNOME..</title> <para> GNOME stands for GNU Network Object Model Environment. The GNOME project intends to build a complete, user-friendly desktop based entirely on free software. GNOME is part of the GNU project. The desktop will consist of small utilities and larger applications which share a consistent look and feel. GNOME uses GTK as the GUI toolkit for all GNOME-compliant applications. </para> </sect1> <sect1 id="thisdoc"> <title>This document.</title> <warning> <para> This tutorial is in its early stages. So it's almost empty and hasn't been checked. GNOME too is developing rapidly so it's APIs are changing frequently and the info here may well be inaccurate. When this document and the code disagree, the code wins. :-) </para> </warning> <para> As a new GNOME developer I've had some trouble finding info about how to do some things (and the people on the list were being a bit annoyed with my questions). So I'm trying to make it easy for you, the developers that come after me. </para> <para> This document is not only intended to be a tutorial, but also a reference. I hope it'll grow to be the ultimate guide to the GNOME internals. I would like to include here the architecture notes that are now in the website and the style guide. </para> <para> If you add or change some API please document it here. </para> </sect1> </chapter> <chapter id="nontech"> <title>Non (very) technical issues.</title> <para> This chapter will discuss issues ranging from how to obtain a CVS account to indenting standards. </para> <sect1 id="proglang"> <title>Programming languages that can be used in GNOME.</title> <para> GNOME is intended to support many languages. But you need to have GTK+ and GNOME bindings for your particular language. Our preferred languages are C and scheme (using guile.) There are bindings too for Objective C. There also are GTK bindings for C++ (Gtk--) and for Perl, but as far as I know, they aren't very good and still there aren't bindings for GNOME stuff for these languages. </para> </sect1> <sect1 id="start"> <title>How to start hacking GNOME.</title> <para> Read the README and HACKING files to learn how to download the latest GNOME from CVS. </para> <para> You will need know the Gtk+ toolkit. If you are not familiar with it, you may consider reading the <ulink URL="http://www.levien.com/~slow/gtk/">Gtk+ Tutorial</ulink>. </para> <para> When you've made your changes you need to make a diff... The best way to do it is: <literallayout> <prompt>$ </prompt><userinput>cvs diff -u > diffs</userinput> </literallayout> [FIXME: Does it work with anonymous CVS?] After this send the diffs file to <email>gnome-devel-list@gnome.org</email>. In a short time one of the core hackers will answer you and he is going to be your "GNOME godfather". Then, and until you have a CVS account send your patches to him. [FIXME: What think, you GNOME core hackers, about the godfather idea?] </para> </sect1> <sect1 id="what"> <title>What to hack.</title> <para> If you want to dirty your hands with GNOME, I've some advice for you: </para> <itemizedlist> <listitem><para>Start simple. Do little things and familiarize yourself with GNOME. You'll have time to do great things afterwards.</para> </listitem> <listitem><para>Read the TODO. You'll find some interesting things to work in.</para> </listitem> </itemizedlist> </sect1> <sect1 id="CVS"> <title>Obtaining a CVS account.</title> <para> When you have been working on GNOME for a while it's may be time to get a CVS account. Usually you will be offered one by your "godfather"; there is no harm in asking, but remember a CVS account carries responsibilities too. </para> </sect1> <sect1 id="altpol"> <title>Policy on hacking another people's code.</title> <para> If you're going to do little clean fixes, go for it. But if you plan to do changes to some API or rewriting some code please ask / tell the author. Modules on CVS have different levels of maturity: some of them are being developed actively, other are very stable and changes to it should usually go first through the module maintainer. To help clarify this situation, usually, there is a file called README.CVS included on every module on CVS where the policy is detailed for the hacking you can do to a module. </para> <para> If in doubt, ask the gnome-hackers@nuclecu.unam.mx for assistance. </para> </sect1> <sect1 id="addAUTH"> <title>When adding yourself to AUTHORS file.</title> <para> We know it. You're working in the GNOME to impress girls saying them: "Hey, I'm a GNOME developer" :-), so you want to add yourself to the AUTHORS file as quickly as you can... </para> <para> To know when you can add yourself only think :-), if you've only fixed some typos don't do it. If you've added a great bunch of (good) code, then go ahead. </para> <para> (<emphasis>Very personal opinion:</emphasis>) I suggest you don't add yourself to the root AUTHORS file. I think you should wait for some of the core hackers recognize your work and add you. (i believe it would be very much more satisfying to know that the "real" hackers value your work, probably this belief is why I am not there yet... :-) ) </para> </sect1> <sect1 id="codstd"> <title>Coding Standards.</title> <itemizedlist> <listitem><para> Code that goes into Gnome uses the Linux kernel coding style (which is basically the GNU coding style but indentation is 8 spaces, and braces are on the same line as the statements that open the block). Make yourself familiar with both coding styles (the GNU coding style and the Linux kernel coding style)</para> <para>If you are working with vim, you can use the following setting for the cindent feature that will be compatible with the style used in gnome-libs. If you put this setting in your ~/.vimrc file, it will turn on cindent and other stuff useful for writing code for *.c and *.h files. </para> <programlisting> set cino=t0,:0,(0,)100,*100 au BufReadPost *.c set cindent sm wm=0 tw=0 au BufReadPost *.h set cindent sm wm=0 tw=0 </programlisting> </listitem> <listitem><para>Try to make your code readable in 80 columns</para> </listitem> <listitem><para> When you change things, edit the ChangeLog, so the other people can know what you've did. The ChangeLog is an important document, as it is shipped with the package and it lets the end user and programmers not familar with the GNOME way of things to know what has been the evolution of a package. It also allows people with no continuous network access to find out what changes were done and by whom</para></listitem> <listitem><para> When you're writing code that should be written better add FIXME comments so it's easy to recognize.</para></listitem> <listitem><para>Try to use defensive programming techniques: the glib library provides various utility macros that help you make your code more robust. g_return_if_fail, g_return_val_if_fail, g_warning, g_error and g_assert are your friends.</para> </listitem> <listitem><para> Document your programs.</para></listitem> <listitem><para> If you do changes to the APIs documented here or add some new ones please update the documentation.</para></listitem> <listitem><para> Add translations for your native language. We would like have a fully internationalized GNOME. (so if you know more than one language do translations for all of them) (we would like all the idioms, from francais to old greek :-) ) </para></listitem> </itemizedlist> </sect1> </chapter> <chapter id="tutorial"> <title>GNOME Developer's Tutorial.</title> <para> From the gnome-hello manual: </para> <para> gnome-hello is a GNOME application which contains all the essential and common features of GNOME programs, such as initialization, event loops, configuration file parsing, internationalization and so forth. </para> <para> gnome-hello is intended to be an example of the GNOME coding standards, as well as a fun and useless little program. </para> <para> In this chapter we're going to use it to explain the basics features that an application must have to be considered gnome-compliant. </para> <sect1 id="tut-basics"> <title>Starting with GNOME</title> <screenshot> <screeninfo>A basic GNOME App: A window with only a button </screeninfo> <graphic fileref="gnome-hello-0-basic.jpg"></graphic> </screenshot> <para> We'll start with a very basic application. It's only a window with a button which when clicked, close the window and prints "Hello GNOME". The code source for it is in <filename> programs/gnome-hello/gnome-hello-0-basic.c</filename>. [FIXME: Should i include the full code here?] </para> <programlisting> #include <config.h> #include <gnome.h> </programlisting> <para> All the programs have to include <filename>gnome.h</filename> which gives all you need to use the Gtk+ and GNOME libraries. </para> <programlisting> int main(int argc, char *argv[]) { </programlisting> <para><function>gnome_init</function> is always called at the beginning of a program. It takes care of initializing both Gtk and GNOME. </para> <programlisting> gnome_init (&argc, &argv); </programlisting> <para>Then we call <function>prepare_app</function> that do the real work and <function>gtk_main</function> to enter into the main processing loop. </para> <programlisting> prepare_app (); gtk_main (); return 0; } </programlisting> <para>Let's now go to the <function>prepare_app</function> code... </para> <programlisting> void prepare_app() { GtkWidget *button; </programlisting> <para>We first make the main window calling <function>gnome_app_new</function> and connect the signal <function>delete_event</function> to the callback <function>quit_cb</function>, so the user can close the app via the window manager: </para> <programlisting> app = gnome_app_new ("hello", "Hello World Gnomified"); gtk_widget_realize (app); gtk_signal_connect (GTK_OBJECT (app), "delete_event", GTK_SIGNAL_FUNC (quit_cb), NULL); </programlisting> <para>Then, we create a button and set it to be the content of the main window. </para> <programlisting> button = gtk_button_new_with_label ("Hello GNOME"); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (hello_cb), NULL); gtk_container_border_width (GTK_CONTAINER (button), 60); gnome_app_set_contents ( GNOME_APP (app), button); </programlisting> <para>Finally, we show the widgets. It could be done in any order, but we display at last the main window so the whole window will pop up at once rather than seeing the window pop up, and then the button form inside of it. </para> <programlisting> gtk_widget_show (button); gtk_widget_show (app); } </programlisting> <para>The callbacks (<function>hello_cb</function> for the button and <function>quit_cb</function> for <literal>delete_event</literal>) are very simple. They just call <function>gtk_main_quit</function> to exit. </para> <programlisting> void hello_cb (GtkWidget *widget, void *data) { g_print ("Hello GNOME\n"); gtk_main_quit (); return; } void quit_cb (GtkWidget *widget, void *data) { gtk_main_quit (); return; } </programlisting> </sect1> <sect1 id="tut-menus"> <title>Adding menus</title> <screenshot> <graphic fileref="gnome-hello-1-menus.jpg"></graphic> </screenshot> <para> From the <citetitle>GNOME Style Guide</citetitle> </para> <para> <literallayout> Menus and MenuBars Menubars should be present in every App. All apps should have a "Help" entry in the Menubar. All menus need at least one entry. An About Menu should be available in under the Help Menu and should open a small dialog telling at least app name, author, version, and date. A "File" menu should be in every application, and contain at least Quit. Menu items that open dialogs should indicate it with an "..." Menu items that lead to submenus should indicate it with an arrow. The Help Menu should be right justified in the Menubar. </literallayout> </para> <para> At the time of the writing of this draft gnome-hello uses the GtkMenuFactory way of creating menus. It's going to be replaced by the gnome-app-helper in a little time. (when the gnome_app_* have support for i18n, accelerators and right-justified menus). Until then isn't going to be an explanation about "how" do the menus. </para> <para> The menu options present in gnome-hello are only the mandatory: <guimenuitem>File/Exit</guimenuitem>, that uses the same quit_cb that we've seen in the previous section, and <guimenuitem>Help/About...</guimenuitem> that uses the gnome-about widget (see <xref linkend="gnome-about">) written by Cesar <email>miquel@df.uba.ar</email>. </para> <programlisting> void about_cb (GtkWidget *widget, void *data) { GtkWidget *about; gchar *authors[] = { /* Here should be your names */ "Mark Galassi", "Horacio J. Peña", NULL }; about = gnome_about_new ( "The Hello World Gnomified", VERSION, /* copyright notice */ "(C) 1998 the Free Software Foundation", authors, /* another comments */ "GNOME is a civilized software system " "so we've a \"hello world\" program", NULL); gtk_widget_show (about); return; } </programlisting> </sect1> <sect1 id="tut-i18n"> <title>Internationalization.</title> <screenshot> <graphic fileref="gnome-hello-2-i18n.jpg"></graphic> </screenshot> <para> Internationalization (i18n for short) is very easy with GNOME. We use GNU gettext. You need to initialize it with a call to <function>bindtextdomain</function> and <function>textdomain</function> in your main function. After it a _() to all the strings that need translation. </para> <para> You will need add your file to po/POTFILES.in. Then you can do <literal>make gnome-modulename.pot;make update-po</literal>, edit the .po file corresponding to your language and do <literal>make install</literal>. Now try your program... Wow! It's speaking in Spanish/Finnish/Latin... </para> <warning> <para> _() is a macro for the function gettext. So it won't work when initializing arrays of strings or structs. You'll need use the N_() macro around the strings instead. If you want know more about it read the info documentation of GNU gettext. </para> </warning> </sect1> <sect1 id="tut-parsing"> <title>Parsing parameters.</title> <para> Well, Carsten and Tom are talking about standardizing it, so I'll wait a bit their definitions. </para> </sect1> <sect1 id="tut-SM"> <title>Session Management</title> <warning> <para> This chapter is definitely under construction, But hold your breath, it will (hopefully) be finished soon.</para> </warning> <sect2> <title>Session Management in general</title> <para> The purpose of session management is to provide users a possibility to save and restore their sessions. A session is a collection of applications, all of them having an internal state. This state may be the name of an open file, a displayed image or the score of a game. </para> <para> Every application that is session management aware connects to one special server: the <emphasis>session manager</emphasis>. A session manager sends commands to his <emphasis>clients</emphasis> telling them to save their state or to terminate. A client must provide the session manager with all information, that is needed to restart the client in the same state, as it is running now. The session manager's task is to take care of this information and to use it when restarting a session. In order to distinguish all clients, the session manager assigns them a unique identifier: the so called <emphasis>client id</emphasis>. </para> <para> The session management additionally includes a protocol to sync the - so called - interact requests of applications. Suppose you have three applications running. Each of this applications has one file opened, that you have just edited without saving. If you now log out, every application may ask you, whether you want to save your changes or if you want to abandon them. It would be very annoying, if all these three applications would pop up their dialog boxes at the same time. If these three applications have implemented session management in the right way, a new dialog box only pops up, if the previous one has been closed. </para> </sect2> <sect2> <title>GNOME Session Management implementation</title> <para> The GNOME project uses a special object - the GnomeClient object - to implement session management. This object handles the connection to a session manager, the setting and removing of properties and the handling of messages sent by a session manager. </para> <para> There are two functions in the GNOME libraries, that create a new GnomeClient object: </para> <funcsynopsis> <funcdef>GnomeClient *<function>gnome_client_new</function></funcdef> <paramdef></paramdef> </funcsynopsis> <funcsynopsis> <funcdef>GnomeClient *<function>gnome_client_new_without_connection</function></funcdef> <paramdef></paramdef> </funcsynopsis> <para> As one may guess from the functions names: The first function tries to connect to a session manager automatically, while the second one does not. You may connect or disconnect a GnomeClient after his creation using the following functions: </para> <funcsynopsis> <funcdef>void <function>gnome_client_connect</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> </funcsynopsis> <funcsynopsis> <funcdef>void <function>gnome_client_disconnect</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> </funcsynopsis> <funcsynopsis> <funcdef>gchar *<function>gnome_client_get_id</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> </funcsynopsis> <funcsynopsis> <funcdef>void <function>gnome_client_set_id</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>const gchar *<parameter>client_id</parameter></paramdef> </funcsynopsis> <para>to be continued...</para> <sect3> <title>Properties</title> <para> </para> </sect3> <sect3> <title>Signals</title> <para> Whenever the session manager wants a client to do something, these wishes are emitted as signals. </para> <funcsynopsis> <funcdef>gint <function>save_yourself_signal</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>gint <parameter>phase</parameter></paramdef> <paramdef>GnomeSaveStyle <parameter>save_style</parameter></paramdef> <paramdef>gint <parameter>shutdown</parameter></paramdef> <paramdef>GnomeInteractStyle <parameter>interact_style</parameter></paramdef> <paramdef>gint <parameter>fast</parameter></paramdef> <paramdef>gpointer <parameter>client_data</parameter></paramdef> </funcsynopsis> <para> This signal is probably the most important one, because it causes the clients to save the programs state. </para> <para> <parameter>save_style</parameter>. </para> <para> The <parameter>shutdown</parameter> parameter indicates, whether this </para> <para> GNOME_INTERACT_NONE, GNOME_INTERACT_ERRORS, GNOME_INTERACT_ANY </para> <para> If the <parameter>fast</parameter> parameter is TRUE, the client is wanted to save it's state as fast as possible. </para> <funcsynopsis> <funcdef>gint <function>die_signal</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>gpointer <parameter>client_data</parameter></paramdef> </funcsynopsis> <para> The above signal is emitted, if the session manager wants the client to terminate. This will mostly happen, if the user logs out from a running session. </para> <funcsynopsis> <funcdef>gint <function>save_complete_signal</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>gpointer <parameter>client_data</parameter></paramdef> </funcsynopsis> <para> The session manager sends this message, if all client have finished saving their state. </para> <funcsynopsis> <funcdef>gint <function>shutdown_cancelled_signal</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>gpointer <parameter>client_data</parameter></paramdef> </funcsynopsis> <para> Whenever a shutdown, that has been announced in a save_yourself signal, has been cancelled by the user, the shutdown_cancelled message is sent. </para> <funcsynopsis> <funcdef>gint <function>connect_signal</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>gint <parameter>restarted</parameter></paramdef> <paramdef>gpointer <parameter>client_data</parameter></paramdef> </funcsynopsis> <funcsynopsis> <funcdef>gint <function>disconnect_signal</function></funcdef> <paramdef>GnomeClient *<parameter>client</parameter></paramdef> <paramdef>gpointer <parameter>client_data</parameter></paramdef> </funcsynopsis> <para> This signal is emitted, if the connection to the session manager gets lost. </para> </sect3> <sect3> <title>The master client</title> <para> To make life a little bit easier for GNOME developers, the GNOME libraries feature a special client: the <emphasis>master client</emphasis>. This client is created automatically, when calling <function>gnome_init</function>. He gets some default properties set and is in general also connected to the session manager automatically. the GNOME libraries also notice, if a this client was restarted. In this case, the libraries try to connect the master client to the session manager with the same client id as last time. That means, that an application calling <function>gnome_init</function> will be recognized and be restarted from a session manager without any extra lines of code. A developer has only to take care, that the applications state is saved or restored correctly and that the application is terminated, if she is wanted to. </para> <para> Your get the master client by calling <function>gnome_master_client</function>. </para> <funcsynopsis> <funcdef>GnomeClient *<function>gnome_master_client</function></funcdef> <paramdef></paramdef> </funcsynopsis> <para> The master client has the following properties preset. </para> <informaltable> <tgroup cols="2"> <thead> <row> <entry>property</entry> <entry>value</entry> </row> </thead> <tbody> <row> <entry>current directory</entry> <entry>current directory</entry> </row> <row> <entry>process id</entry> <entry>pid</entry> </row> <row> <entry>user id</entry> <entry>uid</entry> <!-- one of (entrytbl entry) --> </row> </tbody> </tgroup> </informaltable> </sect3> <sect3> <title>The cloned client</title> <para></para> </sect3> </sect2> <sect2> <title>Continuing the tutorial</title> <para> We want to implement session management, so we have to use the master client. To get access to this client, that was created in <function>gnome_init</function>, you have to call <function>gnome_master_client</function>. Our application should at least listen to the die and the save_yourself signal, so insert the following lines of code right after the <function>gnome_init</function> call. </para> <programlisting role="C"> client= gnome_master_client (); gtk_signal_connect (GTK_OBJECT (client), "save_yourself", GTK_SIGNAL_FUNC (save_state_cb), NULL); gtk_signal_connect (GTK_OBJECT (client), "die", GTK_SIGNAL_FUNC (die_cb), NULL);</programlisting> <para> Now we have to implement our signal functions. The die signal function is rather easy to implement. We just have to terminate our application. Notice, that we must not save the applications state in a die signal function. If the session manager would have wanted us to save the state, he would have sent us a save_yourself signal right before the die signal. If you are writing a more complicated application, you may want to close some files here or do some other magic stuff. </para> <programlisting> static gint die (GnomeClient *client, gpointer client_data) { gtk_exit (0); return FALSE; }</programlisting> <para> Implementing the save_yourself signal is a little bit more difficult, because we have to save the hole state of our application. Our tutorial application has only one state: the window's position on the screen. So we start our save_yourself signal function like this: </para> <programlisting> gint save_yourself (GnomeClient *client, gint phase, GnomeRestartStyle restart_style, gint shutdown, GnomeInteractStyle interact_style, gint fast, gpointer client_data) { gchar *argv[3]; gint x, y, w, h; gdk_window_get_geometry (app->window, &x, &y, &w, &h, NULL);</programlisting> <para> Now that we have our application's state, we have to store it. One way to do this is to store the application's state in the command line that we use to restart our application. That's a quite useful method for storing a little set of values. In this tutorial we'll implement another way of saving the applications state, that is even practical, if you have a huge bunch of data to save. We use the gnome config files. The <function>gnome_client_get_config_prefix</function> gives us a hint , where to store our information. </para> <programlisting> /* Save the state using gnome-config stuff. */ gnome_config_push_prefix (gnome_client_get_config_prefix (client)); gnome_config_set_int ("Geometry/x", x); gnome_config_set_int ("Geometry/y", y); gnome_config_set_int ("Geometry/w", w); gnome_config_set_int ("Geometry/h", h); gnome_config_pop_prefix (); gnome_config_sync();</programlisting> <para> Additionally we have to give the session manager some hints, how to restart our application. That means, we have to use the <function>gnome_client_set_clone_command</function> and <function>gnome_client_set_restart_command</function> functions. Notice that we don't have to add the clients id to these command; we don't even have to add the standard GNOME command line options. Both is added by the GNOME libraries. We also don't have to distinguish between the restart command and the clone command. This is also handled by the libraries.</para> <programlisting> gnome_client_set_clone_command (client, 1, argv); gnome_client_set_restart_command (client, 1, argv); return TRUE; }</programlisting> <para> The attentive reader may have noticed, that we save the applications state, but that we have no code to restore it. This has to be changed!</para> <para> The following code snippets should be inserted directly after the <function>gtk_signal_connect</function> calls, that we inserted at the beginning of the session management chapter.</para> <para> To restore the saved state, we use the cloned client. We get access to this client, by calling <function>gnome_cloned_client</function>. The creation of the cloned client is handled by the GNOME libraries. Our application was only restarted by a session manager, if <function>gnome_cloned_client</function> returns a non NULL value. That means, that, if <function>gnome_cloned_client</function> returns NULL, we don't have to restore any state, because there is no state to restore.</para> <programlisting> if (GNOME_CLIENT_CONNECTED (client)) { GnomeClient *cloned= gnome_cloned_client (); if (cloned) { restarted= 1;</programlisting> <para> We now again use <function>gnome_client_get_config_prefix</function> to get a hint, where to find our saved state. Notice, that we use the cloned client when restoring and the master client when saving the state. The reason for this is, that the cloned client's id may be another then the master client's one, and so the config prefixes differ. This may happen, if you clone a client (something, that's not yet supported by the <application>gnome-session</application> session management server). </para> <programlisting> gnome_config_push_prefix (gnome_client_get_config_prefix (cloned)); os_x = gnome_config_get_int ("Geometry/x"); os_y = gnome_config_get_int ("Geometry/y"); os_w = gnome_config_get_int ("Geometry/w"); os_h = gnome_config_get_int ("Geometry/h"); gnome_config_pop_prefix (); } }</programlisting> <para> Additionally the following lines should be included into the <function>prepare_app</function> function, to really set the values, we just red:</para> <programlisting> if (restarted) { gtk_widget_set_uposition (app, os_x, os_y); gtk_widget_set_usize (app, os_w, os_h); }</programlisting> <para> We have still not finished this tutorial's chapter, because right now, saving the application's state would fill our harddisc. So we have to remove our config files, if they are not needed anymore. This is supported by the session manager, using the discard command property.</para> <para> We have to add a new command line option to our application, that discards a save state (Hold you breath, I'll add the code soon). Additionally, we have to inform the session manager, to call us with this command line option to discard our state. This is easily be done by using the <function>gnome_client_set_discard_command</function> function. The following lines, inserted somewhere in the <function>save_yourself</function> function, do exactly what we want.</para> <programlisting> argv[0] = program_invocation_name; argv[1] = "--discard-session"; argv[2] = gnome_client_get_config_prefix (client); gnome_client_set_discard_command (client, 3, argv);</programlisting> <para> still to be continued...</para> </sect2> </sect1> <sect1 id="tut-doc"> <title>Help and documentation.</title> <para> I think that Mark would like writing this section... [FIXME: Mark, do you want do it?] </para> </sect1> </chapter> <chapter id="arch"> <title>Architecture notes.</title> <para> [FIXME: I think that we could include here the notes that now are in the website.] </para> </chapter> <chapter id="libgnome"> <title>GNOME library.</title> <para> This chapter is intended to be a reference to the libgnome. </para> &libgnome; </chapter> <chapter id="libgnomeui"> <title>GNOME User Interface library.</title> <para> This chapter is intended to be a reference to the libgnomeui. </para> &libgnomeui; </chapter> <!-- Documentation for GnomeAppHelper --> &gnome-app-helper; <!-- Documentation for the GnomeCanvas --> &gnome-canvas; <chapter id="DialogAppUtil-docs"> <title>Talking to the user: <type>GnomeDialog</type>, <type>GnomeMessageBox</type>, <type>GnomeAppBar</type>, and utility functions.</title> &gnome-message-utils; </sect2> </sect1> </chapter> <!-- Documentation on basic X concepts --> &x-concepts; <chapter id="libgnorba-docs"> <title>LibGnorba Documentation</title> &libgnorba; </chapter> <chapter id="gnome-mdi-docs"> <title>Gnome-MDI (Gnome Multi Document Interface)</title> &gnome-mdi; </chapter> </book>