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., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
26 #include "egg-sm-client.h"
27 #include "egg-sm-client-private.h"
29 #include "egg-desktop-file.h"
36 #include <X11/SM/SMlib.h>
40 /* patch provided by Mathias Clasen
41 * see http://git.gnome.org/browse/libegg/commit/?id=0be81fa47fb5dabba2be40888ed5d4b16f0ae6a3
43 #if GTK_CHECK_VERSION( 2, 91, 7 )
47 #define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
48 #define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
49 #define EGG_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
50 #define EGG_IS_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP))
51 #define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP))
52 #define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
54 typedef struct _EggSMClientXSMP EggSMClientXSMP
;
55 typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass
;
57 /* These mostly correspond to the similarly-named states in section
58 * 9.1 of the XSMP spec. Some of the states there aren't represented
59 * here, because we don't need them. SHUTDOWN_CANCELLED is slightly
60 * different from the spec; we use it when the client is IDLE after a
61 * ShutdownCancelled message, but the application is still interacting
62 * and doesn't know the shutdown has been cancelled yet.
67 XSMP_STATE_SAVE_YOURSELF
,
68 XSMP_STATE_INTERACT_REQUEST
,
70 XSMP_STATE_SAVE_YOURSELF_DONE
,
71 XSMP_STATE_SHUTDOWN_CANCELLED
,
72 XSMP_STATE_CONNECTION_CLOSED
73 } EggSMClientXSMPState
;
75 static const char *state_names
[] = {
85 #define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
87 struct _EggSMClientXSMP
94 EggSMClientXSMPState state
;
95 char **restart_command
;
96 gboolean set_restart_command
;
101 /* Current SaveYourself state */
102 guint expecting_initial_save_yourself
: 1;
103 guint need_save_state
: 1;
104 guint need_quit_requested
: 1;
105 guint interact_errors
: 1;
106 guint shutting_down
: 1;
109 guint waiting_to_set_initial_properties
: 1;
110 guint waiting_to_emit_quit
: 1;
111 guint waiting_to_emit_quit_cancelled
: 1;
112 guint waiting_to_save_myself
: 1;
116 struct _EggSMClientXSMPClass
118 EggSMClientClass parent_class
;
122 static void sm_client_xsmp_startup (EggSMClient
*client
,
123 const char *client_id
);
124 static void sm_client_xsmp_set_restart_command (EggSMClient
*client
,
127 static void sm_client_xsmp_will_quit (EggSMClient
*client
,
129 static gboolean
sm_client_xsmp_end_session (EggSMClient
*client
,
130 EggSMClientEndStyle style
,
131 gboolean request_confirmation
);
133 static void xsmp_save_yourself (SmcConn smc_conn
,
134 SmPointer client_data
,
139 static void xsmp_die (SmcConn smc_conn
,
140 SmPointer client_data
);
141 static void xsmp_save_complete (SmcConn smc_conn
,
142 SmPointer client_data
);
143 static void xsmp_shutdown_cancelled (SmcConn smc_conn
,
144 SmPointer client_data
);
145 static void xsmp_interact (SmcConn smc_conn
,
146 SmPointer client_data
);
148 static SmProp
*array_prop (const char *name
,
150 static SmProp
*ptrarray_prop (const char *name
,
152 static SmProp
*string_prop (const char *name
,
154 static SmProp
*card8_prop (const char *name
,
155 unsigned char value
);
157 static void set_properties (EggSMClientXSMP
*xsmp
, ...);
158 static void delete_properties (EggSMClientXSMP
*xsmp
, ...);
160 static GPtrArray
*generate_command (char **restart_command
,
161 const char *client_id
,
162 const char *state_file
);
164 static void save_state (EggSMClientXSMP
*xsmp
);
165 static void do_save_yourself (EggSMClientXSMP
*xsmp
);
166 static void update_pending_events (EggSMClientXSMP
*xsmp
);
168 static void ice_init (void);
169 static gboolean
process_ice_messages (IceConn ice_conn
);
170 static void smc_error_handler (SmcConn smc_conn
,
172 int offending_minor_opcode
,
173 unsigned long offending_sequence
,
178 G_DEFINE_TYPE (EggSMClientXSMP
, egg_sm_client_xsmp
, EGG_TYPE_SM_CLIENT
)
181 egg_sm_client_xsmp_init (EggSMClientXSMP
*xsmp
)
183 xsmp
->state
= XSMP_STATE_CONNECTION_CLOSED
;
184 xsmp
->connection
= NULL
;
185 xsmp
->restart_style
= SmRestartIfRunning
;
189 egg_sm_client_xsmp_class_init (EggSMClientXSMPClass
*klass
)
191 EggSMClientClass
*sm_client_class
= EGG_SM_CLIENT_CLASS (klass
);
193 sm_client_class
->startup
= sm_client_xsmp_startup
;
194 sm_client_class
->set_restart_command
= sm_client_xsmp_set_restart_command
;
195 sm_client_class
->will_quit
= sm_client_xsmp_will_quit
;
196 sm_client_class
->end_session
= sm_client_xsmp_end_session
;
200 egg_sm_client_xsmp_new (void)
202 if (!g_getenv ("SESSION_MANAGER"))
205 return g_object_new (EGG_TYPE_SM_CLIENT_XSMP
, NULL
);
209 sm_client_xsmp_set_initial_properties (gpointer user_data
)
211 EggSMClientXSMP
*xsmp
= user_data
;
212 EggDesktopFile
*desktop_file
;
213 GPtrArray
*clone
, *restart
;
216 g_debug( "egg_sm_client_xsmp_set_initial_properties" );
220 g_source_remove (xsmp
->idle
);
223 xsmp
->waiting_to_set_initial_properties
= FALSE
;
225 if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART
)
226 xsmp
->restart_style
= SmRestartNever
;
228 /* Parse info out of desktop file */
229 desktop_file
= egg_get_desktop_file ();
233 char *cmdline
, **argv
;
236 if (xsmp
->restart_style
== SmRestartIfRunning
)
238 if (egg_desktop_file_get_boolean (desktop_file
,
239 "X-GNOME-AutoRestart", NULL
))
240 xsmp
->restart_style
= SmRestartImmediately
;
243 if (!xsmp
->set_restart_command
)
245 cmdline
= egg_desktop_file_parse_exec (desktop_file
, NULL
, &err
);
246 if (cmdline
&& g_shell_parse_argv (cmdline
, &argc
, &argv
, &err
))
248 egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp
),
249 argc
, (const char **)argv
);
254 g_warning ("Could not parse Exec line in desktop file: %s",
262 if (!xsmp
->set_restart_command
)
263 xsmp
->restart_command
= g_strsplit (g_get_prgname (), " ", -1);
265 clone
= generate_command (xsmp
->restart_command
, NULL
, NULL
);
266 restart
= generate_command (xsmp
->restart_command
, xsmp
->client_id
, NULL
);
268 g_debug ("Setting initial properties");
270 /* Program, CloneCommand, RestartCommand, and UserID are required.
271 * ProcessID isn't required, but the SM may be able to do something
274 g_snprintf (pid_str
, sizeof (pid_str
), "%lu", (gulong
) getpid ());
275 set_properties (xsmp
,
276 string_prop (SmProgram
, g_get_prgname ()),
277 ptrarray_prop (SmCloneCommand
, clone
),
278 ptrarray_prop (SmRestartCommand
, restart
),
279 string_prop (SmUserID
, g_get_user_name ()),
280 string_prop (SmProcessID
, pid_str
),
281 card8_prop (SmRestartStyleHint
, xsmp
->restart_style
),
283 g_ptr_array_free (clone
, TRUE
);
284 g_ptr_array_free (restart
, TRUE
);
288 set_properties (xsmp
,
289 string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file
)),
293 update_pending_events (xsmp
);
297 /* This gets called from two different places: xsmp_die() (when the
298 * server asks us to disconnect) and process_ice_messages() (when the
299 * server disconnects unexpectedly).
302 sm_client_xsmp_disconnect (EggSMClientXSMP
*xsmp
)
306 g_debug( "egg_sm_client_xsmp_disconnect" );
308 if (!xsmp
->connection
)
311 g_debug ("Disconnecting");
313 connection
= xsmp
->connection
;
314 xsmp
->connection
= NULL
;
315 SmcCloseConnection (connection
, 0, NULL
);
316 xsmp
->state
= XSMP_STATE_CONNECTION_CLOSED
;
318 xsmp
->waiting_to_save_myself
= FALSE
;
319 update_pending_events (xsmp
);
323 sm_client_xsmp_startup (EggSMClient
*client
,
324 const char *client_id
)
326 g_debug( "sm_client_xsmp_startup: client=%p, client_id=%s", ( void * ) client
, client_id
);
328 EggSMClientXSMP
*xsmp
= (EggSMClientXSMP
*)client
;
329 SmcCallbacks callbacks
;
331 char error_string_ret
[256];
333 xsmp
->client_id
= g_strdup (client_id
);
336 SmcSetErrorHandler (smc_error_handler
);
338 callbacks
.save_yourself
.callback
= xsmp_save_yourself
;
339 callbacks
.die
.callback
= xsmp_die
;
340 callbacks
.save_complete
.callback
= xsmp_save_complete
;
341 callbacks
.shutdown_cancelled
.callback
= xsmp_shutdown_cancelled
;
343 callbacks
.save_yourself
.client_data
= xsmp
;
344 callbacks
.die
.client_data
= xsmp
;
345 callbacks
.save_complete
.client_data
= xsmp
;
346 callbacks
.shutdown_cancelled
.client_data
= xsmp
;
349 error_string_ret
[0] = '\0';
351 SmcOpenConnection (NULL
, xsmp
, SmProtoMajor
, SmProtoMinor
,
352 SmcSaveYourselfProcMask
| SmcDieProcMask
|
353 SmcSaveCompleteProcMask
|
354 SmcShutdownCancelledProcMask
,
356 xsmp
->client_id
, &ret_client_id
,
357 sizeof (error_string_ret
), error_string_ret
);
359 if (!xsmp
->connection
)
361 g_warning ("Failed to connect to the session manager: %s\n",
362 error_string_ret
[0] ?
363 error_string_ret
: "no error message given");
364 xsmp
->state
= XSMP_STATE_CONNECTION_CLOSED
;
368 /* We expect a pointless initial SaveYourself if either (a) we
369 * didn't have an initial client ID, or (b) we DID have an initial
370 * client ID, but the server rejected it and gave us a new one.
372 if (!xsmp
->client_id
||
373 (ret_client_id
&& strcmp (xsmp
->client_id
, ret_client_id
) != 0))
374 xsmp
->expecting_initial_save_yourself
= TRUE
;
378 g_free (xsmp
->client_id
);
379 xsmp
->client_id
= g_strdup (ret_client_id
);
380 free (ret_client_id
);
382 gdk_threads_enter ();
383 #if GTK_CHECK_VERSION( 2, 91, 7 )
384 gdk_x11_set_sm_client_id (xsmp
->client_id
);
386 gdk_set_sm_client_id (xsmp
->client_id
);
388 gdk_threads_leave ();
390 g_debug ("Got client ID \"%s\"", xsmp
->client_id
);
393 xsmp
->state
= XSMP_STATE_IDLE
;
395 /* Do not set the initial properties until we reach the main loop,
396 * so that the application has a chance to call
397 * egg_set_desktop_file(). (This may also help the session manager
398 * have a better idea of when the application is fully up and
401 xsmp
->waiting_to_set_initial_properties
= TRUE
;
402 xsmp
->idle
= g_idle_add (sm_client_xsmp_set_initial_properties
, client
);
406 sm_client_xsmp_set_restart_command (EggSMClient
*client
,
410 EggSMClientXSMP
*xsmp
= (EggSMClientXSMP
*)client
;
413 g_strfreev (xsmp
->restart_command
);
415 xsmp
->restart_command
= g_new (char *, argc
+ 1);
416 for (i
= 0; i
< argc
; i
++)
417 xsmp
->restart_command
[i
] = g_strdup (argv
[i
]);
418 xsmp
->restart_command
[i
] = NULL
;
420 xsmp
->set_restart_command
= TRUE
;
424 sm_client_xsmp_will_quit (EggSMClient
*client
,
427 EggSMClientXSMP
*xsmp
= (EggSMClientXSMP
*)client
;
429 g_debug( "egg_sm_client_xsmp_will_quit" );
431 if (xsmp
->state
== XSMP_STATE_CONNECTION_CLOSED
)
433 /* The session manager has already exited! Schedule a quit
436 xsmp
->waiting_to_emit_quit
= TRUE
;
437 update_pending_events (xsmp
);
440 else if (xsmp
->state
== XSMP_STATE_SHUTDOWN_CANCELLED
)
442 /* We received a ShutdownCancelled message while the application
443 * was interacting; Schedule a quit_cancelled signal.
445 xsmp
->waiting_to_emit_quit_cancelled
= TRUE
;
446 update_pending_events (xsmp
);
450 g_return_if_fail (xsmp
->state
== XSMP_STATE_INTERACT
);
452 g_debug ("Sending InteractDone(%s)", will_quit
? "False" : "True");
453 SmcInteractDone (xsmp
->connection
, !will_quit
);
455 if (will_quit
&& xsmp
->need_save_state
)
458 g_debug ("Sending SaveYourselfDone(%s)", will_quit
? "True" : "False");
459 SmcSaveYourselfDone (xsmp
->connection
, will_quit
);
460 xsmp
->state
= XSMP_STATE_SAVE_YOURSELF_DONE
;
464 sm_client_xsmp_end_session (EggSMClient
*client
,
465 EggSMClientEndStyle style
,
466 gboolean request_confirmation
)
468 EggSMClientXSMP
*xsmp
= (EggSMClientXSMP
*)client
;
471 g_debug( "egg_sm_client_xsmp_end_session" );
473 /* To end the session via XSMP, we have to send a
474 * SaveYourselfRequest. We aren't allowed to do that if anything
475 * else is going on, but we don't want to expose this fact to the
476 * application. So we do our best to patch things up here...
478 * In the worst case, this method might block for some length of
479 * time in process_ice_messages, but the only time that code path is
480 * honestly likely to get hit is if the application tries to end the
481 * session as the very first thing it does, in which case it
482 * probably won't actually block anyway. It's not worth gunking up
483 * the API to try to deal nicely with the other 0.01% of cases where
487 while (xsmp
->state
!= XSMP_STATE_IDLE
||
488 xsmp
->expecting_initial_save_yourself
)
490 /* If we're already shutting down, we don't need to do anything. */
491 if (xsmp
->shutting_down
)
496 case XSMP_STATE_CONNECTION_CLOSED
:
499 case XSMP_STATE_SAVE_YOURSELF
:
500 /* Trying to log out from the save_state callback? Whatever.
501 * Abort the save_state.
503 SmcSaveYourselfDone (xsmp
->connection
, FALSE
);
504 xsmp
->state
= XSMP_STATE_SAVE_YOURSELF_DONE
;
507 case XSMP_STATE_INTERACT_REQUEST
:
508 case XSMP_STATE_INTERACT
:
509 case XSMP_STATE_SHUTDOWN_CANCELLED
:
510 /* Already in a shutdown-related state, just ignore
511 * the new shutdown request...
515 case XSMP_STATE_IDLE
:
516 if (xsmp
->waiting_to_set_initial_properties
)
517 sm_client_xsmp_set_initial_properties (xsmp
);
519 if (!xsmp
->expecting_initial_save_yourself
)
521 /* else fall through */
523 case XSMP_STATE_SAVE_YOURSELF_DONE
:
524 /* We need to wait for some response from the server.*/
525 process_ice_messages (SmcGetIceConnection (xsmp
->connection
));
529 /* Hm... shouldn't happen */
534 /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
535 * the user chooses to save the session. But gnome-session will do
536 * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
537 * save the session... Sigh.
539 if (!strcmp (SmcVendor (xsmp
->connection
), "xfce4-session"))
540 save_type
= SmSaveBoth
;
542 save_type
= SmSaveGlobal
;
544 g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation
? "!" : "");
545 SmcRequestSaveYourself (xsmp
->connection
,
549 !request_confirmation
, /* fast */
555 idle_do_pending_events (gpointer data
)
557 EggSMClientXSMP
*xsmp
= data
;
558 EggSMClient
*client
= data
;
560 gdk_threads_enter ();
564 if (xsmp
->waiting_to_emit_quit
)
566 xsmp
->waiting_to_emit_quit
= FALSE
;
567 egg_sm_client_quit (client
);
571 if (xsmp
->waiting_to_emit_quit_cancelled
)
573 xsmp
->waiting_to_emit_quit_cancelled
= FALSE
;
574 egg_sm_client_quit_cancelled (client
);
575 xsmp
->state
= XSMP_STATE_IDLE
;
578 if (xsmp
->waiting_to_save_myself
)
580 xsmp
->waiting_to_save_myself
= FALSE
;
581 do_save_yourself (xsmp
);
585 gdk_threads_leave ();
590 update_pending_events (EggSMClientXSMP
*xsmp
)
593 xsmp
->waiting_to_emit_quit
||
594 xsmp
->waiting_to_emit_quit_cancelled
||
595 xsmp
->waiting_to_save_myself
;
600 xsmp
->idle
= g_idle_add (idle_do_pending_events
, xsmp
);
605 g_source_remove (xsmp
->idle
);
611 fix_broken_state (EggSMClientXSMP
*xsmp
, const char *message
,
612 gboolean send_interact_done
,
613 gboolean send_save_yourself_done
)
615 g_warning ("Received XSMP %s message in state %s: client or server error",
616 message
, EGG_SM_CLIENT_XSMP_STATE (xsmp
));
618 /* Forget any pending SaveYourself plans we had */
619 xsmp
->waiting_to_save_myself
= FALSE
;
620 update_pending_events (xsmp
);
622 if (send_interact_done
)
623 SmcInteractDone (xsmp
->connection
, False
);
624 if (send_save_yourself_done
)
625 SmcSaveYourselfDone (xsmp
->connection
, True
);
627 xsmp
->state
= send_save_yourself_done
? XSMP_STATE_SAVE_YOURSELF_DONE
: XSMP_STATE_IDLE
;
633 xsmp_save_yourself (SmcConn smc_conn
,
634 SmPointer client_data
,
640 EggSMClientXSMP
*xsmp
= client_data
;
641 gboolean wants_quit_requested
;
643 g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
644 save_type
== SmSaveLocal
? "SmSaveLocal" :
645 save_type
== SmSaveGlobal
? "SmSaveGlobal" : "SmSaveBoth",
646 shutdown
? "Shutdown" : "!Shutdown",
647 interact_style
== SmInteractStyleAny
? "SmInteractStyleAny" :
648 interact_style
== SmInteractStyleErrors
? "SmInteractStyleErrors" :
649 "SmInteractStyleNone", fast
? "Fast" : "!Fast",
650 EGG_SM_CLIENT_XSMP_STATE (xsmp
));
652 if (xsmp
->state
!= XSMP_STATE_IDLE
&&
653 xsmp
->state
!= XSMP_STATE_SHUTDOWN_CANCELLED
)
655 fix_broken_state (xsmp
, "SaveYourself", FALSE
, TRUE
);
659 if (xsmp
->waiting_to_set_initial_properties
)
660 sm_client_xsmp_set_initial_properties (xsmp
);
662 /* If this is the initial SaveYourself, ignore it; we've already set
663 * properties and there's no reason to actually save state too.
665 if (xsmp
->expecting_initial_save_yourself
)
667 xsmp
->expecting_initial_save_yourself
= FALSE
;
669 if (save_type
== SmSaveLocal
&&
670 interact_style
== SmInteractStyleNone
&&
673 g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
674 SmcSaveYourselfDone (xsmp
->connection
, True
);
675 /* As explained in the comment at the end of
676 * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
677 * state here, not IDLE.
679 xsmp
->state
= XSMP_STATE_SAVE_YOURSELF_DONE
;
683 g_warning ("First SaveYourself was not the expected one!");
686 /* Even ignoring the "fast" flag completely, there are still 18
687 * different combinations of save_type, shutdown and interact_style.
688 * We interpret them as follows:
690 * Type Shutdown Interact Interpretation
691 * G F A/E/N do nothing (1)
692 * G T N do nothing (1)*
693 * G T A/E quit_requested (2)
694 * L/B F A/E/N save_state (3)
695 * L/B T N save_state (3)*
696 * L/B T A/E quit_requested, then save_state (4)
698 * 1. Do nothing, because the SM asked us to do something
699 * uninteresting (save open files, but then don't quit
700 * afterward) or rude (save open files without asking the user
703 * 2. Request interaction and then emit ::quit_requested. This
704 * perhaps isn't quite correct for the SmInteractStyleErrors
705 * case, but we don't care.
707 * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
708 * rows essentially get demoted to SmSaveLocal, because their
709 * Global halves correspond to "do nothing".
711 * 4. Request interaction, emit ::quit_requested, and then emit
712 * ::save_state after interacting. This is the SmSaveBoth
713 * equivalent of #2, but we also promote SmSaveLocal shutdown
714 * SaveYourselfs to SmSaveBoth here, because we want to give
715 * the user a chance to save open files before quitting.
717 * (* It would be nice if we could do something useful when the
718 * session manager sends a SaveYourself with shutdown True and
719 * SmInteractStyleNone. But we can't, so we just pretend it didn't
720 * even tell us it was shutting down. The docs for ::quit mention
721 * that it might not always be preceded by ::quit_requested.)
724 /* As an optimization, we don't actually request interaction and
725 * emit ::quit_requested if the application isn't listening to the
728 wants_quit_requested
= g_signal_has_handler_pending (xsmp
, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT
), 0, FALSE
);
730 xsmp
->need_save_state
= (save_type
!= SmSaveGlobal
);
731 xsmp
->need_quit_requested
= (shutdown
&& wants_quit_requested
&&
732 interact_style
!= SmInteractStyleNone
);
733 xsmp
->interact_errors
= (interact_style
== SmInteractStyleErrors
);
735 xsmp
->shutting_down
= shutdown
;
737 do_save_yourself (xsmp
);
741 do_save_yourself (EggSMClientXSMP
*xsmp
)
743 g_debug( "egg_sm_client_xsmp_do_save_yourself" );
745 if (xsmp
->state
== XSMP_STATE_SHUTDOWN_CANCELLED
)
747 /* The SM cancelled a previous SaveYourself, but we haven't yet
748 * had a chance to tell the application, so we can't start
749 * processing this SaveYourself yet.
751 xsmp
->waiting_to_save_myself
= TRUE
;
752 update_pending_events (xsmp
);
756 if (xsmp
->need_quit_requested
)
758 xsmp
->state
= XSMP_STATE_INTERACT_REQUEST
;
760 g_debug ("Sending InteractRequest(%s)",
761 xsmp
->interact_errors
? "Error" : "Normal");
762 SmcInteractRequest (xsmp
->connection
,
763 xsmp
->interact_errors
? SmDialogError
: SmDialogNormal
,
769 if (xsmp
->need_save_state
)
773 /* Though unlikely, the client could have been disconnected
774 * while the application was saving its state.
776 if (!xsmp
->connection
)
780 g_debug ("Sending SaveYourselfDone(True)");
781 SmcSaveYourselfDone (xsmp
->connection
, True
);
783 /* The client state diagram in the XSMP spec says that after a
784 * non-shutdown SaveYourself, we go directly back to "idle". But
785 * everything else in both the XSMP spec and the libSM docs
788 xsmp
->state
= XSMP_STATE_SAVE_YOURSELF_DONE
;
792 save_state (EggSMClientXSMP
*xsmp
)
794 GKeyFile
*state_file
;
795 char *state_file_path
, *data
;
796 EggDesktopFile
*desktop_file
;
800 /* We set xsmp->state before emitting save_state, but our caller is
801 * responsible for setting it back afterward.
803 xsmp
->state
= XSMP_STATE_SAVE_YOURSELF
;
805 state_file
= egg_sm_client_save_state ((EggSMClient
*)xsmp
);
808 restart
= generate_command (xsmp
->restart_command
, xsmp
->client_id
, NULL
);
809 set_properties (xsmp
,
810 ptrarray_prop (SmRestartCommand
, restart
),
812 g_ptr_array_free (restart
, TRUE
);
813 delete_properties (xsmp
, SmDiscardCommand
, NULL
);
817 desktop_file
= egg_get_desktop_file ();
820 GKeyFile
*merged_file
;
821 char *desktop_file_path
;
823 merged_file
= g_key_file_new ();
825 g_filename_from_uri (egg_desktop_file_get_source (desktop_file
),
827 if (desktop_file_path
&&
828 g_key_file_load_from_file (merged_file
, desktop_file_path
,
829 G_KEY_FILE_KEEP_COMMENTS
|
830 G_KEY_FILE_KEEP_TRANSLATIONS
, NULL
))
833 char **groups
, **keys
, *value
, *exec
;
835 groups
= g_key_file_get_groups (state_file
, NULL
);
836 for (g
= 0; groups
[g
]; g
++)
838 keys
= g_key_file_get_keys (state_file
, groups
[g
], NULL
, NULL
);
839 for (k
= 0; keys
[k
]; k
++)
841 value
= g_key_file_get_value (state_file
, groups
[g
],
845 g_key_file_set_value (merged_file
, groups
[g
],
854 g_key_file_free (state_file
);
855 state_file
= merged_file
;
857 /* Update Exec key using "--sm-client-state-file %k" */
858 restart
= generate_command (xsmp
->restart_command
,
860 for (i
= 0; i
< restart
->len
; i
++)
861 restart
->pdata
[i
] = g_shell_quote (restart
->pdata
[i
]);
862 g_ptr_array_add (restart
, NULL
);
863 exec
= g_strjoinv (" ", (char **)restart
->pdata
);
864 g_strfreev ((char **)restart
->pdata
);
865 g_ptr_array_free (restart
, FALSE
);
867 g_key_file_set_string (state_file
, EGG_DESKTOP_FILE_GROUP
,
868 EGG_DESKTOP_FILE_KEY_EXEC
,
875 g_free (desktop_file_path
);
878 /* Now write state_file to disk. (We can't use mktemp(), because
879 * that requires the filename to end with "XXXXXX", and we want
880 * it to end with ".desktop".)
883 data
= g_key_file_to_data (state_file
, NULL
, NULL
);
884 g_key_file_free (state_file
);
889 state_file_path
= g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
890 g_get_user_config_dir (),
891 G_DIR_SEPARATOR
, G_DIR_SEPARATOR
,
893 (long)time (NULL
) + offset
,
894 desktop_file
? "desktop" : "state");
896 fd
= open (state_file_path
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
902 g_free (state_file_path
);
905 else if (errno
== ENOTDIR
|| errno
== ENOENT
)
907 char *sep
= strrchr (state_file_path
, G_DIR_SEPARATOR
);
910 if (g_mkdir_with_parents (state_file_path
, 0755) != 0)
912 g_warning ("Could not create directory '%s'",
914 g_free (state_file_path
);
915 state_file_path
= NULL
;
922 g_warning ("Could not create file '%s': %s",
923 state_file_path
, g_strerror (errno
));
924 g_free (state_file_path
);
925 state_file_path
= NULL
;
930 g_file_set_contents (state_file_path
, data
, -1, NULL
);
935 restart
= generate_command (xsmp
->restart_command
, xsmp
->client_id
,
937 set_properties (xsmp
,
938 ptrarray_prop (SmRestartCommand
, restart
),
940 g_ptr_array_free (restart
, TRUE
);
944 set_properties (xsmp
,
945 array_prop (SmDiscardCommand
,
946 "/bin/rm", "-rf", state_file_path
,
949 g_free (state_file_path
);
954 xsmp_interact (SmcConn smc_conn
,
955 SmPointer client_data
)
957 EggSMClientXSMP
*xsmp
= client_data
;
958 EggSMClient
*client
= client_data
;
960 g_debug ("Received Interact message in state %s",
961 EGG_SM_CLIENT_XSMP_STATE (xsmp
));
963 if (xsmp
->state
!= XSMP_STATE_INTERACT_REQUEST
)
965 fix_broken_state (xsmp
, "Interact", TRUE
, TRUE
);
969 xsmp
->state
= XSMP_STATE_INTERACT
;
970 egg_sm_client_quit_requested (client
);
974 xsmp_die (SmcConn smc_conn
,
975 SmPointer client_data
)
977 EggSMClientXSMP
*xsmp
= client_data
;
978 EggSMClient
*client
= client_data
;
980 g_debug ("Received Die message in state %s",
981 EGG_SM_CLIENT_XSMP_STATE (xsmp
));
983 sm_client_xsmp_disconnect (xsmp
);
984 egg_sm_client_quit (client
);
988 xsmp_save_complete (SmcConn smc_conn
,
989 SmPointer client_data
)
991 EggSMClientXSMP
*xsmp
= client_data
;
993 g_debug ("Received SaveComplete message in state %s",
994 EGG_SM_CLIENT_XSMP_STATE (xsmp
));
996 if (xsmp
->state
== XSMP_STATE_SAVE_YOURSELF_DONE
)
997 xsmp
->state
= XSMP_STATE_IDLE
;
999 fix_broken_state (xsmp
, "SaveComplete", FALSE
, FALSE
);
1003 xsmp_shutdown_cancelled (SmcConn smc_conn
,
1004 SmPointer client_data
)
1006 EggSMClientXSMP
*xsmp
= client_data
;
1007 EggSMClient
*client
= client_data
;
1009 g_debug ("Received ShutdownCancelled message in state %s",
1010 EGG_SM_CLIENT_XSMP_STATE (xsmp
));
1012 xsmp
->shutting_down
= FALSE
;
1014 if (xsmp
->state
== XSMP_STATE_SAVE_YOURSELF_DONE
)
1016 /* We've finished interacting and now the SM has agreed to
1017 * cancel the shutdown.
1019 xsmp
->state
= XSMP_STATE_IDLE
;
1020 egg_sm_client_quit_cancelled (client
);
1022 else if (xsmp
->state
== XSMP_STATE_SHUTDOWN_CANCELLED
)
1024 /* Hm... ok, so we got a shutdown SaveYourself, which got
1025 * cancelled, but the application was still interacting, so we
1026 * didn't tell it yet, and then *another* SaveYourself arrived,
1027 * which we must still be waiting to tell the app about, except
1028 * that now that SaveYourself has been cancelled too! Dizzy yet?
1030 xsmp
->waiting_to_save_myself
= FALSE
;
1031 update_pending_events (xsmp
);
1035 g_debug ("Sending SaveYourselfDone(False)");
1036 SmcSaveYourselfDone (xsmp
->connection
, False
);
1038 if (xsmp
->state
== XSMP_STATE_INTERACT
)
1040 /* The application is currently interacting, so we can't
1041 * tell it about the cancellation yet; we will wait until
1042 * after it calls egg_sm_client_will_quit().
1044 xsmp
->state
= XSMP_STATE_SHUTDOWN_CANCELLED
;
1048 /* The shutdown was cancelled before the application got a
1049 * chance to interact.
1051 xsmp
->state
= XSMP_STATE_IDLE
;
1058 /* Create a restart/clone/Exec command based on @restart_command.
1059 * If @client_id is non-%NULL, add "--sm-client-id @client_id".
1060 * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
1062 * None of the input strings are g_strdup()ed; the caller must keep
1063 * them around until it is done with the returned GPtrArray, and must
1064 * then free the array, but not its contents.
1067 generate_command (char **restart_command
, const char *client_id
,
1068 const char *state_file
)
1073 cmd
= g_ptr_array_new ();
1074 g_ptr_array_add (cmd
, restart_command
[0]);
1078 g_ptr_array_add (cmd
, (char *)"--sm-client-id");
1079 g_ptr_array_add (cmd
, (char *)client_id
);
1084 g_ptr_array_add (cmd
, (char *)"--sm-client-state-file");
1085 g_ptr_array_add (cmd
, (char *)state_file
);
1088 for (i
= 1; restart_command
[i
]; i
++)
1089 g_ptr_array_add (cmd
, restart_command
[i
]);
1094 /* Takes a NULL-terminated list of SmProp * values, created by
1095 * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
1099 set_properties (EggSMClientXSMP
*xsmp
, ...)
1106 props
= g_ptr_array_new ();
1108 va_start (ap
, xsmp
);
1109 while ((prop
= va_arg (ap
, SmProp
*)))
1110 g_ptr_array_add (props
, prop
);
1113 if (xsmp
->connection
)
1115 SmcSetProperties (xsmp
->connection
, props
->len
,
1116 (SmProp
**)props
->pdata
);
1119 for (i
= 0; i
< props
->len
; i
++)
1121 prop
= props
->pdata
[i
];
1122 g_free (prop
->vals
);
1125 g_ptr_array_free (props
, TRUE
);
1128 /* Takes a NULL-terminated list of property names and deletes them. */
1130 delete_properties (EggSMClientXSMP
*xsmp
, ...)
1136 if (!xsmp
->connection
)
1139 props
= g_ptr_array_new ();
1141 va_start (ap
, xsmp
);
1142 while ((prop
= va_arg (ap
, char *)))
1143 g_ptr_array_add (props
, prop
);
1146 SmcDeleteProperties (xsmp
->connection
, props
->len
,
1147 (char **)props
->pdata
);
1149 g_ptr_array_free (props
, TRUE
);
1152 /* Takes an array of strings and creates a LISTofARRAY8 property. The
1153 * strings are neither dupped nor freed; they need to remain valid
1154 * until you're done with the SmProp.
1157 array_prop (const char *name
, ...)
1165 prop
= g_new (SmProp
, 1);
1166 prop
->name
= (char *)name
;
1167 prop
->type
= (char *)SmLISTofARRAY8
;
1169 vals
= g_array_new (FALSE
, FALSE
, sizeof (SmPropValue
));
1171 va_start (ap
, name
);
1172 while ((value
= va_arg (ap
, char *)))
1174 pv
.length
= strlen (value
);
1176 g_array_append_val (vals
, pv
);
1179 prop
->num_vals
= vals
->len
;
1180 prop
->vals
= (SmPropValue
*)vals
->data
;
1182 g_array_free (vals
, FALSE
);
1187 /* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
1188 * The array contents are neither dupped nor freed; they need to
1189 * remain valid until you're done with the SmProp.
1192 ptrarray_prop (const char *name
, GPtrArray
*values
)
1199 prop
= g_new (SmProp
, 1);
1200 prop
->name
= (char *)name
;
1201 prop
->type
= (char *)SmLISTofARRAY8
;
1203 vals
= g_array_new (FALSE
, FALSE
, sizeof (SmPropValue
));
1205 for (i
= 0; i
< values
->len
; i
++)
1207 pv
.length
= strlen (values
->pdata
[i
]);
1208 pv
.value
= values
->pdata
[i
];
1209 g_array_append_val (vals
, pv
);
1212 prop
->num_vals
= vals
->len
;
1213 prop
->vals
= (SmPropValue
*)vals
->data
;
1215 g_array_free (vals
, FALSE
);
1220 /* Takes a string and creates an ARRAY8 property. The string is
1221 * neither dupped nor freed; it needs to remain valid until you're
1222 * done with the SmProp.
1225 string_prop (const char *name
, const char *value
)
1229 prop
= g_new (SmProp
, 1);
1230 prop
->name
= (char *)name
;
1231 prop
->type
= (char *)SmARRAY8
;
1234 prop
->vals
= g_new (SmPropValue
, 1);
1236 prop
->vals
[0].length
= strlen (value
);
1237 prop
->vals
[0].value
= (char *)value
;
1242 /* Takes a char and creates a CARD8 property. */
1244 card8_prop (const char *name
, unsigned char value
)
1249 /* To avoid having to allocate and free prop->vals[0], we cheat and
1250 * make vals a 2-element-long array and then use the second element
1254 prop
= g_new (SmProp
, 1);
1255 prop
->name
= (char *)name
;
1256 prop
->type
= (char *)SmCARD8
;
1259 prop
->vals
= g_new (SmPropValue
, 2);
1260 card8val
= (char *)(&prop
->vals
[1]);
1261 card8val
[0] = value
;
1263 prop
->vals
[0].length
= 1;
1264 prop
->vals
[0].value
= card8val
;
1269 /* ICE code. This makes no effort to play nice with anyone else trying
1270 * to use libICE. Fortunately, no one uses libICE for anything other
1271 * than SM. (DCOP uses ICE, but it has its own private copy of
1274 * When this moves to gtk, it will need to be cleverer, to avoid
1275 * tripping over old apps that use GnomeClient or that use libSM
1279 #include <X11/ICE/ICElib.h>
1282 static void ice_error_handler (IceConn ice_conn
,
1284 int offending_minor_opcode
,
1285 unsigned long offending_sequence
,
1289 static void ice_io_error_handler (IceConn ice_conn
);
1290 static void ice_connection_watch (IceConn ice_conn
,
1291 IcePointer client_data
,
1293 IcePointer
*watch_data
);
1298 IceSetIOErrorHandler (ice_io_error_handler
);
1299 IceSetErrorHandler (ice_error_handler
);
1300 IceAddConnectionWatch (ice_connection_watch
, NULL
);
1304 process_ice_messages (IceConn ice_conn
)
1306 IceProcessMessagesStatus status
;
1308 gdk_threads_enter ();
1309 status
= IceProcessMessages (ice_conn
, NULL
, NULL
);
1310 gdk_threads_leave ();
1314 case IceProcessMessagesSuccess
:
1317 case IceProcessMessagesIOError
:
1318 sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn
));
1321 case IceProcessMessagesConnectionClosed
:
1325 g_assert_not_reached ();
1330 ice_iochannel_watch (GIOChannel
*channel
,
1331 GIOCondition condition
,
1332 gpointer client_data
)
1334 return process_ice_messages (client_data
);
1338 ice_connection_watch (IceConn ice_conn
,
1339 IcePointer client_data
,
1341 IcePointer
*watch_data
)
1347 GIOChannel
*channel
;
1348 int fd
= IceConnectionNumber (ice_conn
);
1350 fcntl (fd
, F_SETFD
, fcntl (fd
, F_GETFD
, 0) | FD_CLOEXEC
);
1351 channel
= g_io_channel_unix_new (fd
);
1352 watch_id
= g_io_add_watch (channel
, G_IO_IN
| G_IO_ERR
,
1353 ice_iochannel_watch
, ice_conn
);
1354 g_io_channel_unref (channel
);
1356 *watch_data
= GUINT_TO_POINTER (watch_id
);
1360 watch_id
= GPOINTER_TO_UINT (*watch_data
);
1361 g_source_remove (watch_id
);
1366 ice_error_handler (IceConn ice_conn
,
1368 int offending_minor_opcode
,
1369 unsigned long offending_sequence
,
1378 ice_io_error_handler (IceConn ice_conn
)
1384 smc_error_handler (SmcConn smc_conn
,
1386 int offending_minor_opcode
,
1387 unsigned long offending_sequence
,