2
* Copyright (C) 2007 Novell, Inc.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
23
#include <glib/gi18n.h>
25
#include "eggsmclient.h"
26
#include "eggsmclient-private.h"
28
static void egg_sm_client_debug_handler (const char *log_domain,
29
GLogLevelFlags log_level,
41
static guint signals[LAST_SIGNAL];
43
struct _EggSMClientPrivate {
47
#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate))
49
G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
51
static EggSMClient *global_client;
52
static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
55
egg_sm_client_init (EggSMClient *client)
61
egg_sm_client_class_init (EggSMClientClass *klass)
63
GObjectClass *object_class = G_OBJECT_CLASS (klass);
65
g_type_class_add_private (klass, sizeof (EggSMClientPrivate));
68
* EggSMClient::save_state:
70
* @state_file: a #GKeyFile to save state information into
72
* Emitted when the session manager has requested that the
73
* application save information about its current state. The
74
* application should save its state into @state_file, and then the
75
* session manager may then restart the application in a future
76
* session and tell it to initialize itself from that state.
78
* You should not save any data into @state_file's "start group"
79
* (ie, the %NULL group). Instead, applications should save their
80
* data into groups with names that start with the application name,
81
* and libraries that connect to this signal should save their data
82
* into groups with names that start with the library name.
84
* Alternatively, rather than (or in addition to) using @state_file,
85
* the application can save its state by calling
86
* egg_sm_client_set_restart_command() during the processing of this
87
* signal (eg, to include a list of files to open).
90
g_signal_new ("save_state",
91
G_OBJECT_CLASS_TYPE (object_class),
93
G_STRUCT_OFFSET (EggSMClientClass, save_state),
95
g_cclosure_marshal_VOID__POINTER,
100
* EggSMClient::quit_requested:
101
* @client: the client
103
* Emitted when the session manager requests that the application
104
* exit (generally because the user is logging out). The application
105
* should decide whether or not it is willing to quit (perhaps after
106
* asking the user what to do with documents that have unsaved
107
* changes) and then call egg_sm_client_will_quit(), passing %TRUE
108
* or %FALSE to give its answer to the session manager. (It does not
109
* need to give an answer before returning from the signal handler;
110
* it can interact with the user asynchronously and then give its
111
* answer later on.) If the application does not connect to this
112
* signal, then #EggSMClient will automatically return %TRUE on its
115
* The application should not save its session state as part of
116
* handling this signal; if the user has requested that the session
117
* be saved when logging out, then ::save_state will be emitted
120
* If the application agrees to quit, it should then wait for either
121
* the ::quit_cancelled or ::quit signals to be emitted.
123
signals[QUIT_REQUESTED] =
124
g_signal_new ("quit_requested",
125
G_OBJECT_CLASS_TYPE (object_class),
127
G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
129
g_cclosure_marshal_VOID__VOID,
134
* EggSMClient::quit_cancelled:
135
* @client: the client
137
* Emitted when the session manager decides to cancel a logout after
138
* the application has already agreed to quit. After receiving this
139
* signal, the application can go back to what it was doing before
140
* receiving the ::quit_requested signal.
142
signals[QUIT_CANCELLED] =
143
g_signal_new ("quit_cancelled",
144
G_OBJECT_CLASS_TYPE (object_class),
146
G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
148
g_cclosure_marshal_VOID__VOID,
154
* @client: the client
156
* Emitted when the session manager wants the application to quit
157
* (generally because the user is logging out). The application
158
* should exit as soon as possible after receiving this signal; if
159
* it does not, the session manager may choose to forcibly kill it.
161
* Normally a GUI application would only be sent a ::quit if it
162
* agreed to quit in response to a ::quit_requested signal. However,
163
* this is not guaranteed; in some situations the session manager
164
* may decide to end the session without giving applications a
168
g_signal_new ("quit",
169
G_OBJECT_CLASS_TYPE (object_class),
171
G_STRUCT_OFFSET (EggSMClientClass, quit),
173
g_cclosure_marshal_VOID__VOID,
178
static gboolean sm_client_disable = FALSE;
179
static char *sm_client_state_file = NULL;
180
static char *sm_client_id = NULL;
181
static char *sm_config_prefix = NULL;
184
sm_client_post_parse_func (GOptionContext *context,
189
EggSMClient *client = egg_sm_client_get ();
191
if (sm_client_id == NULL)
193
const gchar *desktop_autostart_id;
195
desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
197
if (desktop_autostart_id != NULL)
198
sm_client_id = g_strdup (desktop_autostart_id);
201
/* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
202
* use the same client id. */
203
g_unsetenv ("DESKTOP_AUTOSTART_ID");
205
if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
206
EGG_SM_CLIENT_GET_CLASS (client)->startup)
207
EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
212
* egg_sm_client_get_option_group:
214
* Creates a %GOptionGroup containing the session-management-related
215
* options. You should add this group to the application's
216
* %GOptionContext if you want to use #EggSMClient.
218
* Return value: the %GOptionGroup
221
egg_sm_client_get_option_group (void)
223
const GOptionEntry entries[] = {
224
{ "sm-client-disable", 0, 0,
225
G_OPTION_ARG_NONE, &sm_client_disable,
226
N_("Disable connection to session manager"), NULL },
227
{ "sm-client-state-file", 0, 0,
228
G_OPTION_ARG_FILENAME, &sm_client_state_file,
229
N_("Specify file containing saved configuration"), N_("FILE") },
230
{ "sm-client-id", 0, 0,
231
G_OPTION_ARG_STRING, &sm_client_id,
232
N_("Specify session management ID"), N_("ID") },
233
/* GnomeClient compatibility option */
234
{ "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
235
G_OPTION_ARG_NONE, &sm_client_disable,
237
/* GnomeClient compatibility option. This is a dummy option that only
238
* exists so that sessions saved by apps with GnomeClient can be restored
239
* later when they've switched to EggSMClient. See bug #575308.
241
{ "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
242
G_OPTION_ARG_STRING, &sm_config_prefix,
248
/* Use our own debug handler for the "EggSMClient" domain. */
249
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
250
egg_sm_client_debug_handler, NULL);
252
group = g_option_group_new ("sm-client",
253
_("Session management options:"),
254
_("Show session management options"),
256
g_option_group_add_entries (group, entries);
257
g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func);
263
* egg_sm_client_set_mode:
264
* @mode: an #EggSMClient mode
266
* Sets the "mode" of #EggSMClient as follows:
268
* %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
269
* disabled, until the mode is changed again. The application will
270
* not even connect to the session manager. (egg_sm_client_get()
271
* will still return an #EggSMClient object.)
273
* %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
274
* the session manager (and thus will receive notification when the
275
* user is logging out, etc), but will request to not be
276
* automatically restarted with saved state in future sessions.
278
* %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
281
* This must be called before the application's main loop begins and
282
* before any call to egg_sm_client_get(), unless the mode was set
283
* earlier to %EGG_SM_CLIENT_MODE_DISABLED and this call enables
284
* session management. Note that option parsing will call
285
* egg_sm_client_get().
288
egg_sm_client_set_mode (EggSMClientMode mode)
290
EggSMClientMode old_mode = global_client_mode;
292
g_return_if_fail (global_client == NULL || global_client_mode == EGG_SM_CLIENT_MODE_DISABLED);
293
g_return_if_fail (!(global_client != NULL && mode == EGG_SM_CLIENT_MODE_DISABLED));
295
global_client_mode = mode;
297
if (global_client != NULL && old_mode == EGG_SM_CLIENT_MODE_DISABLED)
299
if (EGG_SM_CLIENT_GET_CLASS (global_client)->startup)
300
EGG_SM_CLIENT_GET_CLASS (global_client)->startup (global_client, sm_client_id);
305
* egg_sm_client_get_mode:
307
* Gets the global #EggSMClientMode. See egg_sm_client_set_mode()
310
* Return value: the global #EggSMClientMode
313
egg_sm_client_get_mode (void)
315
return global_client_mode;
321
* Returns the master #EggSMClient for the application.
323
* On platforms that support saved sessions (ie, POSIX/X11), the
324
* application will only request to be restarted by the session
325
* manager if you call egg_set_desktop_file() to set an application
326
* desktop file. In particular, if the desktop file contains the key
329
* Return value: the master #EggSMClient.
332
egg_sm_client_get (void)
336
if (!sm_client_disable)
338
#if defined (GDK_WINDOWING_WIN32)
339
global_client = egg_sm_client_win32_new ();
340
#elif defined (GDK_WINDOWING_QUARTZ)
341
global_client = egg_sm_client_osx_new ();
343
/* If both D-Bus and XSMP are compiled in, try XSMP first
344
* (since it supports state saving) and fall back to D-Bus
345
* if XSMP isn't available.
347
# ifdef EGG_SM_CLIENT_BACKEND_XSMP
348
global_client = egg_sm_client_xsmp_new ();
350
# ifdef EGG_SM_CLIENT_BACKEND_DBUS
352
global_client = egg_sm_client_dbus_new ();
357
/* Fallback: create a dummy client, so that callers don't have
358
* to worry about a %NULL return value.
361
global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL);
364
return global_client;
368
* egg_sm_client_is_resumed:
369
* @client: the client
371
* Checks whether or not the current session has been resumed from
372
* a previous saved session. If so, the application should call
373
* egg_sm_client_get_state_file() and restore its state from the
374
* returned #GKeyFile.
376
* Return value: %TRUE if the session has been resumed
379
egg_sm_client_is_resumed (EggSMClient *client)
381
g_return_val_if_fail (client == global_client, FALSE);
383
return sm_client_state_file != NULL;
387
* egg_sm_client_get_state_file:
388
* @client: the client
390
* If the application was resumed by the session manager, this will
391
* return the #GKeyFile containing its state from the previous
394
* Note that other libraries and #EggSMClient itself may also store
395
* state in the key file, so if you call egg_sm_client_get_groups(),
396
* on it, the return value will likely include groups that you did not
397
* put there yourself. (It is also not guaranteed that the first
398
* group created by the application will still be the "start group"
399
* when it is resumed.)
401
* Return value: the #GKeyFile containing the application's earlier
402
* state, or %NULL on error. You should not free this key file; it
403
* is owned by @client.
406
egg_sm_client_get_state_file (EggSMClient *client)
408
EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client);
409
char *state_file_path;
412
g_return_val_if_fail (client == global_client, NULL);
414
if (!sm_client_state_file)
416
if (priv->state_file)
417
return priv->state_file;
419
if (!strncmp (sm_client_state_file, "file://", 7))
420
state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL);
422
state_file_path = g_strdup (sm_client_state_file);
424
priv->state_file = g_key_file_new ();
425
if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err))
427
g_warning ("Could not load SM state file '%s': %s",
428
sm_client_state_file, err->message);
429
g_clear_error (&err);
430
g_key_file_free (priv->state_file);
431
priv->state_file = NULL;
434
g_free (state_file_path);
435
return priv->state_file;
439
* egg_sm_client_set_restart_command:
440
* @client: the client
441
* @argc: the length of @argv
442
* @argv: argument vector
444
* Sets the command used to restart @client if it does not have a
445
* .desktop file that can be used to find its restart command.
447
* This can also be used when handling the ::save_state signal, to
448
* save the current state via an updated command line. (Eg, providing
449
* a list of filenames to open when the application is resumed.)
452
egg_sm_client_set_restart_command (EggSMClient *client,
456
g_return_if_fail (EGG_IS_SM_CLIENT (client));
458
if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command)
459
EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv);
463
* egg_sm_client_will_quit:
464
* @client: the client
465
* @will_quit: whether or not the application is willing to quit
467
* This MUST be called in response to the ::quit_requested signal, to
468
* indicate whether or not the application is willing to quit. The
469
* application may call it either directly from the signal handler, or
470
* at some later point (eg, after asynchronously interacting with the
473
* If the application does not connect to ::quit_requested,
474
* #EggSMClient will call this method on its behalf (passing %TRUE
477
* After calling this method, the application should wait to receive
478
* either ::quit_cancelled or ::quit.
481
egg_sm_client_will_quit (EggSMClient *client,
484
g_return_if_fail (EGG_IS_SM_CLIENT (client));
486
if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit)
487
EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit);
491
* egg_sm_client_end_session:
492
* @style: a hint at how to end the session
493
* @request_confirmation: whether or not the user should get a chance
494
* to confirm the action
496
* Requests that the session manager end the current session. @style
497
* indicates how the session should be ended, and
498
* @request_confirmation indicates whether or not the user should be
499
* given a chance to confirm the logout/reboot/shutdown. Both of these
500
* flags are merely hints though; the session manager may choose to
503
* Return value: %TRUE if the request was sent; %FALSE if it could not
504
* be (eg, because it could not connect to the session manager).
507
egg_sm_client_end_session (EggSMClientEndStyle style,
508
gboolean request_confirmation)
510
EggSMClient *client = egg_sm_client_get ();
512
g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE);
514
if (EGG_SM_CLIENT_GET_CLASS (client)->end_session)
516
return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style,
517
request_confirmation);
523
/* Signal-emitting callbacks from platform-specific code */
526
egg_sm_client_save_state (EggSMClient *client)
528
GKeyFile *state_file;
531
g_return_val_if_fail (client == global_client, NULL);
533
state_file = g_key_file_new ();
535
g_debug ("Emitting save_state");
536
g_signal_emit (client, signals[SAVE_STATE], 0, state_file);
537
g_debug ("Done emitting save_state");
539
group = g_key_file_get_start_group (state_file);
547
g_key_file_free (state_file);
553
egg_sm_client_quit_requested (EggSMClient *client)
555
g_return_if_fail (client == global_client);
557
if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE))
559
g_debug ("Not emitting quit_requested because no one is listening");
560
egg_sm_client_will_quit (client, TRUE);
564
g_debug ("Emitting quit_requested");
565
g_signal_emit (client, signals[QUIT_REQUESTED], 0);
566
g_debug ("Done emitting quit_requested");
570
egg_sm_client_quit_cancelled (EggSMClient *client)
572
g_return_if_fail (client == global_client);
574
g_debug ("Emitting quit_cancelled");
575
g_signal_emit (client, signals[QUIT_CANCELLED], 0);
576
g_debug ("Done emitting quit_cancelled");
580
egg_sm_client_quit (EggSMClient *client)
582
g_return_if_fail (client == global_client);
584
g_debug ("Emitting quit");
585
g_signal_emit (client, signals[QUIT], 0);
586
g_debug ("Done emitting quit");
588
/* FIXME: should we just call gtk_main_quit() here? */
592
egg_sm_client_debug_handler (const char *log_domain,
593
GLogLevelFlags log_level,
597
static int debug = -1;
600
debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
603
g_log_default_handler (log_domain, log_level, message, NULL);