Whole document tree
    

Whole document tree

Session Management

3.5. Session Management

Warning

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

3.5.1. 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.

3.5.2. 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:

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:

to be continued...

3.5.3. 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...