Gnome Developers' Information
Prev | Chapter 3. GNOME Developer's Tutorial. | Next

# Session Management

Warning
This chapter is definitely under construction, But hold your breath, it will (hopefully) be finished soon.

## Session Management in general

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.

Every application that is session management aware connects to one special server: the session manager. A session manager sends commands to his clients 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 client id.

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.

## GNOME Session Management implementation

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.

There are two functions in the GNOME libraries, that create a new GnomeClient object:

`GnomeClient *gnome_client_new`();

`GnomeClient *gnome_client_new_without_connection`();

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:

`void gnome_client_connect`(GnomeClient *client);

`void gnome_client_disconnect`(GnomeClient *client);

`gchar *gnome_client_get_id`(GnomeClient *client);

`void gnome_client_set_id`(GnomeClient *client, const gchar *client_id);

to be continued...

### Properties

### Signals

Whenever the session manager wants a client to do something, these wishes are emitted as signals.

`gint save_yourself_signal`(GnomeClient *client, gint phase, GnomeSaveStyle save_style, gint shutdown, GnomeInteractStyle interact_style, gint fast, gpointer client_data);

This signal is probably the most important one, because it causes the clients to save the programs state.

`save_style`.

The `shutdown` parameter indicates, whether this

GNOME_INTERACT_NONE, GNOME_INTERACT_ERRORS, GNOME_INTERACT_ANY

If the `fast` parameter is TRUE, the client is wanted to save it's state as fast as possible.

`gint die_signal`(GnomeClient *client, gpointer client_data);

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.

`gint save_complete_signal`(GnomeClient *client, gpointer client_data);

The session manager sends this message, if all client have finished saving their state.

`gint shutdown_cancelled_signal`(GnomeClient *client, gpointer client_data);

Whenever a shutdown, that has been announced in a save_yourself signal, has been cancelled by the user, the shutdown_cancelled message is sent.

`gint connect_signal`(GnomeClient *client, gint restarted, gpointer client_data);

`gint disconnect_signal`(GnomeClient *client, gpointer client_data);

This signal is emitted, if the connection to the session manager gets lost.

### The master client

To make life a little bit easier for GNOME developers, the GNOME libraries feature a special client: the master client. This client is created automatically, when calling `gnome_init`. 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 `gnome_init` 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.

Your get the master client by calling `gnome_master_client`.

`GnomeClient *gnome_master_client`();

The master client has the following properties preset.

property | value
--- | ---
current directory | current directory
process id | pid
user id | uid

### The cloned client

## Continuing the tutorial

We want to implement session management, so we have to use the master client. To get access to this client, that was created in `gnome_init`, you have to call `gnome_master_client`. Our application should at least listen to the die and the save_yourself signal, so insert the following lines of code right after the `gnome_init` call.

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

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.

    static gint
    die (GnomeClient *client, gpointer client_data)
    {
      gtk_exit (0);

      return FALSE;
    }

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:

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

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 `gnome_client_get_config_prefix` gives us a hint , where to store our information.

      /* 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();

Additionally we have to give the session manager some hints, how to restart our application. That means, we have to use the `gnome_client_set_clone_command` and `gnome_client_set_restart_command` 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.

      gnome_client_set_clone_command (client, 1, argv);
      gnome_client_set_restart_command (client, 1, argv);

      return TRUE;
    }

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!

The following code snippets should be inserted directly after the `gtk_signal_connect` calls, that we inserted at the beginning of the session management chapter.

To restore the saved state, we use the cloned client. We get access to this client, by calling `gnome_cloned_client`. The creation of the cloned client is handled by the GNOME libraries. Our application was only restarted by a session manager, if `gnome_cloned_client` returns a non NULL value. That means, that, if `gnome_cloned_client` returns NULL, we don't have to restore any state, because there is no state to restore.

      if (GNOME_CLIENT_CONNECTED (client))
        {
          GnomeClient *cloned= gnome_cloned_client ();

          if (cloned)
    	{
    	  restarted= 1;

We now again use `gnome_client_get_config_prefix` 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 gnome-session session management server).

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

Additionally the following lines should be included into the `prepare_app` function, to really set the values, we just red:

      if (restarted) {
        gtk_widget_set_uposition (app, os_x, os_y);
        gtk_widget_set_usize     (app, os_w, os_h);
      }

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.

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 `gnome_client_set_discard_command` function. The following lines, inserted somewhere in the `save_yourself` function, do exactly what we want.

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

still to be continued...

Prev | Home | Next
Parsing parameters. | Up | Help and documentation.