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 "eggsmclient.h"
27
#include "eggsmclient-private.h"
29
#include "eggdesktopfile.h"
36
#include <X11/SM/SMlib.h>
40
#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
41
#define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
42
#define EGG_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
43
#define EGG_IS_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP))
44
#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP))
45
#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
47
typedef struct _EggSMClientXSMP EggSMClientXSMP;
48
typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass;
50
/* These mostly correspond to the similarly-named states in section
51
* 9.1 of the XSMP spec. Some of the states there aren't represented
52
* here, because we don't need them. SHUTDOWN_CANCELLED is slightly
53
* different from the spec; we use it when the client is IDLE after a
54
* ShutdownCancelled message, but the application is still interacting
55
* and doesn't know the shutdown has been cancelled yet.
60
XSMP_STATE_SAVE_YOURSELF,
61
XSMP_STATE_INTERACT_REQUEST,
63
XSMP_STATE_SAVE_YOURSELF_DONE,
64
XSMP_STATE_SHUTDOWN_CANCELLED,
65
XSMP_STATE_CONNECTION_CLOSED
66
} EggSMClientXSMPState;
68
static const char *state_names[] = {
78
#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
80
struct _EggSMClientXSMP
87
EggSMClientXSMPState state;
88
char **restart_command;
89
gboolean set_restart_command;
94
/* Current SaveYourself state */
95
guint expecting_initial_save_yourself : 1;
96
guint need_save_state : 1;
97
guint need_quit_requested : 1;
98
guint interact_errors : 1;
99
guint shutting_down : 1;
102
guint waiting_to_set_initial_properties : 1;
103
guint waiting_to_emit_quit : 1;
104
guint waiting_to_emit_quit_cancelled : 1;
105
guint waiting_to_save_myself : 1;
109
struct _EggSMClientXSMPClass
111
EggSMClientClass parent_class;
115
static void sm_client_xsmp_startup (EggSMClient *client,
116
const char *client_id);
117
static void sm_client_xsmp_set_restart_command (EggSMClient *client,
120
static void sm_client_xsmp_will_quit (EggSMClient *client,
122
static gboolean sm_client_xsmp_end_session (EggSMClient *client,
123
EggSMClientEndStyle style,
124
gboolean request_confirmation);
126
static void xsmp_save_yourself (SmcConn smc_conn,
127
SmPointer client_data,
132
static void xsmp_die (SmcConn smc_conn,
133
SmPointer client_data);
134
static void xsmp_save_complete (SmcConn smc_conn,
135
SmPointer client_data);
136
static void xsmp_shutdown_cancelled (SmcConn smc_conn,
137
SmPointer client_data);
138
static void xsmp_interact (SmcConn smc_conn,
139
SmPointer client_data);
141
static SmProp *array_prop (const char *name,
143
static SmProp *ptrarray_prop (const char *name,
145
static SmProp *string_prop (const char *name,
147
static SmProp *card8_prop (const char *name,
148
unsigned char value);
150
static void set_properties (EggSMClientXSMP *xsmp, ...);
151
static void delete_properties (EggSMClientXSMP *xsmp, ...);
153
static GPtrArray *generate_command (char **restart_command,
154
const char *client_id,
155
const char *state_file);
157
static void save_state (EggSMClientXSMP *xsmp);
158
static void do_save_yourself (EggSMClientXSMP *xsmp);
159
static void update_pending_events (EggSMClientXSMP *xsmp);
161
static void ice_init (void);
162
static gboolean process_ice_messages (IceConn ice_conn);
163
static void smc_error_handler (SmcConn smc_conn,
165
int offending_minor_opcode,
166
unsigned long offending_sequence,
171
G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
174
egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
176
xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
177
xsmp->connection = NULL;
178
xsmp->restart_style = SmRestartIfRunning;
182
egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
184
EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
186
sm_client_class->startup = sm_client_xsmp_startup;
187
sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
188
sm_client_class->will_quit = sm_client_xsmp_will_quit;
189
sm_client_class->end_session = sm_client_xsmp_end_session;
193
egg_sm_client_xsmp_new (void)
195
if (!g_getenv ("SESSION_MANAGER"))
198
return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
202
sm_client_xsmp_set_initial_properties (gpointer user_data)
204
EggSMClientXSMP *xsmp = user_data;
205
EggDesktopFile *desktop_file;
206
GPtrArray *clone, *restart;
211
g_source_remove (xsmp->idle);
214
xsmp->waiting_to_set_initial_properties = FALSE;
216
if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
217
xsmp->restart_style = SmRestartNever;
219
/* Parse info out of desktop file */
220
desktop_file = egg_get_desktop_file ();
224
char *cmdline, **argv;
227
if (xsmp->restart_style == SmRestartIfRunning)
229
if (egg_desktop_file_get_boolean (desktop_file,
230
"X-GNOME-AutoRestart", NULL))
231
xsmp->restart_style = SmRestartImmediately;
234
if (!xsmp->set_restart_command)
236
cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
237
if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
239
egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
240
argc, (const char **)argv);
245
g_warning ("Could not parse Exec line in desktop file: %s",
253
if (!xsmp->set_restart_command)
254
xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
256
clone = generate_command (xsmp->restart_command, NULL, NULL);
257
restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
259
g_debug ("Setting initial properties");
261
/* Program, CloneCommand, RestartCommand, and UserID are required.
262
* ProcessID isn't required, but the SM may be able to do something
265
g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
266
set_properties (xsmp,
267
string_prop (SmProgram, g_get_prgname ()),
268
ptrarray_prop (SmCloneCommand, clone),
269
ptrarray_prop (SmRestartCommand, restart),
270
string_prop (SmUserID, g_get_user_name ()),
271
string_prop (SmProcessID, pid_str),
272
card8_prop (SmRestartStyleHint, xsmp->restart_style),
274
g_ptr_array_free (clone, TRUE);
275
g_ptr_array_free (restart, TRUE);
279
set_properties (xsmp,
280
string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
284
update_pending_events (xsmp);
288
/* This gets called from two different places: xsmp_die() (when the
289
* server asks us to disconnect) and process_ice_messages() (when the
290
* server disconnects unexpectedly).
293
sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
297
if (!xsmp->connection)
300
g_debug ("Disconnecting");
302
connection = xsmp->connection;
303
xsmp->connection = NULL;
304
SmcCloseConnection (connection, 0, NULL);
305
xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
307
xsmp->waiting_to_save_myself = FALSE;
308
update_pending_events (xsmp);
312
sm_client_xsmp_startup (EggSMClient *client,
313
const char *client_id)
315
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
316
SmcCallbacks callbacks;
318
char error_string_ret[256];
320
xsmp->client_id = g_strdup (client_id);
323
SmcSetErrorHandler (smc_error_handler);
325
callbacks.save_yourself.callback = xsmp_save_yourself;
326
callbacks.die.callback = xsmp_die;
327
callbacks.save_complete.callback = xsmp_save_complete;
328
callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
330
callbacks.save_yourself.client_data = xsmp;
331
callbacks.die.client_data = xsmp;
332
callbacks.save_complete.client_data = xsmp;
333
callbacks.shutdown_cancelled.client_data = xsmp;
336
error_string_ret[0] = '\0';
338
SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
339
SmcSaveYourselfProcMask | SmcDieProcMask |
340
SmcSaveCompleteProcMask |
341
SmcShutdownCancelledProcMask,
343
xsmp->client_id, &ret_client_id,
344
sizeof (error_string_ret), error_string_ret);
346
if (!xsmp->connection)
348
g_warning ("Failed to connect to the session manager: %s\n",
349
error_string_ret[0] ?
350
error_string_ret : "no error message given");
351
xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
355
/* We expect a pointless initial SaveYourself if either (a) we
356
* didn't have an initial client ID, or (b) we DID have an initial
357
* client ID, but the server rejected it and gave us a new one.
359
if (!xsmp->client_id ||
360
(ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
361
xsmp->expecting_initial_save_yourself = TRUE;
365
g_free (xsmp->client_id);
366
xsmp->client_id = g_strdup (ret_client_id);
367
free (ret_client_id);
369
gdk_threads_enter ();
370
gdk_set_sm_client_id (xsmp->client_id);
371
gdk_threads_leave ();
373
g_debug ("Got client ID \"%s\"", xsmp->client_id);
376
xsmp->state = XSMP_STATE_IDLE;
378
/* Do not set the initial properties until we reach the main loop,
379
* so that the application has a chance to call
380
* egg_set_desktop_file(). (This may also help the session manager
381
* have a better idea of when the application is fully up and
384
xsmp->waiting_to_set_initial_properties = TRUE;
385
xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
389
sm_client_xsmp_set_restart_command (EggSMClient *client,
393
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
396
g_strfreev (xsmp->restart_command);
398
xsmp->restart_command = g_new (char *, argc + 1);
399
for (i = 0; i < argc; i++)
400
xsmp->restart_command[i] = g_strdup (argv[i]);
401
xsmp->restart_command[i] = NULL;
403
xsmp->set_restart_command = TRUE;
407
sm_client_xsmp_will_quit (EggSMClient *client,
410
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
412
if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
414
/* The session manager has already exited! Schedule a quit
417
xsmp->waiting_to_emit_quit = TRUE;
418
update_pending_events (xsmp);
421
else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
423
/* We received a ShutdownCancelled message while the application
424
* was interacting; Schedule a quit_cancelled signal.
426
xsmp->waiting_to_emit_quit_cancelled = TRUE;
427
update_pending_events (xsmp);
431
g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
433
g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
434
SmcInteractDone (xsmp->connection, !will_quit);
436
if (will_quit && xsmp->need_save_state)
439
g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
440
SmcSaveYourselfDone (xsmp->connection, will_quit);
441
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
445
sm_client_xsmp_end_session (EggSMClient *client,
446
EggSMClientEndStyle style,
447
gboolean request_confirmation)
449
EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
452
/* To end the session via XSMP, we have to send a
453
* SaveYourselfRequest. We aren't allowed to do that if anything
454
* else is going on, but we don't want to expose this fact to the
455
* application. So we do our best to patch things up here...
457
* In the worst case, this method might block for some length of
458
* time in process_ice_messages, but the only time that code path is
459
* honestly likely to get hit is if the application tries to end the
460
* session as the very first thing it does, in which case it
461
* probably won't actually block anyway. It's not worth gunking up
462
* the API to try to deal nicely with the other 0.01% of cases where
466
while (xsmp->state != XSMP_STATE_IDLE ||
467
xsmp->expecting_initial_save_yourself)
469
/* If we're already shutting down, we don't need to do anything. */
470
if (xsmp->shutting_down)
475
case XSMP_STATE_CONNECTION_CLOSED:
478
case XSMP_STATE_SAVE_YOURSELF:
479
/* Trying to log out from the save_state callback? Whatever.
480
* Abort the save_state.
482
SmcSaveYourselfDone (xsmp->connection, FALSE);
483
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
486
case XSMP_STATE_INTERACT_REQUEST:
487
case XSMP_STATE_INTERACT:
488
case XSMP_STATE_SHUTDOWN_CANCELLED:
489
/* Already in a shutdown-related state, just ignore
490
* the new shutdown request...
494
case XSMP_STATE_IDLE:
495
if (xsmp->waiting_to_set_initial_properties)
496
sm_client_xsmp_set_initial_properties (xsmp);
498
if (!xsmp->expecting_initial_save_yourself)
500
/* else fall through */
502
case XSMP_STATE_SAVE_YOURSELF_DONE:
503
/* We need to wait for some response from the server.*/
504
process_ice_messages (SmcGetIceConnection (xsmp->connection));
508
/* Hm... shouldn't happen */
513
/* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
514
* the user chooses to save the session. But gnome-session will do
515
* the wrong thing if we pass SmSaveBoth and the user chooses NOT to
516
* save the session... Sigh.
518
if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
519
save_type = SmSaveBoth;
521
save_type = SmSaveGlobal;
523
g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
524
SmcRequestSaveYourself (xsmp->connection,
528
!request_confirmation, /* fast */
534
idle_do_pending_events (gpointer data)
536
EggSMClientXSMP *xsmp = data;
537
EggSMClient *client = data;
539
gdk_threads_enter ();
543
if (xsmp->waiting_to_emit_quit)
545
xsmp->waiting_to_emit_quit = FALSE;
546
egg_sm_client_quit (client);
550
if (xsmp->waiting_to_emit_quit_cancelled)
552
xsmp->waiting_to_emit_quit_cancelled = FALSE;
553
egg_sm_client_quit_cancelled (client);
554
xsmp->state = XSMP_STATE_IDLE;
557
if (xsmp->waiting_to_save_myself)
559
xsmp->waiting_to_save_myself = FALSE;
560
do_save_yourself (xsmp);
564
gdk_threads_leave ();
569
update_pending_events (EggSMClientXSMP *xsmp)
572
xsmp->waiting_to_emit_quit ||
573
xsmp->waiting_to_emit_quit_cancelled ||
574
xsmp->waiting_to_save_myself;
579
xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
584
g_source_remove (xsmp->idle);
590
fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
591
gboolean send_interact_done,
592
gboolean send_save_yourself_done)
594
g_warning ("Received XSMP %s message in state %s: client or server error",
595
message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
597
/* Forget any pending SaveYourself plans we had */
598
xsmp->waiting_to_save_myself = FALSE;
599
update_pending_events (xsmp);
601
if (send_interact_done)
602
SmcInteractDone (xsmp->connection, False);
603
if (send_save_yourself_done)
604
SmcSaveYourselfDone (xsmp->connection, True);
606
xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
612
xsmp_save_yourself (SmcConn smc_conn,
613
SmPointer client_data,
619
EggSMClientXSMP *xsmp = client_data;
620
gboolean wants_quit_requested;
622
g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
623
save_type == SmSaveLocal ? "SmSaveLocal" :
624
save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
625
shutdown ? "Shutdown" : "!Shutdown",
626
interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
627
interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
628
"SmInteractStyleNone", fast ? "Fast" : "!Fast",
629
EGG_SM_CLIENT_XSMP_STATE (xsmp));
631
if (xsmp->state != XSMP_STATE_IDLE &&
632
xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
634
fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
638
if (xsmp->waiting_to_set_initial_properties)
639
sm_client_xsmp_set_initial_properties (xsmp);
641
/* If this is the initial SaveYourself, ignore it; we've already set
642
* properties and there's no reason to actually save state too.
644
if (xsmp->expecting_initial_save_yourself)
646
xsmp->expecting_initial_save_yourself = FALSE;
648
if (save_type == SmSaveLocal &&
649
interact_style == SmInteractStyleNone &&
652
g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
653
SmcSaveYourselfDone (xsmp->connection, True);
654
/* As explained in the comment at the end of
655
* do_save_yourself(), SAVE_YOURSELF_DONE is the correct
656
* state here, not IDLE.
658
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
662
g_warning ("First SaveYourself was not the expected one!");
665
/* Even ignoring the "fast" flag completely, there are still 18
666
* different combinations of save_type, shutdown and interact_style.
667
* We interpret them as follows:
669
* Type Shutdown Interact Interpretation
670
* G F A/E/N do nothing (1)
671
* G T N do nothing (1)*
672
* G T A/E quit_requested (2)
673
* L/B F A/E/N save_state (3)
674
* L/B T N save_state (3)*
675
* L/B T A/E quit_requested, then save_state (4)
677
* 1. Do nothing, because the SM asked us to do something
678
* uninteresting (save open files, but then don't quit
679
* afterward) or rude (save open files without asking the user
682
* 2. Request interaction and then emit ::quit_requested. This
683
* perhaps isn't quite correct for the SmInteractStyleErrors
684
* case, but we don't care.
686
* 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
687
* rows essentially get demoted to SmSaveLocal, because their
688
* Global halves correspond to "do nothing".
690
* 4. Request interaction, emit ::quit_requested, and then emit
691
* ::save_state after interacting. This is the SmSaveBoth
692
* equivalent of #2, but we also promote SmSaveLocal shutdown
693
* SaveYourselfs to SmSaveBoth here, because we want to give
694
* the user a chance to save open files before quitting.
696
* (* It would be nice if we could do something useful when the
697
* session manager sends a SaveYourself with shutdown True and
698
* SmInteractStyleNone. But we can't, so we just pretend it didn't
699
* even tell us it was shutting down. The docs for ::quit mention
700
* that it might not always be preceded by ::quit_requested.)
703
/* As an optimization, we don't actually request interaction and
704
* emit ::quit_requested if the application isn't listening to the
707
wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
709
xsmp->need_save_state = (save_type != SmSaveGlobal);
710
xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
711
interact_style != SmInteractStyleNone);
712
xsmp->interact_errors = (interact_style == SmInteractStyleErrors);
714
xsmp->shutting_down = shutdown;
716
do_save_yourself (xsmp);
720
do_save_yourself (EggSMClientXSMP *xsmp)
722
if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
724
/* The SM cancelled a previous SaveYourself, but we haven't yet
725
* had a chance to tell the application, so we can't start
726
* processing this SaveYourself yet.
728
xsmp->waiting_to_save_myself = TRUE;
729
update_pending_events (xsmp);
733
if (xsmp->need_quit_requested)
735
xsmp->state = XSMP_STATE_INTERACT_REQUEST;
737
g_debug ("Sending InteractRequest(%s)",
738
xsmp->interact_errors ? "Error" : "Normal");
739
SmcInteractRequest (xsmp->connection,
740
xsmp->interact_errors ? SmDialogError : SmDialogNormal,
746
if (xsmp->need_save_state)
750
/* Though unlikely, the client could have been disconnected
751
* while the application was saving its state.
753
if (!xsmp->connection)
757
g_debug ("Sending SaveYourselfDone(True)");
758
SmcSaveYourselfDone (xsmp->connection, True);
760
/* The client state diagram in the XSMP spec says that after a
761
* non-shutdown SaveYourself, we go directly back to "idle". But
762
* everything else in both the XSMP spec and the libSM docs
765
xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
769
save_state (EggSMClientXSMP *xsmp)
771
GKeyFile *state_file;
772
char *state_file_path, *data;
773
EggDesktopFile *desktop_file;
777
/* We set xsmp->state before emitting save_state, but our caller is
778
* responsible for setting it back afterward.
780
xsmp->state = XSMP_STATE_SAVE_YOURSELF;
782
state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
785
restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
786
set_properties (xsmp,
787
ptrarray_prop (SmRestartCommand, restart),
789
g_ptr_array_free (restart, TRUE);
790
delete_properties (xsmp, SmDiscardCommand, NULL);
794
desktop_file = egg_get_desktop_file ();
797
GKeyFile *merged_file;
798
char *desktop_file_path;
800
merged_file = g_key_file_new ();
802
g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
804
if (desktop_file_path &&
805
g_key_file_load_from_file (merged_file, desktop_file_path,
806
G_KEY_FILE_KEEP_COMMENTS |
807
G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
810
char **groups, **keys, *value, *exec;
812
groups = g_key_file_get_groups (state_file, NULL);
813
for (g = 0; groups[g]; g++)
815
keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
816
for (k = 0; keys[k]; k++)
818
value = g_key_file_get_value (state_file, groups[g],
822
g_key_file_set_value (merged_file, groups[g],
831
g_key_file_free (state_file);
832
state_file = merged_file;
834
/* Update Exec key using "--sm-client-state-file %k" */
835
restart = generate_command (xsmp->restart_command,
837
for (i = 0; i < restart->len; i++)
838
restart->pdata[i] = g_shell_quote (restart->pdata[i]);
839
g_ptr_array_add (restart, NULL);
840
exec = g_strjoinv (" ", (char **)restart->pdata);
841
g_strfreev ((char **)restart->pdata);
842
g_ptr_array_free (restart, FALSE);
844
g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
845
EGG_DESKTOP_FILE_KEY_EXEC,
852
g_free (desktop_file_path);
855
/* Now write state_file to disk. (We can't use mktemp(), because
856
* that requires the filename to end with "XXXXXX", and we want
857
* it to end with ".desktop".)
860
data = g_key_file_to_data (state_file, NULL, NULL);
861
g_key_file_free (state_file);
866
state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
867
g_get_user_config_dir (),
868
G_DIR_SEPARATOR, G_DIR_SEPARATOR,
870
(long)time (NULL) + offset,
871
desktop_file ? "desktop" : "state");
873
fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
879
g_free (state_file_path);
882
else if (errno == ENOTDIR || errno == ENOENT)
884
char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
887
if (g_mkdir_with_parents (state_file_path, 0755) != 0)
889
g_warning ("Could not create directory '%s'",
891
g_free (state_file_path);
892
state_file_path = NULL;
899
g_warning ("Could not create file '%s': %s",
900
state_file_path, g_strerror (errno));
901
g_free (state_file_path);
902
state_file_path = NULL;
907
g_file_set_contents (state_file_path, data, -1, NULL);
912
restart = generate_command (xsmp->restart_command, xsmp->client_id,
914
set_properties (xsmp,
915
ptrarray_prop (SmRestartCommand, restart),
917
g_ptr_array_free (restart, TRUE);
921
set_properties (xsmp,
922
array_prop (SmDiscardCommand,
923
"/bin/rm", "-rf", state_file_path,
926
g_free (state_file_path);
931
xsmp_interact (SmcConn smc_conn,
932
SmPointer client_data)
934
EggSMClientXSMP *xsmp = client_data;
935
EggSMClient *client = client_data;
937
g_debug ("Received Interact message in state %s",
938
EGG_SM_CLIENT_XSMP_STATE (xsmp));
940
if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
942
fix_broken_state (xsmp, "Interact", TRUE, TRUE);
946
xsmp->state = XSMP_STATE_INTERACT;
947
egg_sm_client_quit_requested (client);
951
xsmp_die (SmcConn smc_conn,
952
SmPointer client_data)
954
EggSMClientXSMP *xsmp = client_data;
955
EggSMClient *client = client_data;
957
g_debug ("Received Die message in state %s",
958
EGG_SM_CLIENT_XSMP_STATE (xsmp));
960
sm_client_xsmp_disconnect (xsmp);
961
egg_sm_client_quit (client);
965
xsmp_save_complete (SmcConn smc_conn,
966
SmPointer client_data)
968
EggSMClientXSMP *xsmp = client_data;
970
g_debug ("Received SaveComplete message in state %s",
971
EGG_SM_CLIENT_XSMP_STATE (xsmp));
973
if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
974
xsmp->state = XSMP_STATE_IDLE;
976
fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
980
xsmp_shutdown_cancelled (SmcConn smc_conn,
981
SmPointer client_data)
983
EggSMClientXSMP *xsmp = client_data;
984
EggSMClient *client = client_data;
986
g_debug ("Received ShutdownCancelled message in state %s",
987
EGG_SM_CLIENT_XSMP_STATE (xsmp));
989
xsmp->shutting_down = FALSE;
991
if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
993
/* We've finished interacting and now the SM has agreed to
994
* cancel the shutdown.
996
xsmp->state = XSMP_STATE_IDLE;
997
egg_sm_client_quit_cancelled (client);
999
else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
1001
/* Hm... ok, so we got a shutdown SaveYourself, which got
1002
* cancelled, but the application was still interacting, so we
1003
* didn't tell it yet, and then *another* SaveYourself arrived,
1004
* which we must still be waiting to tell the app about, except
1005
* that now that SaveYourself has been cancelled too! Dizzy yet?
1007
xsmp->waiting_to_save_myself = FALSE;
1008
update_pending_events (xsmp);
1012
g_debug ("Sending SaveYourselfDone(False)");
1013
SmcSaveYourselfDone (xsmp->connection, False);
1015
if (xsmp->state == XSMP_STATE_INTERACT)
1017
/* The application is currently interacting, so we can't
1018
* tell it about the cancellation yet; we will wait until
1019
* after it calls egg_sm_client_will_quit().
1021
xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
1025
/* The shutdown was cancelled before the application got a
1026
* chance to interact.
1028
xsmp->state = XSMP_STATE_IDLE;
1035
/* Create a restart/clone/Exec command based on @restart_command.
1036
* If @client_id is non-%NULL, add "--sm-client-id @client_id".
1037
* If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
1039
* None of the input strings are g_strdup()ed; the caller must keep
1040
* them around until it is done with the returned GPtrArray, and must
1041
* then free the array, but not its contents.
1044
generate_command (char **restart_command, const char *client_id,
1045
const char *state_file)
1050
cmd = g_ptr_array_new ();
1051
g_ptr_array_add (cmd, restart_command[0]);
1055
g_ptr_array_add (cmd, (char *)"--sm-client-id");
1056
g_ptr_array_add (cmd, (char *)client_id);
1061
g_ptr_array_add (cmd, (char *)"--sm-client-state-file");
1062
g_ptr_array_add (cmd, (char *)state_file);
1065
for (i = 1; restart_command[i]; i++)
1066
g_ptr_array_add (cmd, restart_command[i]);
1071
/* Takes a NULL-terminated list of SmProp * values, created by
1072
* array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
1076
set_properties (EggSMClientXSMP *xsmp, ...)
1083
props = g_ptr_array_new ();
1085
va_start (ap, xsmp);
1086
while ((prop = va_arg (ap, SmProp *)))
1087
g_ptr_array_add (props, prop);
1090
if (xsmp->connection)
1092
SmcSetProperties (xsmp->connection, props->len,
1093
(SmProp **)props->pdata);
1096
for (i = 0; i < props->len; i++)
1098
prop = props->pdata[i];
1099
g_free (prop->vals);
1102
g_ptr_array_free (props, TRUE);
1105
/* Takes a NULL-terminated list of property names and deletes them. */
1107
delete_properties (EggSMClientXSMP *xsmp, ...)
1113
if (!xsmp->connection)
1116
props = g_ptr_array_new ();
1118
va_start (ap, xsmp);
1119
while ((prop = va_arg (ap, char *)))
1120
g_ptr_array_add (props, prop);
1123
SmcDeleteProperties (xsmp->connection, props->len,
1124
(char **)props->pdata);
1126
g_ptr_array_free (props, TRUE);
1129
/* Takes an array of strings and creates a LISTofARRAY8 property. The
1130
* strings are neither dupped nor freed; they need to remain valid
1131
* until you're done with the SmProp.
1134
array_prop (const char *name, ...)
1142
prop = g_new (SmProp, 1);
1143
prop->name = (char *)name;
1144
prop->type = (char *)SmLISTofARRAY8;
1146
vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
1148
va_start (ap, name);
1149
while ((value = va_arg (ap, char *)))
1151
pv.length = strlen (value);
1153
g_array_append_val (vals, pv);
1156
prop->num_vals = vals->len;
1157
prop->vals = (SmPropValue *)vals->data;
1159
g_array_free (vals, FALSE);
1164
/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
1165
* The array contents are neither dupped nor freed; they need to
1166
* remain valid until you're done with the SmProp.
1169
ptrarray_prop (const char *name, GPtrArray *values)
1176
prop = g_new (SmProp, 1);
1177
prop->name = (char *)name;
1178
prop->type = (char *)SmLISTofARRAY8;
1180
vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
1182
for (i = 0; i < values->len; i++)
1184
pv.length = strlen (values->pdata[i]);
1185
pv.value = values->pdata[i];
1186
g_array_append_val (vals, pv);
1189
prop->num_vals = vals->len;
1190
prop->vals = (SmPropValue *)vals->data;
1192
g_array_free (vals, FALSE);
1197
/* Takes a string and creates an ARRAY8 property. The string is
1198
* neither dupped nor freed; it needs to remain valid until you're
1199
* done with the SmProp.
1202
string_prop (const char *name, const char *value)
1206
prop = g_new (SmProp, 1);
1207
prop->name = (char *)name;
1208
prop->type = (char *)SmARRAY8;
1211
prop->vals = g_new (SmPropValue, 1);
1213
prop->vals[0].length = strlen (value);
1214
prop->vals[0].value = (char *)value;
1219
/* Takes a char and creates a CARD8 property. */
1221
card8_prop (const char *name, unsigned char value)
1226
/* To avoid having to allocate and free prop->vals[0], we cheat and
1227
* make vals a 2-element-long array and then use the second element
1231
prop = g_new (SmProp, 1);
1232
prop->name = (char *)name;
1233
prop->type = (char *)SmCARD8;
1236
prop->vals = g_new (SmPropValue, 2);
1237
card8val = (char *)(&prop->vals[1]);
1238
card8val[0] = value;
1240
prop->vals[0].length = 1;
1241
prop->vals[0].value = card8val;
1246
/* ICE code. This makes no effort to play nice with anyone else trying
1247
* to use libICE. Fortunately, no one uses libICE for anything other
1248
* than SM. (DCOP uses ICE, but it has its own private copy of
1251
* When this moves to gtk, it will need to be cleverer, to avoid
1252
* tripping over old apps that use GnomeClient or that use libSM
1256
#include <X11/ICE/ICElib.h>
1259
static void ice_error_handler (IceConn ice_conn,
1261
int offending_minor_opcode,
1262
unsigned long offending_sequence,
1266
static void ice_io_error_handler (IceConn ice_conn);
1267
static void ice_connection_watch (IceConn ice_conn,
1268
IcePointer client_data,
1270
IcePointer *watch_data);
1275
IceSetIOErrorHandler (ice_io_error_handler);
1276
IceSetErrorHandler (ice_error_handler);
1277
IceAddConnectionWatch (ice_connection_watch, NULL);
1281
process_ice_messages (IceConn ice_conn)
1283
IceProcessMessagesStatus status;
1285
gdk_threads_enter ();
1286
status = IceProcessMessages (ice_conn, NULL, NULL);
1287
gdk_threads_leave ();
1291
case IceProcessMessagesSuccess:
1294
case IceProcessMessagesIOError:
1295
sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
1298
case IceProcessMessagesConnectionClosed:
1302
g_assert_not_reached ();
1307
ice_iochannel_watch (GIOChannel *channel,
1308
GIOCondition condition,
1309
gpointer client_data)
1311
return process_ice_messages (client_data);
1315
ice_connection_watch (IceConn ice_conn,
1316
IcePointer client_data,
1318
IcePointer *watch_data)
1324
GIOChannel *channel;
1325
int fd = IceConnectionNumber (ice_conn);
1327
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
1328
channel = g_io_channel_unix_new (fd);
1329
watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
1330
ice_iochannel_watch, ice_conn);
1331
g_io_channel_unref (channel);
1333
*watch_data = GUINT_TO_POINTER (watch_id);
1337
watch_id = GPOINTER_TO_UINT (*watch_data);
1338
g_source_remove (watch_id);
1343
ice_error_handler (IceConn ice_conn,
1345
int offending_minor_opcode,
1346
unsigned long offending_sequence,
1355
ice_io_error_handler (IceConn ice_conn)
1361
smc_error_handler (SmcConn smc_conn,
1363
int offending_minor_opcode,
1364
unsigned long offending_sequence,