2 * Copyright (C) 2007 Novell, Inc.
4 * Inspired by various other pieces of code including GsmClient (C)
5 * 2001 Havoc Pennington, GnomeClient (C) 1998 Carsten Schaar, and twm
6 * session code (C) 1998 The Open Group.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
26 #include "eggsmclient.h"
27 #include "eggsmclient-private.h"
29 #include "eggdesktopfile.h"
36 #include <X11/SM/SMlib.h>
41 #define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
42 #define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
43 #define EGG_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
44 #define EGG_IS_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP))
45 #define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP))
46 #define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
48 typedef struct _EggSMClientXSMP EggSMClientXSMP;
49 typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass;
51 /* These mostly correspond to the similarly-named states in section
52 * 9.1 of the XSMP spec. Some of the states there aren't represented
53 * here, because we don't need them. SHUTDOWN_CANCELLED is slightly
54 * different from the spec; we use it when the client is IDLE after a
55 * ShutdownCancelled message, but the application is still interacting
56 * and doesn't know the shutdown has been cancelled yet.
61 XSMP_STATE_SAVE_YOURSELF,
62 XSMP_STATE_INTERACT_REQUEST,
64 XSMP_STATE_SAVE_YOURSELF_DONE,
65 XSMP_STATE_SHUTDOWN_CANCELLED,
66 XSMP_STATE_CONNECTION_CLOSED
67 } EggSMClientXSMPState;
69 static const char *state_names[] = {
79 #define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
81 struct _EggSMClientXSMP
88 EggSMClientXSMPState state;
89 char **restart_command;
90 gboolean set_restart_command;
95 /* Current SaveYourself state */
96 guint expecting_initial_save_yourself : 1;
97 guint need_save_state : 1;
98 guint need_quit_requested : 1;
99 guint interact_errors : 1;
100 guint shutting_down : 1;
103 guint waiting_to_set_initial_properties : 1;
104 guint waiting_to_emit_quit : 1;
105 guint waiting_to_emit_quit_cancelled : 1;
106 guint waiting_to_save_myself : 1;
110 struct _EggSMClientXSMPClass
112 EggSMClientClass parent_class;
116 static void sm_client_xsmp_startup (EggSMClient *client,
117 const char *client_id);
118 static void sm_client_xsmp_set_restart_command (EggSMClient *client,
121 static void sm_client_xsmp_will_quit (EggSMClient *client,
123 static gboolean sm_client_xsmp_end_session (EggSMClient *client,
124 EggSMClientEndStyle style,
125 gboolean request_confirmation);
127 static void xsmp_save_yourself (SmcConn smc_conn,
128 SmPointer client_data,
133 static void xsmp_die (SmcConn smc_conn,
134 SmPointer client_data);
135 static void xsmp_save_complete (SmcConn smc_conn,
136 SmPointer client_data);
137 static void xsmp_shutdown_cancelled (SmcConn smc_conn,
138 SmPointer client_data);
139 static void xsmp_interact (SmcConn smc_conn,
140 SmPointer client_data);
142 static SmProp *array_prop (const char *name,
144 static SmProp *ptrarray_prop (const char *name,
146 static SmProp *string_prop (const char *name,
148 static SmProp *card8_prop (const char *name,
149 unsigned char value);
151 static void set_properties (EggSMClientXSMP *xsmp, ...);
152 static void delete_properties (EggSMClientXSMP *xsmp, ...);
154 static GPtrArray *generate_command (char **restart_command,
155 const char *client_id,
156 const char *state_file);
158 static void save_state (EggSMClientXSMP *xsmp);
159 static void do_save_yourself (EggSMClientXSMP *xsmp);
160 static void update_pending_events (EggSMClientXSMP *xsmp);
162 static void ice_init (void);
163 static gboolean process_ice_messages (IceConn ice_conn);
164 static void smc_error_handler (SmcConn smc_conn,
166 int offending_minor_opcode,
167 unsigned long offending_sequence,
172 G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
175 egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
177 xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
178 xsmp->connection = NULL;
179 xsmp->restart_style = SmRestartIfRunning;
183 egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
185 EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
187 sm_client_class->startup = sm_client_xsmp_startup;
188 sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
189 sm_client_class->will_quit = sm_client_xsmp_will_quit;
190 sm_client_class->end_session = sm_client_xsmp_end_session;
194 egg_sm_client_xsmp_new (void)
196 if (!g_getenv ("SESSION_MANAGER"))
199 return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
203 sm_client_xsmp_set_initial_properties (gpointer user_data)
205 EggSMClientXSMP *xsmp = user_data;
206 EggDesktopFile *desktop_file;
207 GPtrArray *clone, *restart;
212 g_source_remove (xsmp->idle);
215 xsmp->waiting_to_set_initial_properties = FALSE;
217 if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
218 xsmp->restart_style = SmRestartNever;
220 /* Parse info out of desktop file */
221 desktop_file = egg_get_desktop_file ();
225 char *cmdline, **argv;
228 if (xsmp->restart_style == SmRestartIfRunning)
230 if (egg_desktop_file_get_boolean (desktop_file,
231 "X-GNOME-AutoRestart", NULL))
232 xsmp->restart_style = SmRestartImmediately;
235 if (!xsmp->set_restart_command)
237 cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
238 if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
240 egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
241 argc, (const char **)argv);
246 g_warning ("Could not parse Exec line in desktop file: %s",
254 if (!xsmp->set_restart_command)
255 xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
257 clone = generate_command (xsmp->restart_command, NULL, NULL);
258 restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
260 g_debug ("Setting initial properties");
262 /* Program, CloneCommand, RestartCommand, and UserID are required.
263 * ProcessID isn't required, but the SM may be able to do something
266 g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
267 set_properties (xsmp,
268 string_prop (SmProgram, g_get_prgname ()),
269 ptrarray_prop (SmCloneCommand, clone),
270 ptrarray_prop (SmRestartCommand, restart),
271 string_prop (SmUserID, g_get_user_name ()),
272 string_prop (SmProcessID, pid_str),
273 card8_prop (SmRestartStyleHint, xsmp->restart_style),
275 g_ptr_array_free (clone, TRUE);
276 g_ptr_array_free (restart, TRUE);
280 set_properties (xsmp,
281 string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
285 update_pending_events (xsmp);
289 /* This gets called from two different places: xsmp_die() (when the
290 * server asks us to disconnect) and process_ice_messages() (when the
291 * server disconnects unexpectedly).
294 sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
298 if (!xsmp->connection)
301 g_debug ("Disconnecting");
303 connection = xsmp->connection;
304 xsmp->connection = NULL;
305 SmcCloseConnection (connection, 0, NULL);
306 xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
308 xsmp->waiting_to_save_myself = FALSE;
309 update_pending_events (xsmp);
313 sm_client_xsmp_startup (EggSMClient *client,
314 const char *client_id)
316 EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
317 SmcCallbacks callbacks;
319 char error_string_ret[256];
321 xsmp->client_id = g_strdup (client_id);
324 SmcSetErrorHandler (smc_error_handler);
326 callbacks.save_yourself.callback = xsmp_save_yourself;
327 callbacks.die.callback = xsmp_die;
328 callbacks.save_complete.callback = xsmp_save_complete;
329 callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
331 callbacks.save_yourself.client_data = xsmp;
332 callbacks.die.client_data = xsmp;
333 callbacks.save_complete.client_data = xsmp;
334 callbacks.shutdown_cancelled.client_data = xsmp;
337 error_string_ret[0] = '\0';
339 SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
340 SmcSaveYourselfProcMask | SmcDieProcMask |
341 SmcSaveCompleteProcMask |
342 SmcShutdownCancelledProcMask,
344 xsmp->client_id, &ret_client_id,
345 sizeof (error_string_ret), error_string_ret);
347 if (!xsmp->connection)
349 g_warning ("Failed to connect to the session manager: %s\n",
350 error_string_ret[0] ?
351 error_string_ret : "no error message given");
352 xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
356 /* We expect a pointless initial SaveYourself if either (a) we
357 * didn't have an initial client ID, or (b) we DID have an initial
358 * client ID, but the server rejected it and gave us a new one.
360 if (!xsmp->client_id ||
361 (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
362 xsmp->expecting_initial_save_yourself = TRUE;
366 g_free (xsmp->client_id);
367 xsmp->client_id = g_strdup (ret_client_id);
368 free (ret_client_id);
370 gdk_threads_enter ();
371 #if !GTK_CHECK_VERSION(2,91,7) && !GTK_CHECK_VERSION(3,0,0)
372 gdk_set_sm_client_id (xsmp->client_id);
374 gdk_x11_set_sm_client_id (xsmp->client_id);
376 gdk_threads_leave ();
378 g_debug ("Got client ID \"%s\"", xsmp->client_id);
381 xsmp->state = XSMP_STATE_IDLE;
383 /* Do not set the initial properties until we reach the main loop,
384 * so that the application has a chance to call
385 * egg_set_desktop_file(). (This may also help the session manager
386 * have a better idea of when the application is fully up and
389 xsmp->waiting_to_set_initial_properties = TRUE;
390 xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
394 sm_client_xsmp_set_restart_command (EggSMClient *client,
398 EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
401 g_strfreev (xsmp->restart_command);
403 xsmp->restart_command = g_new (char *, argc + 1);
404 for (i = 0; i < argc; i++)
405 xsmp->restart_command[i] = g_strdup (argv[i]);
406 xsmp->restart_command[i] = NULL;
408 xsmp->set_restart_command = TRUE;
412 sm_client_xsmp_will_quit (EggSMClient *client,
415 EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
417 if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
419 /* The session manager has already exited! Schedule a quit
422 xsmp->waiting_to_emit_quit = TRUE;
423 update_pending_events (xsmp);
426 else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
428 /* We received a ShutdownCancelled message while the application
429 * was interacting; Schedule a quit_cancelled signal.
431 xsmp->waiting_to_emit_quit_cancelled = TRUE;
432 update_pending_events (xsmp);
436 g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
438 g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
439 SmcInteractDone (xsmp->connection, !will_quit);
441 if (will_quit && xsmp->need_save_state)
444 g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
445 SmcSaveYourselfDone (xsmp->connection, will_quit);
446 xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
450 sm_client_xsmp_end_session (EggSMClient *client,
451 EggSMClientEndStyle style,
452 gboolean request_confirmation)
454 EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
457 /* To end the session via XSMP, we have to send a
458 * SaveYourselfRequest. We aren't allowed to do that if anything
459 * else is going on, but we don't want to expose this fact to the
460 * application. So we do our best to patch things up here...
462 * In the worst case, this method might block for some length of
463 * time in process_ice_messages, but the only time that code path is
464 * honestly likely to get hit is if the application tries to end the
465 * session as the very first thing it does, in which case it
466 * probably won't actually block anyway. It's not worth gunking up
467 * the API to try to deal nicely with the other 0.01% of cases where
471 while (xsmp->state != XSMP_STATE_IDLE ||
472 xsmp->expecting_initial_save_yourself)
474 /* If we're already shutting down, we don't need to do anything. */
475 if (xsmp->shutting_down)
480 case XSMP_STATE_CONNECTION_CLOSED:
483 case XSMP_STATE_SAVE_YOURSELF:
484 /* Trying to log out from the save_state callback? Whatever.
485 * Abort the save_state.
487 SmcSaveYourselfDone (xsmp->connection, FALSE);
488 xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
491 case XSMP_STATE_INTERACT_REQUEST:
492 case XSMP_STATE_INTERACT:
493 case XSMP_STATE_SHUTDOWN_CANCELLED:
494 /* Already in a shutdown-related state, just ignore
495 * the new shutdown request...
499 case XSMP_STATE_IDLE:
500 if (xsmp->waiting_to_set_initial_properties)
501 sm_client_xsmp_set_initial_properties (xsmp);
503 if (!xsmp->expecting_initial_save_yourself)
505 /* else fall through */
507 case XSMP_STATE_SAVE_YOURSELF_DONE:
508 /* We need to wait for some response from the server.*/
509 process_ice_messages (SmcGetIceConnection (xsmp->connection));
513 /* Hm... shouldn't happen */
518 /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
519 * the user chooses to save the session. But gnome-session will do
520 * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
521 * save the session... Sigh.
523 if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
524 save_type = SmSaveBoth;
526 save_type = SmSaveGlobal;
528 g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
529 SmcRequestSaveYourself (xsmp->connection,
533 !request_confirmation, /* fast */
539 idle_do_pending_events (gpointer data)
541 EggSMClientXSMP *xsmp = data;
542 EggSMClient *client = data;
544 gdk_threads_enter ();
548 if (xsmp->waiting_to_emit_quit)
550 xsmp->waiting_to_emit_quit = FALSE;
551 egg_sm_client_quit (client);
555 if (xsmp->waiting_to_emit_quit_cancelled)
557 xsmp->waiting_to_emit_quit_cancelled = FALSE;
558 egg_sm_client_quit_cancelled (client);
559 xsmp->state = XSMP_STATE_IDLE;
562 if (xsmp->waiting_to_save_myself)
564 xsmp->waiting_to_save_myself = FALSE;
565 do_save_yourself (xsmp);
569 gdk_threads_leave ();
574 update_pending_events (EggSMClientXSMP *xsmp)
577 xsmp->waiting_to_emit_quit ||
578 xsmp->waiting_to_emit_quit_cancelled ||
579 xsmp->waiting_to_save_myself;
584 xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
589 g_source_remove (xsmp->idle);
595 fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
596 gboolean send_interact_done,
597 gboolean send_save_yourself_done)
599 g_warning ("Received XSMP %s message in state %s: client or server error",
600 message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
602 /* Forget any pending SaveYourself plans we had */
603 xsmp->waiting_to_save_myself = FALSE;
604 update_pending_events (xsmp);
606 if (send_interact_done)
607 SmcInteractDone (xsmp->connection, False);
608 if (send_save_yourself_done)
609 SmcSaveYourselfDone (xsmp->connection, True);
611 xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
617 xsmp_save_yourself (SmcConn smc_conn,
618 SmPointer client_data,
624 EggSMClientXSMP *xsmp = client_data;
625 gboolean wants_quit_requested;
627 g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
628 save_type == SmSaveLocal ? "SmSaveLocal" :
629 save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
630 shutdown ? "Shutdown" : "!Shutdown",
631 interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
632 interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
633 "SmInteractStyleNone", fast ? "Fast" : "!Fast",
634 EGG_SM_CLIENT_XSMP_STATE (xsmp));
636 if (xsmp->state != XSMP_STATE_IDLE &&
637 xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
639 fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
643 if (xsmp->waiting_to_set_initial_properties)
644 sm_client_xsmp_set_initial_properties (xsmp);
646 /* If this is the initial SaveYourself, ignore it; we've already set
647 * properties and there's no reason to actually save state too.
649 if (xsmp->expecting_initial_save_yourself)
651 xsmp->expecting_initial_save_yourself = FALSE;
653 if (save_type == SmSaveLocal &&
654 interact_style == SmInteractStyleNone &&
657 g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
658 SmcSaveYourselfDone (xsmp->connection, True);
659 /* As explained in the comment at the end of
660 * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
661 * state here, not IDLE.
663 xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
667 g_warning ("First SaveYourself was not the expected one!");
670 /* Even ignoring the "fast" flag completely, there are still 18
671 * different combinations of save_type, shutdown and interact_style.
672 * We interpret them as follows:
674 * Type Shutdown Interact Interpretation
675 * G F A/E/N do nothing (1)
676 * G T N do nothing (1)*
677 * G T A/E quit_requested (2)
678 * L/B F A/E/N save_state (3)
679 * L/B T N save_state (3)*
680 * L/B T A/E quit_requested, then save_state (4)
682 * 1. Do nothing, because the SM asked us to do something
683 * uninteresting (save open files, but then don't quit
684 * afterward) or rude (save open files without asking the user
687 * 2. Request interaction and then emit ::quit_requested. This
688 * perhaps isn't quite correct for the SmInteractStyleErrors
689 * case, but we don't care.
691 * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
692 * rows essentially get demoted to SmSaveLocal, because their
693 * Global halves correspond to "do nothing".
695 * 4. Request interaction, emit ::quit_requested, and then emit
696 * ::save_state after interacting. This is the SmSaveBoth
697 * equivalent of #2, but we also promote SmSaveLocal shutdown
698 * SaveYourselfs to SmSaveBoth here, because we want to give
699 * the user a chance to save open files before quitting.
701 * (* It would be nice if we could do something useful when the
702 * session manager sends a SaveYourself with shutdown True and
703 * SmInteractStyleNone. But we can't, so we just pretend it didn't
704 * even tell us it was shutting down. The docs for ::quit mention
705 * that it might not always be preceded by ::quit_requested.)
708 /* As an optimization, we don't actually request interaction and
709 * emit ::quit_requested if the application isn't listening to the
712 wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
714 xsmp->need_save_state = (save_type != SmSaveGlobal);
715 xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
716 interact_style != SmInteractStyleNone);
717 xsmp->interact_errors = (interact_style == SmInteractStyleErrors);
719 xsmp->shutting_down = shutdown;
721 do_save_yourself (xsmp);
725 do_save_yourself (EggSMClientXSMP *xsmp)
727 if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
729 /* The SM cancelled a previous SaveYourself, but we haven't yet
730 * had a chance to tell the application, so we can't start
731 * processing this SaveYourself yet.
733 xsmp->waiting_to_save_myself = TRUE;
734 update_pending_events (xsmp);
738 if (xsmp->need_quit_requested)
740 xsmp->state = XSMP_STATE_INTERACT_REQUEST;
742 g_debug ("Sending InteractRequest(%s)",
743 xsmp->interact_errors ? "Error" : "Normal");
744 SmcInteractRequest (xsmp->connection,
745 xsmp->interact_errors ? SmDialogError : SmDialogNormal,
751 if (xsmp->need_save_state)
755 /* Though unlikely, the client could have been disconnected
756 * while the application was saving its state.
758 if (!xsmp->connection)
762 g_debug ("Sending SaveYourselfDone(True)");
763 SmcSaveYourselfDone (xsmp->connection, True);
765 /* The client state diagram in the XSMP spec says that after a
766 * non-shutdown SaveYourself, we go directly back to "idle". But
767 * everything else in both the XSMP spec and the libSM docs
770 xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
774 save_state (EggSMClientXSMP *xsmp)
776 GKeyFile *state_file;
777 char *state_file_path, *data;
778 EggDesktopFile *desktop_file;
782 /* We set xsmp->state before emitting save_state, but our caller is
783 * responsible for setting it back afterward.
785 xsmp->state = XSMP_STATE_SAVE_YOURSELF;
787 state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
790 restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
791 set_properties (xsmp,
792 ptrarray_prop (SmRestartCommand, restart),
794 g_ptr_array_free (restart, TRUE);
795 delete_properties (xsmp, SmDiscardCommand, NULL);
799 desktop_file = egg_get_desktop_file ();
802 GKeyFile *merged_file;
803 char *desktop_file_path;
805 merged_file = g_key_file_new ();
807 g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
809 if (desktop_file_path &&
810 g_key_file_load_from_file (merged_file, desktop_file_path,
811 G_KEY_FILE_KEEP_COMMENTS |
812 G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
815 char **groups, **keys, *value, *exec;
817 groups = g_key_file_get_groups (state_file, NULL);
818 for (g = 0; groups[g]; g++)
820 keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
821 for (k = 0; keys[k]; k++)
823 value = g_key_file_get_value (state_file, groups[g],
827 g_key_file_set_value (merged_file, groups[g],
836 g_key_file_free (state_file);
837 state_file = merged_file;
839 /* Update Exec key using "--sm-client-state-file %k" */
840 restart = generate_command (xsmp->restart_command,
842 for (i = 0; i < restart->len; i++)
843 restart->pdata[i] = g_shell_quote (restart->pdata[i]);
844 g_ptr_array_add (restart, NULL);
845 exec = g_strjoinv (" ", (char **)restart->pdata);
846 g_strfreev ((char **)restart->pdata);
847 g_ptr_array_free (restart, FALSE);
849 g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
850 EGG_DESKTOP_FILE_KEY_EXEC,
857 g_free (desktop_file_path);
860 /* Now write state_file to disk. (We can't use mktemp(), because
861 * that requires the filename to end with "XXXXXX", and we want
862 * it to end with ".desktop".)
865 data = g_key_file_to_data (state_file, NULL, NULL);
866 g_key_file_free (state_file);
871 state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
872 g_get_user_config_dir (),
873 G_DIR_SEPARATOR, G_DIR_SEPARATOR,
875 (long)time (NULL) + offset,
876 desktop_file ? "desktop" : "state");
878 fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
884 g_free (state_file_path);
887 else if (errno == ENOTDIR || errno == ENOENT)
889 char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
892 if (g_mkdir_with_parents (state_file_path, 0755) != 0)
894 g_warning ("Could not create directory '%s'",
896 g_free (state_file_path);
897 state_file_path = NULL;
904 g_warning ("Could not create file '%s': %s",
905 state_file_path, g_strerror (errno));
906 g_free (state_file_path);
907 state_file_path = NULL;
912 g_file_set_contents (state_file_path, data, -1, NULL);
917 restart = generate_command (xsmp->restart_command, xsmp->client_id,
919 set_properties (xsmp,
920 ptrarray_prop (SmRestartCommand, restart),
922 g_ptr_array_free (restart, TRUE);
926 set_properties (xsmp,
927 array_prop (SmDiscardCommand,
928 "/bin/rm", "-rf", state_file_path,
931 g_free (state_file_path);
936 xsmp_interact (SmcConn smc_conn,
937 SmPointer client_data)
939 EggSMClientXSMP *xsmp = client_data;
940 EggSMClient *client = client_data;
942 g_debug ("Received Interact message in state %s",
943 EGG_SM_CLIENT_XSMP_STATE (xsmp));
945 if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
947 fix_broken_state (xsmp, "Interact", TRUE, TRUE);
951 xsmp->state = XSMP_STATE_INTERACT;
952 egg_sm_client_quit_requested (client);
956 xsmp_die (SmcConn smc_conn,
957 SmPointer client_data)
959 EggSMClientXSMP *xsmp = client_data;
960 EggSMClient *client = client_data;
962 g_debug ("Received Die message in state %s",
963 EGG_SM_CLIENT_XSMP_STATE (xsmp));
965 sm_client_xsmp_disconnect (xsmp);
966 egg_sm_client_quit (client);
970 xsmp_save_complete (SmcConn smc_conn,
971 SmPointer client_data)
973 EggSMClientXSMP *xsmp = client_data;
975 g_debug ("Received SaveComplete message in state %s",
976 EGG_SM_CLIENT_XSMP_STATE (xsmp));
978 if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
979 xsmp->state = XSMP_STATE_IDLE;
981 fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
985 xsmp_shutdown_cancelled (SmcConn smc_conn,
986 SmPointer client_data)
988 EggSMClientXSMP *xsmp = client_data;
989 EggSMClient *client = client_data;
991 g_debug ("Received ShutdownCancelled message in state %s",
992 EGG_SM_CLIENT_XSMP_STATE (xsmp));
994 xsmp->shutting_down = FALSE;
996 if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
998 /* We've finished interacting and now the SM has agreed to
999 * cancel the shutdown.
1001 xsmp->state = XSMP_STATE_IDLE;
1002 egg_sm_client_quit_cancelled (client);
1004 else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
1006 /* Hm... ok, so we got a shutdown SaveYourself, which got
1007 * cancelled, but the application was still interacting, so we
1008 * didn't tell it yet, and then *another* SaveYourself arrived,
1009 * which we must still be waiting to tell the app about, except
1010 * that now that SaveYourself has been cancelled too! Dizzy yet?
1012 xsmp->waiting_to_save_myself = FALSE;
1013 update_pending_events (xsmp);
1017 g_debug ("Sending SaveYourselfDone(False)");
1018 SmcSaveYourselfDone (xsmp->connection, False);
1020 if (xsmp->state == XSMP_STATE_INTERACT)
1022 /* The application is currently interacting, so we can't
1023 * tell it about the cancellation yet; we will wait until
1024 * after it calls egg_sm_client_will_quit().
1026 xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
1030 /* The shutdown was cancelled before the application got a
1031 * chance to interact.
1033 xsmp->state = XSMP_STATE_IDLE;
1040 /* Create a restart/clone/Exec command based on @restart_command.
1041 * If @client_id is non-%NULL, add "--sm-client-id @client_id".
1042 * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
1044 * None of the input strings are g_strdup()ed; the caller must keep
1045 * them around until it is done with the returned GPtrArray, and must
1046 * then free the array, but not its contents.
1049 generate_command (char **restart_command, const char *client_id,
1050 const char *state_file)
1055 cmd = g_ptr_array_new ();
1056 g_ptr_array_add (cmd, restart_command[0]);
1060 g_ptr_array_add (cmd, (char *)"--sm-client-id");
1061 g_ptr_array_add (cmd, (char *)client_id);
1066 g_ptr_array_add (cmd, (char *)"--sm-client-state-file");
1067 g_ptr_array_add (cmd, (char *)state_file);
1070 for (i = 1; restart_command[i]; i++)
1071 g_ptr_array_add (cmd, restart_command[i]);
1076 /* Takes a NULL-terminated list of SmProp * values, created by
1077 * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
1081 set_properties (EggSMClientXSMP *xsmp, ...)
1088 props = g_ptr_array_new ();
1090 va_start (ap, xsmp);
1091 while ((prop = va_arg (ap, SmProp *)))
1092 g_ptr_array_add (props, prop);
1095 if (xsmp->connection)
1097 SmcSetProperties (xsmp->connection, props->len,
1098 (SmProp **)props->pdata);
1101 for (i = 0; i < props->len; i++)
1103 prop = props->pdata[i];
1104 g_free (prop->vals);
1107 g_ptr_array_free (props, TRUE);
1110 /* Takes a NULL-terminated list of property names and deletes them. */
1112 delete_properties (EggSMClientXSMP *xsmp, ...)
1118 if (!xsmp->connection)
1121 props = g_ptr_array_new ();
1123 va_start (ap, xsmp);
1124 while ((prop = va_arg (ap, char *)))
1125 g_ptr_array_add (props, prop);
1128 SmcDeleteProperties (xsmp->connection, props->len,
1129 (char **)props->pdata);
1131 g_ptr_array_free (props, TRUE);
1134 /* Takes an array of strings and creates a LISTofARRAY8 property. The
1135 * strings are neither dupped nor freed; they need to remain valid
1136 * until you're done with the SmProp.
1139 array_prop (const char *name, ...)
1147 prop = g_new (SmProp, 1);
1148 prop->name = (char *)name;
1149 prop->type = (char *)SmLISTofARRAY8;
1151 vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
1153 va_start (ap, name);
1154 while ((value = va_arg (ap, char *)))
1156 pv.length = strlen (value);
1158 g_array_append_val (vals, pv);
1161 prop->num_vals = vals->len;
1162 prop->vals = (SmPropValue *)vals->data;
1164 g_array_free (vals, FALSE);
1169 /* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
1170 * The array contents are neither dupped nor freed; they need to
1171 * remain valid until you're done with the SmProp.
1174 ptrarray_prop (const char *name, GPtrArray *values)
1181 prop = g_new (SmProp, 1);
1182 prop->name = (char *)name;
1183 prop->type = (char *)SmLISTofARRAY8;
1185 vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
1187 for (i = 0; i < values->len; i++)
1189 pv.length = strlen (values->pdata[i]);
1190 pv.value = values->pdata[i];
1191 g_array_append_val (vals, pv);
1194 prop->num_vals = vals->len;
1195 prop->vals = (SmPropValue *)vals->data;
1197 g_array_free (vals, FALSE);
1202 /* Takes a string and creates an ARRAY8 property. The string is
1203 * neither dupped nor freed; it needs to remain valid until you're
1204 * done with the SmProp.
1207 string_prop (const char *name, const char *value)
1211 prop = g_new (SmProp, 1);
1212 prop->name = (char *)name;
1213 prop->type = (char *)SmARRAY8;
1216 prop->vals = g_new (SmPropValue, 1);
1218 prop->vals[0].length = strlen (value);
1219 prop->vals[0].value = (char *)value;
1224 /* Takes a char and creates a CARD8 property. */
1226 card8_prop (const char *name, unsigned char value)
1231 /* To avoid having to allocate and free prop->vals[0], we cheat and
1232 * make vals a 2-element-long array and then use the second element
1236 prop = g_new (SmProp, 1);
1237 prop->name = (char *)name;
1238 prop->type = (char *)SmCARD8;
1241 prop->vals = g_new (SmPropValue, 2);
1242 card8val = (char *)(&prop->vals[1]);
1243 card8val[0] = value;
1245 prop->vals[0].length = 1;
1246 prop->vals[0].value = card8val;
1251 /* ICE code. This makes no effort to play nice with anyone else trying
1252 * to use libICE. Fortunately, no one uses libICE for anything other
1253 * than SM. (DCOP uses ICE, but it has its own private copy of
1256 * When this moves to gtk, it will need to be cleverer, to avoid
1257 * tripping over old apps that use GnomeClient or that use libSM
1261 #include <X11/ICE/ICElib.h>
1264 static void ice_error_handler (IceConn ice_conn,
1266 int offending_minor_opcode,
1267 unsigned long offending_sequence,
1271 static void ice_io_error_handler (IceConn ice_conn);
1272 static void ice_connection_watch (IceConn ice_conn,
1273 IcePointer client_data,
1275 IcePointer *watch_data);
1280 IceSetIOErrorHandler (ice_io_error_handler);
1281 IceSetErrorHandler (ice_error_handler);
1282 IceAddConnectionWatch (ice_connection_watch, NULL);
1286 process_ice_messages (IceConn ice_conn)
1288 IceProcessMessagesStatus status;
1290 gdk_threads_enter ();
1291 status = IceProcessMessages (ice_conn, NULL, NULL);
1292 gdk_threads_leave ();
1296 case IceProcessMessagesSuccess:
1299 case IceProcessMessagesIOError:
1300 sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
1303 case IceProcessMessagesConnectionClosed:
1307 g_assert_not_reached ();
1312 ice_iochannel_watch (GIOChannel *channel,
1313 GIOCondition condition,
1314 gpointer client_data)
1316 return process_ice_messages (client_data);
1320 ice_connection_watch (IceConn ice_conn,
1321 IcePointer client_data,
1323 IcePointer *watch_data)
1329 GIOChannel *channel;
1330 int fd = IceConnectionNumber (ice_conn);
1332 fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
1333 channel = g_io_channel_unix_new (fd);
1334 watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
1335 ice_iochannel_watch, ice_conn);
1336 g_io_channel_unref (channel);
1338 *watch_data = GUINT_TO_POINTER (watch_id);
1342 watch_id = GPOINTER_TO_UINT (*watch_data);
1343 g_source_remove (watch_id);
1348 ice_error_handler (IceConn ice_conn,
1350 int offending_minor_opcode,
1351 unsigned long offending_sequence,
1360 ice_io_error_handler (IceConn ice_conn)
1366 smc_error_handler (SmcConn smc_conn,
1368 int offending_minor_opcode,
1369 unsigned long offending_sequence,