Manage locked and mandatory preferences
[nautilus-actions.git] / src / nact / egg-sm-client-xsmp.c
blob516baf0e82de3dd0e54c8c6fe6ff50fd1f5f1f53
1 /*
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.
24 #include "config.h"
26 #include "egg-sm-client.h"
27 #include "egg-sm-client-private.h"
29 #include "egg-desktop-file.h"
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <X11/SM/SMlib.h>
38 #include <gdk/gdk.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 )
44 #include <gdk/gdkx.h>
45 #endif
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.
64 typedef enum
66 XSMP_STATE_IDLE,
67 XSMP_STATE_SAVE_YOURSELF,
68 XSMP_STATE_INTERACT_REQUEST,
69 XSMP_STATE_INTERACT,
70 XSMP_STATE_SAVE_YOURSELF_DONE,
71 XSMP_STATE_SHUTDOWN_CANCELLED,
72 XSMP_STATE_CONNECTION_CLOSED
73 } EggSMClientXSMPState;
75 static const char *state_names[] = {
76 "idle",
77 "save-yourself",
78 "interact-request",
79 "interact",
80 "save-yourself-done",
81 "shutdown-cancelled",
82 "connection-closed"
85 #define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
87 struct _EggSMClientXSMP
89 EggSMClient parent;
91 SmcConn connection;
92 char *client_id;
94 EggSMClientXSMPState state;
95 char **restart_command;
96 gboolean set_restart_command;
97 int restart_style;
99 guint idle;
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;
108 /* Todo list */
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,
125 int argc,
126 const char **argv);
127 static void sm_client_xsmp_will_quit (EggSMClient *client,
128 gboolean will_quit);
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,
135 int save_style,
136 Bool shutdown,
137 int interact_style,
138 Bool fast);
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,
149 ...);
150 static SmProp *ptrarray_prop (const char *name,
151 GPtrArray *values);
152 static SmProp *string_prop (const char *name,
153 const char *value);
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,
171 Bool swap,
172 int offending_minor_opcode,
173 unsigned long offending_sequence,
174 int error_class,
175 int severity,
176 SmPointer values);
178 G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
180 static void
181 egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
183 xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
184 xsmp->connection = NULL;
185 xsmp->restart_style = SmRestartIfRunning;
188 static void
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;
199 EggSMClient *
200 egg_sm_client_xsmp_new (void)
202 if (!g_getenv ("SESSION_MANAGER"))
203 return NULL;
205 return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
208 static gboolean
209 sm_client_xsmp_set_initial_properties (gpointer user_data)
211 EggSMClientXSMP *xsmp = user_data;
212 EggDesktopFile *desktop_file;
213 GPtrArray *clone, *restart;
214 char pid_str[64];
216 g_debug( "egg_sm_client_xsmp_set_initial_properties" );
218 if (xsmp->idle)
220 g_source_remove (xsmp->idle);
221 xsmp->idle = 0;
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 ();
230 if (desktop_file)
232 GError *err = NULL;
233 char *cmdline, **argv;
234 int argc;
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);
250 g_strfreev (argv);
252 else
254 g_warning ("Could not parse Exec line in desktop file: %s",
255 err->message);
256 g_error_free (err);
258 g_free (cmdline);
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
272 * useful with it.
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),
282 NULL);
283 g_ptr_array_free (clone, TRUE);
284 g_ptr_array_free (restart, TRUE);
286 if (desktop_file)
288 set_properties (xsmp,
289 string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
290 NULL);
293 update_pending_events (xsmp);
294 return FALSE;
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).
301 static void
302 sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
304 SmcConn connection;
306 g_debug( "egg_sm_client_xsmp_disconnect" );
308 if (!xsmp->connection)
309 return;
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);
322 static void
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;
330 char *ret_client_id;
331 char error_string_ret[256];
333 xsmp->client_id = g_strdup (client_id);
335 ice_init ();
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;
348 client_id = NULL;
349 error_string_ret[0] = '\0';
350 xsmp->connection =
351 SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
352 SmcSaveYourselfProcMask | SmcDieProcMask |
353 SmcSaveCompleteProcMask |
354 SmcShutdownCancelledProcMask,
355 &callbacks,
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;
365 return;
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;
376 if (ret_client_id)
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);
385 #else
386 gdk_set_sm_client_id (xsmp->client_id);
387 #endif
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
399 * running.)
401 xsmp->waiting_to_set_initial_properties = TRUE;
402 xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
405 static void
406 sm_client_xsmp_set_restart_command (EggSMClient *client,
407 int argc,
408 const char **argv)
410 EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
411 int i;
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;
423 static void
424 sm_client_xsmp_will_quit (EggSMClient *client,
425 gboolean will_quit)
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
434 * signal.
436 xsmp->waiting_to_emit_quit = TRUE;
437 update_pending_events (xsmp);
438 return;
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);
447 return;
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)
456 save_state (xsmp);
458 g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
459 SmcSaveYourselfDone (xsmp->connection, will_quit);
460 xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
463 static gboolean
464 sm_client_xsmp_end_session (EggSMClient *client,
465 EggSMClientEndStyle style,
466 gboolean request_confirmation)
468 EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
469 int save_type;
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
484 * this happens.
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)
492 return TRUE;
494 switch (xsmp->state)
496 case XSMP_STATE_CONNECTION_CLOSED:
497 return FALSE;
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;
505 break;
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...
513 return TRUE;
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)
520 break;
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));
526 break;
528 default:
529 /* Hm... shouldn't happen */
530 return FALSE;
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;
541 else
542 save_type = SmSaveGlobal;
544 g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
545 SmcRequestSaveYourself (xsmp->connection,
546 save_type,
547 True, /* shutdown */
548 SmInteractStyleAny,
549 !request_confirmation, /* fast */
550 True /* global */);
551 return TRUE;
554 static gboolean
555 idle_do_pending_events (gpointer data)
557 EggSMClientXSMP *xsmp = data;
558 EggSMClient *client = data;
560 gdk_threads_enter ();
562 xsmp->idle = 0;
564 if (xsmp->waiting_to_emit_quit)
566 xsmp->waiting_to_emit_quit = FALSE;
567 egg_sm_client_quit (client);
568 goto out;
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);
584 out:
585 gdk_threads_leave ();
586 return FALSE;
589 static void
590 update_pending_events (EggSMClientXSMP *xsmp)
592 gboolean want_idle =
593 xsmp->waiting_to_emit_quit ||
594 xsmp->waiting_to_emit_quit_cancelled ||
595 xsmp->waiting_to_save_myself;
597 if (want_idle)
599 if (xsmp->idle == 0)
600 xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
602 else
604 if (xsmp->idle != 0)
605 g_source_remove (xsmp->idle);
606 xsmp->idle = 0;
610 static void
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;
630 /* SM callbacks */
632 static void
633 xsmp_save_yourself (SmcConn smc_conn,
634 SmPointer client_data,
635 int save_type,
636 Bool shutdown,
637 int interact_style,
638 Bool fast)
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);
656 return;
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 &&
671 !shutdown && !fast)
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;
680 return;
682 else
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
701 * for confirmation).
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
726 * signal.
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);
740 static void
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);
753 return;
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,
764 xsmp_interact,
765 xsmp);
766 return;
769 if (xsmp->need_save_state)
771 save_state (xsmp);
773 /* Though unlikely, the client could have been disconnected
774 * while the application was saving its state.
776 if (!xsmp->connection)
777 return;
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
786 * disagrees.
788 xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
791 static void
792 save_state (EggSMClientXSMP *xsmp)
794 GKeyFile *state_file;
795 char *state_file_path, *data;
796 EggDesktopFile *desktop_file;
797 GPtrArray *restart;
798 int offset, fd;
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);
806 if (!state_file)
808 restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
809 set_properties (xsmp,
810 ptrarray_prop (SmRestartCommand, restart),
811 NULL);
812 g_ptr_array_free (restart, TRUE);
813 delete_properties (xsmp, SmDiscardCommand, NULL);
814 return;
817 desktop_file = egg_get_desktop_file ();
818 if (desktop_file)
820 GKeyFile *merged_file;
821 char *desktop_file_path;
823 merged_file = g_key_file_new ();
824 desktop_file_path =
825 g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
826 NULL, NULL);
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))
832 guint g, k, i;
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],
842 keys[k], NULL);
843 if (value)
845 g_key_file_set_value (merged_file, groups[g],
846 keys[k], value);
847 g_free (value);
850 g_strfreev (keys);
852 g_strfreev (groups);
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,
859 NULL, "%k");
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,
869 exec);
870 g_free (exec);
872 else
873 desktop_file = NULL;
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);
886 offset = 0;
887 while (1)
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,
892 g_get_prgname (),
893 (long)time (NULL) + offset,
894 desktop_file ? "desktop" : "state");
896 fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
897 if (fd == -1)
899 if (errno == EEXIST)
901 offset++;
902 g_free (state_file_path);
903 continue;
905 else if (errno == ENOTDIR || errno == ENOENT)
907 char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
909 *sep = '\0';
910 if (g_mkdir_with_parents (state_file_path, 0755) != 0)
912 g_warning ("Could not create directory '%s'",
913 state_file_path);
914 g_free (state_file_path);
915 state_file_path = NULL;
916 break;
919 continue;
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;
926 break;
929 close (fd);
930 g_file_set_contents (state_file_path, data, -1, NULL);
931 break;
933 g_free (data);
935 restart = generate_command (xsmp->restart_command, xsmp->client_id,
936 state_file_path);
937 set_properties (xsmp,
938 ptrarray_prop (SmRestartCommand, restart),
939 NULL);
940 g_ptr_array_free (restart, TRUE);
942 if (state_file_path)
944 set_properties (xsmp,
945 array_prop (SmDiscardCommand,
946 "/bin/rm", "-rf", state_file_path,
947 NULL),
948 NULL);
949 g_free (state_file_path);
953 static void
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);
966 return;
969 xsmp->state = XSMP_STATE_INTERACT;
970 egg_sm_client_quit_requested (client);
973 static void
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);
987 static void
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;
998 else
999 fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
1002 static void
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);
1033 else
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;
1046 else
1048 /* The shutdown was cancelled before the application got a
1049 * chance to interact.
1051 xsmp->state = XSMP_STATE_IDLE;
1056 /* Utilities */
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.
1066 static GPtrArray *
1067 generate_command (char **restart_command, const char *client_id,
1068 const char *state_file)
1070 GPtrArray *cmd;
1071 int i;
1073 cmd = g_ptr_array_new ();
1074 g_ptr_array_add (cmd, restart_command[0]);
1076 if (client_id)
1078 g_ptr_array_add (cmd, (char *)"--sm-client-id");
1079 g_ptr_array_add (cmd, (char *)client_id);
1082 if (state_file)
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]);
1091 return cmd;
1094 /* Takes a NULL-terminated list of SmProp * values, created by
1095 * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
1096 * frees them.
1098 static void
1099 set_properties (EggSMClientXSMP *xsmp, ...)
1101 GPtrArray *props;
1102 SmProp *prop;
1103 va_list ap;
1104 guint i;
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);
1111 va_end (ap);
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);
1123 g_free (prop);
1125 g_ptr_array_free (props, TRUE);
1128 /* Takes a NULL-terminated list of property names and deletes them. */
1129 static void
1130 delete_properties (EggSMClientXSMP *xsmp, ...)
1132 GPtrArray *props;
1133 char *prop;
1134 va_list ap;
1136 if (!xsmp->connection)
1137 return;
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);
1144 va_end (ap);
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.
1156 static SmProp *
1157 array_prop (const char *name, ...)
1159 SmProp *prop;
1160 SmPropValue pv;
1161 GArray *vals;
1162 char *value;
1163 va_list ap;
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);
1175 pv.value = 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);
1184 return prop;
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.
1191 static SmProp *
1192 ptrarray_prop (const char *name, GPtrArray *values)
1194 SmProp *prop;
1195 SmPropValue pv;
1196 GArray *vals;
1197 guint i;
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);
1217 return prop;
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.
1224 static SmProp *
1225 string_prop (const char *name, const char *value)
1227 SmProp *prop;
1229 prop = g_new (SmProp, 1);
1230 prop->name = (char *)name;
1231 prop->type = (char *)SmARRAY8;
1233 prop->num_vals = 1;
1234 prop->vals = g_new (SmPropValue, 1);
1236 prop->vals[0].length = strlen (value);
1237 prop->vals[0].value = (char *)value;
1239 return prop;
1242 /* Takes a char and creates a CARD8 property. */
1243 static SmProp *
1244 card8_prop (const char *name, unsigned char value)
1246 SmProp *prop;
1247 char *card8val;
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
1251 * to store value.
1254 prop = g_new (SmProp, 1);
1255 prop->name = (char *)name;
1256 prop->type = (char *)SmCARD8;
1258 prop->num_vals = 1;
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;
1266 return prop;
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
1272 * libICE.)
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
1276 * directly.
1279 #include <X11/ICE/ICElib.h>
1280 #include <fcntl.h>
1282 static void ice_error_handler (IceConn ice_conn,
1283 Bool swap,
1284 int offending_minor_opcode,
1285 unsigned long offending_sequence,
1286 int error_class,
1287 int severity,
1288 IcePointer values);
1289 static void ice_io_error_handler (IceConn ice_conn);
1290 static void ice_connection_watch (IceConn ice_conn,
1291 IcePointer client_data,
1292 Bool opening,
1293 IcePointer *watch_data);
1295 static void
1296 ice_init (void)
1298 IceSetIOErrorHandler (ice_io_error_handler);
1299 IceSetErrorHandler (ice_error_handler);
1300 IceAddConnectionWatch (ice_connection_watch, NULL);
1303 static gboolean
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 ();
1312 switch (status)
1314 case IceProcessMessagesSuccess:
1315 return TRUE;
1317 case IceProcessMessagesIOError:
1318 sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
1319 return FALSE;
1321 case IceProcessMessagesConnectionClosed:
1322 return FALSE;
1324 default:
1325 g_assert_not_reached ();
1329 static gboolean
1330 ice_iochannel_watch (GIOChannel *channel,
1331 GIOCondition condition,
1332 gpointer client_data)
1334 return process_ice_messages (client_data);
1337 static void
1338 ice_connection_watch (IceConn ice_conn,
1339 IcePointer client_data,
1340 Bool opening,
1341 IcePointer *watch_data)
1343 guint watch_id;
1345 if (opening)
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);
1358 else
1360 watch_id = GPOINTER_TO_UINT (*watch_data);
1361 g_source_remove (watch_id);
1365 static void
1366 ice_error_handler (IceConn ice_conn,
1367 Bool swap,
1368 int offending_minor_opcode,
1369 unsigned long offending_sequence,
1370 int error_class,
1371 int severity,
1372 IcePointer values)
1374 /* Do nothing */
1377 static void
1378 ice_io_error_handler (IceConn ice_conn)
1380 /* Do nothing */
1383 static void
1384 smc_error_handler (SmcConn smc_conn,
1385 Bool swap,
1386 int offending_minor_opcode,
1387 unsigned long offending_sequence,
1388 int error_class,
1389 int severity,
1390 SmPointer values)
1392 /* Do nothing */