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 "egg-sm-client.h"
26 #include "egg-sm-client-private.h"
28 /* pwi 2009-11-23 disable this specific log handler */
29 /*static void egg_sm_client_debug_handler (const char *log_domain,
30 GLogLevelFlags log_level,
32 gpointer user_data);*/
42 static guint signals
[LAST_SIGNAL
];
44 struct _EggSMClientPrivate
{
48 #define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate))
50 G_DEFINE_TYPE (EggSMClient
, egg_sm_client
, G_TYPE_OBJECT
)
52 static EggSMClient
*global_client
;
53 static EggSMClientMode global_client_mode
= EGG_SM_CLIENT_MODE_NORMAL
;
56 egg_sm_client_init (EggSMClient
*client
)
62 egg_sm_client_class_init (EggSMClientClass
*klass
)
64 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
66 g_type_class_add_private (klass
, sizeof (EggSMClientPrivate
));
69 * EggSMClient::save_state:
71 * @state_file: a #GKeyFile to save state information into
73 * Emitted when the session manager has requested that the
74 * application save information about its current state. The
75 * application should save its state into @state_file, and then the
76 * session manager may then restart the application in a future
77 * session and tell it to initialize itself from that state.
79 * You should not save any data into @state_file's "start group"
80 * (ie, the %NULL group). Instead, applications should save their
81 * data into groups with names that start with the application name,
82 * and libraries that connect to this signal should save their data
83 * into groups with names that start with the library name.
85 * Alternatively, rather than (or in addition to) using @state_file,
86 * the application can save its state by calling
87 * egg_sm_client_set_restart_command() during the processing of this
88 * signal (eg, to include a list of files to open).
91 g_signal_new ("save_state",
92 G_OBJECT_CLASS_TYPE (object_class
),
94 G_STRUCT_OFFSET (EggSMClientClass
, save_state
),
96 g_cclosure_marshal_VOID__POINTER
,
101 * EggSMClient::quit_requested:
102 * @client: the client
104 * Emitted when the session manager requests that the application
105 * exit (generally because the user is logging out). The application
106 * should decide whether or not it is willing to quit (perhaps after
107 * asking the user what to do with documents that have unsaved
108 * changes) and then call egg_sm_client_will_quit(), passing %TRUE
109 * or %FALSE to give its answer to the session manager. (It does not
110 * need to give an answer before returning from the signal handler;
111 * it can interact with the user asynchronously and then give its
112 * answer later on.) If the application does not connect to this
113 * signal, then #EggSMClient will automatically return %TRUE on its
116 * The application should not save its session state as part of
117 * handling this signal; if the user has requested that the session
118 * be saved when logging out, then ::save_state will be emitted
121 * If the application agrees to quit, it should then wait for either
122 * the ::quit_cancelled or ::quit signals to be emitted.
124 signals
[QUIT_REQUESTED
] =
125 g_signal_new ("quit_requested",
126 G_OBJECT_CLASS_TYPE (object_class
),
128 G_STRUCT_OFFSET (EggSMClientClass
, quit_requested
),
130 g_cclosure_marshal_VOID__VOID
,
135 * EggSMClient::quit_cancelled:
136 * @client: the client
138 * Emitted when the session manager decides to cancel a logout after
139 * the application has already agreed to quit. After receiving this
140 * signal, the application can go back to what it was doing before
141 * receiving the ::quit_requested signal.
143 signals
[QUIT_CANCELLED
] =
144 g_signal_new ("quit_cancelled",
145 G_OBJECT_CLASS_TYPE (object_class
),
147 G_STRUCT_OFFSET (EggSMClientClass
, quit_cancelled
),
149 g_cclosure_marshal_VOID__VOID
,
155 * @client: the client
157 * Emitted when the session manager wants the application to quit
158 * (generally because the user is logging out). The application
159 * should exit as soon as possible after receiving this signal; if
160 * it does not, the session manager may choose to forcibly kill it.
162 * Normally a GUI application would only be sent a ::quit if it
163 * agreed to quit in response to a ::quit_requested signal. However,
164 * this is not guaranteed; in some situations the session manager
165 * may decide to end the session without giving applications a
169 g_signal_new ("quit",
170 G_OBJECT_CLASS_TYPE (object_class
),
172 G_STRUCT_OFFSET (EggSMClientClass
, quit
),
174 g_cclosure_marshal_VOID__VOID
,
179 static gboolean sm_client_disable
= FALSE
;
180 static gboolean has_startup_run
= FALSE
;
181 static char *sm_client_state_file
= NULL
;
182 static char *sm_client_id
= NULL
;
183 static char *sm_config_prefix
= NULL
;
186 sm_client_post_parse_func (GOptionContext
*context
,
191 egg_sm_client_startup();
197 egg_sm_client_startup (void)
199 if( has_startup_run
)
202 EggSMClient
*client
= egg_sm_client_get ();
204 if (sm_client_id
== NULL
)
206 const gchar
*desktop_autostart_id
;
208 desktop_autostart_id
= g_getenv ("DESKTOP_AUTOSTART_ID");
210 if (desktop_autostart_id
!= NULL
)
211 sm_client_id
= g_strdup (desktop_autostart_id
);
214 /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
215 * use the same client id. */
216 g_unsetenv ("DESKTOP_AUTOSTART_ID");
218 if (EGG_SM_CLIENT_GET_CLASS (client
)->startup
)
219 EGG_SM_CLIENT_GET_CLASS (client
)->startup (client
, sm_client_id
);
221 has_startup_run
= TRUE
;
225 * egg_sm_client_get_option_group:
227 * Creates a %GOptionGroup containing the session-management-related
228 * options. You should add this group to the application's
229 * %GOptionContext if you want to use #EggSMClient.
231 * Return value: the %GOptionGroup
234 egg_sm_client_get_option_group (void)
236 const GOptionEntry entries
[] = {
237 { "sm-client-disable", 0, 0,
238 G_OPTION_ARG_NONE
, &sm_client_disable
,
239 N_("Disable connection to session manager"), NULL
},
240 { "sm-client-state-file", 0, 0,
241 G_OPTION_ARG_FILENAME
, &sm_client_state_file
,
242 N_("Specify file containing saved configuration"), N_("FILE") },
243 { "sm-client-id", 0, 0,
244 G_OPTION_ARG_STRING
, &sm_client_id
,
245 N_("Specify session management ID"), N_("ID") },
246 /* GnomeClient compatibility option */
247 { "sm-disable", 0, G_OPTION_FLAG_HIDDEN
,
248 G_OPTION_ARG_NONE
, &sm_client_disable
,
250 /* GnomeClient compatibility option. This is a dummy option that only
251 * exists so that sessions saved by apps with GnomeClient can be restored
252 * later when they've switched to EggSMClient. See bug #575308.
254 { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN
,
255 G_OPTION_ARG_STRING
, &sm_config_prefix
,
261 /* Use our own debug handler for the "EggSMClient" domain. */
262 /* pwi 2009-11-23 disable this specific log handler */
263 /* g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
264 egg_sm_client_debug_handler, NULL);*/
266 group
= g_option_group_new ("sm-client",
267 _("Session management options:"),
268 _("Show session management options"),
270 g_option_group_add_entries (group
, entries
);
271 g_option_group_set_parse_hooks (group
, NULL
, sm_client_post_parse_func
);
277 * egg_sm_client_set_mode:
278 * @mode: an #EggSMClient mode
280 * Sets the "mode" of #EggSMClient as follows:
282 * %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
283 * disabled. The application will not even connect to the session
284 * manager. (egg_sm_client_get() will still return an #EggSMClient,
285 * but it will just be a dummy object.)
287 * %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
288 * the session manager (and thus will receive notification when the
289 * user is logging out, etc), but will request to not be
290 * automatically restarted with saved state in future sessions.
292 * %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
295 * This must be called before the application's main loop begins.
298 egg_sm_client_set_mode (EggSMClientMode mode
)
300 global_client_mode
= mode
;
304 * egg_sm_client_get_mode:
306 * Gets the global #EggSMClientMode. See egg_sm_client_set_mode()
309 * Return value: the global #EggSMClientMode
312 egg_sm_client_get_mode (void)
314 return global_client_mode
;
320 * Returns the master #EggSMClient for the application.
322 * On platforms that support saved sessions (ie, POSIX/X11), the
323 * application will only request to be restarted by the session
324 * manager if you call egg_set_desktop_file() to set an application
325 * desktop file. In particular, if the desktop file contains the key
328 * Return value: the master #EggSMClient.
331 egg_sm_client_get (void)
335 if (global_client_mode
!= EGG_SM_CLIENT_MODE_DISABLED
&&
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 g_debug( "egg_sm_client_get: egg_sm_client_xsmp_new" );
349 global_client
= egg_sm_client_xsmp_new ();
351 # ifdef EGG_SM_CLIENT_BACKEND_DBUS
353 global_client
= egg_sm_client_dbus_new ();
358 /* Fallback: create a dummy client, so that callers don't have
359 * to worry about a %NULL return value.
361 if (!global_client
) {
362 g_debug( "egg_sm_client_get: allocating dummy client" );
363 global_client
= g_object_new (EGG_TYPE_SM_CLIENT
, NULL
);
367 return global_client
;
371 * egg_sm_client_is_resumed:
372 * @client: the client
374 * Checks whether or not the current session has been resumed from
375 * a previous saved session. If so, the application should call
376 * egg_sm_client_get_state_file() and restore its state from the
377 * returned #GKeyFile.
379 * Return value: %TRUE if the session has been resumed
382 egg_sm_client_is_resumed (EggSMClient
*client
)
384 g_return_val_if_fail (client
== global_client
, FALSE
);
386 return sm_client_state_file
!= NULL
;
390 * egg_sm_client_get_state_file:
391 * @client: the client
393 * If the application was resumed by the session manager, this will
394 * return the #GKeyFile containing its state from the previous
397 * Note that other libraries and #EggSMClient itself may also store
398 * state in the key file, so if you call egg_sm_client_get_groups(),
399 * on it, the return value will likely include groups that you did not
400 * put there yourself. (It is also not guaranteed that the first
401 * group created by the application will still be the "start group"
402 * when it is resumed.)
404 * Return value: the #GKeyFile containing the application's earlier
405 * state, or %NULL on error. You should not free this key file; it
406 * is owned by @client.
409 egg_sm_client_get_state_file (EggSMClient
*client
)
411 EggSMClientPrivate
*priv
= EGG_SM_CLIENT_GET_PRIVATE (client
);
412 char *state_file_path
;
415 g_return_val_if_fail (client
== global_client
, NULL
);
417 if (!sm_client_state_file
)
419 if (priv
->state_file
)
420 return priv
->state_file
;
422 if (!strncmp (sm_client_state_file
, "file://", 7))
423 state_file_path
= g_filename_from_uri (sm_client_state_file
, NULL
, NULL
);
425 state_file_path
= g_strdup (sm_client_state_file
);
427 priv
->state_file
= g_key_file_new ();
428 if (!g_key_file_load_from_file (priv
->state_file
, state_file_path
, 0, &err
))
430 g_warning ("Could not load SM state file '%s': %s",
431 sm_client_state_file
, err
->message
);
432 g_clear_error (&err
);
433 g_key_file_free (priv
->state_file
);
434 priv
->state_file
= NULL
;
437 g_free (state_file_path
);
438 return priv
->state_file
;
442 * egg_sm_client_set_restart_command:
443 * @client: the client
444 * @argc: the length of @argv
445 * @argv: argument vector
447 * Sets the command used to restart @client if it does not have a
448 * .desktop file that can be used to find its restart command.
450 * This can also be used when handling the ::save_state signal, to
451 * save the current state via an updated command line. (Eg, providing
452 * a list of filenames to open when the application is resumed.)
455 egg_sm_client_set_restart_command (EggSMClient
*client
,
459 g_return_if_fail (EGG_IS_SM_CLIENT (client
));
461 if (EGG_SM_CLIENT_GET_CLASS (client
)->set_restart_command
)
462 EGG_SM_CLIENT_GET_CLASS (client
)->set_restart_command (client
, argc
, argv
);
466 * egg_sm_client_will_quit:
467 * @client: the client
468 * @will_quit: whether or not the application is willing to quit
470 * This MUST be called in response to the ::quit_requested signal, to
471 * indicate whether or not the application is willing to quit. The
472 * application may call it either directly from the signal handler, or
473 * at some later point (eg, after asynchronously interacting with the
476 * If the application does not connect to ::quit_requested,
477 * #EggSMClient will call this method on its behalf (passing %TRUE
480 * After calling this method, the application should wait to receive
481 * either ::quit_cancelled or ::quit.
484 egg_sm_client_will_quit (EggSMClient
*client
,
487 g_debug( "egg_sm_client_will_quit: will_quit=%s", will_quit
? "True":"False" );
489 g_return_if_fail (EGG_IS_SM_CLIENT (client
));
491 if (EGG_SM_CLIENT_GET_CLASS (client
)->will_quit
)
492 EGG_SM_CLIENT_GET_CLASS (client
)->will_quit (client
, will_quit
);
496 * egg_sm_client_end_session:
497 * @style: a hint at how to end the session
498 * @request_confirmation: whether or not the user should get a chance
499 * to confirm the action
501 * Requests that the session manager end the current session. @style
502 * indicates how the session should be ended, and
503 * @request_confirmation indicates whether or not the user should be
504 * given a chance to confirm the logout/reboot/shutdown. Both of these
505 * flags are merely hints though; the session manager may choose to
508 * Return value: %TRUE if the request was sent; %FALSE if it could not
509 * be (eg, because it could not connect to the session manager).
512 egg_sm_client_end_session (EggSMClientEndStyle style
,
513 gboolean request_confirmation
)
515 EggSMClient
*client
= egg_sm_client_get ();
517 g_debug( "egg_sm_client_end_session: request_confirmation=%s", request_confirmation
? "True":"False" );
518 g_return_val_if_fail (EGG_IS_SM_CLIENT (client
), FALSE
);
520 if (EGG_SM_CLIENT_GET_CLASS (client
)->end_session
)
522 return EGG_SM_CLIENT_GET_CLASS (client
)->end_session (client
, style
,
523 request_confirmation
);
529 /* Signal-emitting callbacks from platform-specific code */
532 egg_sm_client_save_state (EggSMClient
*client
)
534 GKeyFile
*state_file
;
537 g_return_val_if_fail (client
== global_client
, NULL
);
539 state_file
= g_key_file_new ();
541 g_debug ("Emitting save_state");
542 g_signal_emit (client
, signals
[SAVE_STATE
], 0, state_file
);
543 g_debug ("Done emitting save_state");
545 group
= g_key_file_get_start_group (state_file
);
553 g_key_file_free (state_file
);
559 egg_sm_client_quit_requested (EggSMClient
*client
)
561 g_debug( "egg_sm_client_quit_requested: client=%p", ( void * ) client
);
562 g_return_if_fail (client
== global_client
);
564 if (!g_signal_has_handler_pending (client
, signals
[QUIT_REQUESTED
], 0, FALSE
))
566 g_debug ("Not emitting quit_requested because no one is listening");
567 egg_sm_client_will_quit (client
, TRUE
);
571 g_debug ("Emitting quit_requested");
572 g_signal_emit (client
, signals
[QUIT_REQUESTED
], 0);
573 g_debug ("Done emitting quit_requested");
577 egg_sm_client_quit_cancelled (EggSMClient
*client
)
579 g_debug( "egg_sm_client_quit_cancelled: client=%p", ( void * ) client
);
580 g_return_if_fail (client
== global_client
);
582 g_debug ("Emitting quit_cancelled");
583 g_signal_emit (client
, signals
[QUIT_CANCELLED
], 0);
584 g_debug ("Done emitting quit_cancelled");
588 egg_sm_client_quit (EggSMClient
*client
)
590 g_debug( "egg_sm_client_quit: client=%p", ( void * ) client
);
591 g_return_if_fail (client
== global_client
);
593 g_debug ("Emitting quit");
594 g_signal_emit (client
, signals
[QUIT
], 0);
595 g_debug ("Done emitting quit");
597 /* FIXME: should we just call gtk_main_quit() here? */
600 /* pwi 2009-11-23 disable this specific log handler */
602 egg_sm_client_debug_handler (const char *log_domain,
603 GLogLevelFlags log_level,
607 static int debug = -1;
610 debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
613 g_log_default_handler (log_domain, log_level, message, NULL);