~ubuntu-branches/ubuntu/maverick/gnome-session/maverick

« back to all changes in this revision

Viewing changes to egg/eggsmclient-xsmp.c

  • Committer: Bazaar Package Importer
  • Author(s): Josselin Mouette
  • Date: 2009-04-14 19:24:09 UTC
  • mto: (1.3.1 upstream) (2.1.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 112.
  • Revision ID: james.westby@ubuntu.com-20090414192409-koxw9bt1lf1zr01z
ImportĀ upstreamĀ versionĀ 2.26.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007 Novell, Inc.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 *
 
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.
 
22
 */
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#include "eggsmclient.h"
 
27
#include "eggsmclient-private.h"
 
28
 
 
29
#include "eggdesktopfile.h"
 
30
 
 
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>
 
37
 
 
38
#include <gdk/gdk.h>
 
39
 
 
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))
 
46
 
 
47
typedef struct _EggSMClientXSMP        EggSMClientXSMP;
 
48
typedef struct _EggSMClientXSMPClass   EggSMClientXSMPClass;
 
49
 
 
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.
 
56
 */
 
57
typedef enum
 
58
{
 
59
  XSMP_STATE_IDLE,
 
60
  XSMP_STATE_SAVE_YOURSELF,
 
61
  XSMP_STATE_INTERACT_REQUEST,
 
62
  XSMP_STATE_INTERACT,
 
63
  XSMP_STATE_SAVE_YOURSELF_DONE,
 
64
  XSMP_STATE_SHUTDOWN_CANCELLED,
 
65
  XSMP_STATE_CONNECTION_CLOSED
 
66
} EggSMClientXSMPState;
 
67
 
 
68
static const char *state_names[] = {
 
69
  "idle",
 
70
  "save-yourself",
 
71
  "interact-request",
 
72
  "interact",
 
73
  "save-yourself-done",
 
74
  "shutdown-cancelled",
 
75
  "connection-closed"
 
76
};
 
77
 
 
78
#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
 
79
 
 
80
struct _EggSMClientXSMP
 
81
{
 
82
  EggSMClient parent;
 
83
 
 
84
  SmcConn connection;
 
85
  char *client_id;
 
86
 
 
87
  EggSMClientXSMPState state;
 
88
  char **restart_command;
 
89
  gboolean set_restart_command;
 
90
  int restart_style;
 
91
 
 
92
  guint idle;
 
93
 
 
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;
 
100
 
 
101
  /* Todo list */
 
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;
 
106
 
 
107
};
 
108
 
 
109
struct _EggSMClientXSMPClass
 
110
{
 
111
  EggSMClientClass parent_class;
 
112
 
 
113
};
 
114
 
 
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,
 
118
                                                    int           argc,
 
119
                                                    const char  **argv);
 
120
static void     sm_client_xsmp_will_quit (EggSMClient *client,
 
121
                                          gboolean     will_quit);
 
122
static gboolean sm_client_xsmp_end_session (EggSMClient         *client,
 
123
                                            EggSMClientEndStyle  style,
 
124
                                            gboolean  request_confirmation);
 
125
 
 
126
static void xsmp_save_yourself      (SmcConn   smc_conn,
 
127
                                     SmPointer client_data,
 
128
                                     int       save_style,
 
129
                                     Bool      shutdown,
 
130
                                     int       interact_style,
 
131
                                     Bool      fast);
 
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);
 
140
 
 
141
static SmProp *array_prop        (const char    *name,
 
142
                                  ...);
 
143
static SmProp *ptrarray_prop     (const char    *name,
 
144
                                  GPtrArray     *values);
 
145
static SmProp *string_prop       (const char    *name,
 
146
                                  const char    *value);
 
147
static SmProp *card8_prop        (const char    *name,
 
148
                                  unsigned char  value);
 
149
 
 
150
static void set_properties         (EggSMClientXSMP *xsmp, ...);
 
151
static void delete_properties      (EggSMClientXSMP *xsmp, ...);
 
152
 
 
153
static GPtrArray *generate_command (char       **restart_command,
 
154
                                    const char  *client_id,
 
155
                                    const char  *state_file);
 
156
 
 
157
static void save_state            (EggSMClientXSMP *xsmp);
 
158
static void do_save_yourself      (EggSMClientXSMP *xsmp);
 
159
static void update_pending_events (EggSMClientXSMP *xsmp);
 
160
 
 
161
static void     ice_init             (void);
 
162
static gboolean process_ice_messages (IceConn       ice_conn);
 
163
static void     smc_error_handler    (SmcConn       smc_conn,
 
164
                                      Bool          swap,
 
165
                                      int           offending_minor_opcode,
 
166
                                      unsigned long offending_sequence,
 
167
                                      int           error_class,
 
168
                                      int           severity,
 
169
                                      SmPointer     values);
 
170
 
 
171
G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
 
172
 
 
173
static void
 
174
egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
 
175
{
 
176
  xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
 
177
  xsmp->connection = NULL;
 
178
  xsmp->restart_style = SmRestartIfRunning;
 
179
}
 
180
 
 
181
static void
 
182
egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
 
183
{
 
184
  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
 
185
 
 
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;
 
190
}
 
191
 
 
192
EggSMClient *
 
193
egg_sm_client_xsmp_new (void)
 
194
{
 
195
  if (!g_getenv ("SESSION_MANAGER"))
 
196
    return NULL;
 
197
 
 
198
  return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
 
199
}
 
200
 
 
201
static gboolean
 
202
sm_client_xsmp_set_initial_properties (gpointer user_data)
 
203
{
 
204
  EggSMClientXSMP *xsmp = user_data;
 
205
  EggDesktopFile *desktop_file;
 
206
  GPtrArray *clone, *restart;
 
207
  char pid_str[64];
 
208
 
 
209
  if (xsmp->idle)
 
210
    {
 
211
      g_source_remove (xsmp->idle);
 
212
      xsmp->idle = 0;
 
213
    }
 
214
  xsmp->waiting_to_set_initial_properties = FALSE;
 
215
 
 
216
  if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
 
217
    xsmp->restart_style = SmRestartNever;
 
218
 
 
219
  /* Parse info out of desktop file */
 
220
  desktop_file = egg_get_desktop_file ();
 
221
  if (desktop_file)
 
222
    {
 
223
      GError *err = NULL;
 
224
      char *cmdline, **argv;
 
225
      int argc;
 
226
 
 
227
      if (xsmp->restart_style == SmRestartIfRunning)
 
228
        {
 
229
          if (egg_desktop_file_get_boolean (desktop_file, 
 
230
                                            "X-GNOME-AutoRestart", NULL))
 
231
            xsmp->restart_style = SmRestartImmediately;
 
232
        }
 
233
 
 
234
      if (!xsmp->set_restart_command)
 
235
        {
 
236
          cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
 
237
          if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
 
238
            {
 
239
              egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
 
240
                                                 argc, (const char **)argv);
 
241
              g_strfreev (argv);
 
242
            }
 
243
          else
 
244
            {
 
245
              g_warning ("Could not parse Exec line in desktop file: %s",
 
246
                         err->message);
 
247
              g_error_free (err);
 
248
            }
 
249
          g_free (cmdline);
 
250
        }
 
251
    }
 
252
 
 
253
  if (!xsmp->set_restart_command)
 
254
    xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
 
255
 
 
256
  clone = generate_command (xsmp->restart_command, NULL, NULL);
 
257
  restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
 
258
 
 
259
  g_debug ("Setting initial properties");
 
260
 
 
261
  /* Program, CloneCommand, RestartCommand, and UserID are required.
 
262
   * ProcessID isn't required, but the SM may be able to do something
 
263
   * useful with it.
 
264
   */
 
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),
 
273
                  NULL);
 
274
  g_ptr_array_free (clone, TRUE);
 
275
  g_ptr_array_free (restart, TRUE);
 
276
 
 
277
  if (desktop_file)
 
278
    {
 
279
      set_properties (xsmp,
 
280
                      string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
 
281
                      NULL);
 
282
    }
 
283
 
 
284
  update_pending_events (xsmp);
 
285
  return FALSE;
 
286
}
 
287
 
 
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).
 
291
 */
 
292
static void
 
293
sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
 
294
{
 
295
  SmcConn connection;
 
296
 
 
297
  if (!xsmp->connection)
 
298
    return;
 
299
 
 
300
  g_debug ("Disconnecting");
 
301
 
 
302
  connection = xsmp->connection;
 
303
  xsmp->connection = NULL;
 
304
  SmcCloseConnection (connection, 0, NULL);
 
305
  xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
 
306
 
 
307
  xsmp->waiting_to_save_myself = FALSE;
 
308
  update_pending_events (xsmp);
 
309
}
 
310
 
 
311
static void
 
312
sm_client_xsmp_startup (EggSMClient *client,
 
313
                        const char  *client_id)
 
314
{
 
315
  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
 
316
  SmcCallbacks callbacks;
 
317
  char *ret_client_id;
 
318
  char error_string_ret[256];
 
319
 
 
320
  xsmp->client_id = g_strdup (client_id);
 
321
 
 
322
  ice_init ();
 
323
  SmcSetErrorHandler (smc_error_handler);
 
324
 
 
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;
 
329
 
 
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;
 
334
 
 
335
  client_id = NULL;
 
336
  error_string_ret[0] = '\0';
 
337
  xsmp->connection =
 
338
    SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
 
339
                       SmcSaveYourselfProcMask | SmcDieProcMask |
 
340
                       SmcSaveCompleteProcMask |
 
341
                       SmcShutdownCancelledProcMask,
 
342
                       &callbacks,
 
343
                       xsmp->client_id, &ret_client_id,
 
344
                       sizeof (error_string_ret), error_string_ret);
 
345
 
 
346
  if (!xsmp->connection)
 
347
    {
 
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;
 
352
      return;
 
353
    }
 
354
 
 
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.
 
358
   */
 
359
  if (!xsmp->client_id ||
 
360
      (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
 
361
    xsmp->expecting_initial_save_yourself = TRUE;
 
362
 
 
363
  if (ret_client_id)
 
364
    {
 
365
      g_free (xsmp->client_id);
 
366
      xsmp->client_id = g_strdup (ret_client_id);
 
367
      free (ret_client_id);
 
368
 
 
369
      gdk_threads_enter ();
 
370
      gdk_set_sm_client_id (xsmp->client_id);
 
371
      gdk_threads_leave ();
 
372
 
 
373
      g_debug ("Got client ID \"%s\"", xsmp->client_id);
 
374
    }
 
375
 
 
376
  xsmp->state = XSMP_STATE_IDLE;
 
377
 
 
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
 
382
   * running.)
 
383
   */
 
384
  xsmp->waiting_to_set_initial_properties = TRUE;
 
385
  xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
 
386
}
 
387
 
 
388
static void
 
389
sm_client_xsmp_set_restart_command (EggSMClient  *client,
 
390
                                    int           argc,
 
391
                                    const char  **argv)
 
392
{
 
393
  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
 
394
  int i;
 
395
 
 
396
  g_strfreev (xsmp->restart_command);
 
397
 
 
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;
 
402
 
 
403
  xsmp->set_restart_command = TRUE;
 
404
}
 
405
 
 
406
static void
 
407
sm_client_xsmp_will_quit (EggSMClient *client,
 
408
                          gboolean     will_quit)
 
409
{
 
410
  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
 
411
 
 
412
  if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
 
413
    {
 
414
      /* The session manager has already exited! Schedule a quit
 
415
       * signal.
 
416
       */
 
417
      xsmp->waiting_to_emit_quit = TRUE;
 
418
      update_pending_events (xsmp);
 
419
      return;
 
420
    }
 
421
  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
 
422
    {
 
423
      /* We received a ShutdownCancelled message while the application
 
424
       * was interacting; Schedule a quit_cancelled signal.
 
425
       */
 
426
      xsmp->waiting_to_emit_quit_cancelled = TRUE;
 
427
      update_pending_events (xsmp);
 
428
      return;
 
429
    }
 
430
 
 
431
  g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
 
432
 
 
433
  g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
 
434
  SmcInteractDone (xsmp->connection, !will_quit);
 
435
 
 
436
  if (will_quit && xsmp->need_save_state)
 
437
    save_state (xsmp);
 
438
 
 
439
  g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
 
440
  SmcSaveYourselfDone (xsmp->connection, will_quit);
 
441
  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 
442
}
 
443
 
 
444
static gboolean
 
445
sm_client_xsmp_end_session (EggSMClient         *client,
 
446
                            EggSMClientEndStyle  style,
 
447
                            gboolean             request_confirmation)
 
448
{
 
449
  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
 
450
  int save_type;
 
451
 
 
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...
 
456
   *
 
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
 
463
   * this happens.
 
464
   */
 
465
 
 
466
  while (xsmp->state != XSMP_STATE_IDLE ||
 
467
         xsmp->expecting_initial_save_yourself)
 
468
    {
 
469
      /* If we're already shutting down, we don't need to do anything. */
 
470
      if (xsmp->shutting_down)
 
471
        return TRUE;
 
472
 
 
473
      switch (xsmp->state)
 
474
        {
 
475
        case XSMP_STATE_CONNECTION_CLOSED:
 
476
          return FALSE;
 
477
 
 
478
        case XSMP_STATE_SAVE_YOURSELF:
 
479
          /* Trying to log out from the save_state callback? Whatever.
 
480
           * Abort the save_state.
 
481
           */
 
482
          SmcSaveYourselfDone (xsmp->connection, FALSE);
 
483
          xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 
484
          break;
 
485
 
 
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...
 
491
           */
 
492
          return TRUE;
 
493
 
 
494
        case XSMP_STATE_IDLE:
 
495
          if (xsmp->waiting_to_set_initial_properties)
 
496
            sm_client_xsmp_set_initial_properties (xsmp);
 
497
 
 
498
          if (!xsmp->expecting_initial_save_yourself)
 
499
            break;
 
500
          /* else fall through */
 
501
 
 
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));
 
505
          break;
 
506
 
 
507
        default:
 
508
          /* Hm... shouldn't happen */
 
509
          return FALSE;
 
510
        }
 
511
    }
 
512
 
 
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.
 
517
   */
 
518
  if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
 
519
    save_type = SmSaveBoth;
 
520
  else
 
521
    save_type = SmSaveGlobal;
 
522
 
 
523
  g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
 
524
  SmcRequestSaveYourself (xsmp->connection,
 
525
                          save_type,
 
526
                          True, /* shutdown */
 
527
                          SmInteractStyleAny,
 
528
                          !request_confirmation, /* fast */
 
529
                          True /* global */);
 
530
  return TRUE;
 
531
}
 
532
 
 
533
static gboolean
 
534
idle_do_pending_events (gpointer data)
 
535
{
 
536
  EggSMClientXSMP *xsmp = data;
 
537
  EggSMClient *client = data;
 
538
 
 
539
  gdk_threads_enter ();
 
540
 
 
541
  xsmp->idle = 0;
 
542
 
 
543
  if (xsmp->waiting_to_emit_quit)
 
544
    {
 
545
      xsmp->waiting_to_emit_quit = FALSE;
 
546
      egg_sm_client_quit (client);
 
547
      goto out;
 
548
    }
 
549
 
 
550
  if (xsmp->waiting_to_emit_quit_cancelled)
 
551
    {
 
552
      xsmp->waiting_to_emit_quit_cancelled = FALSE;
 
553
      egg_sm_client_quit_cancelled (client);
 
554
      xsmp->state = XSMP_STATE_IDLE;
 
555
    }
 
556
 
 
557
  if (xsmp->waiting_to_save_myself)
 
558
    {
 
559
      xsmp->waiting_to_save_myself = FALSE;
 
560
      do_save_yourself (xsmp);
 
561
    }
 
562
 
 
563
 out:
 
564
  gdk_threads_leave ();
 
565
  return FALSE;
 
566
}
 
567
 
 
568
static void
 
569
update_pending_events (EggSMClientXSMP *xsmp)
 
570
{
 
571
  gboolean want_idle =
 
572
    xsmp->waiting_to_emit_quit ||
 
573
    xsmp->waiting_to_emit_quit_cancelled ||
 
574
    xsmp->waiting_to_save_myself;
 
575
 
 
576
  if (want_idle)
 
577
    {
 
578
      if (xsmp->idle == 0)
 
579
        xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
 
580
    }
 
581
  else
 
582
    {
 
583
      if (xsmp->idle != 0)
 
584
        g_source_remove (xsmp->idle);
 
585
      xsmp->idle = 0;
 
586
    }
 
587
}
 
588
 
 
589
static void
 
590
fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
 
591
                  gboolean send_interact_done,
 
592
                  gboolean send_save_yourself_done)
 
593
{
 
594
  g_warning ("Received XSMP %s message in state %s: client or server error",
 
595
             message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
 
596
 
 
597
  /* Forget any pending SaveYourself plans we had */
 
598
  xsmp->waiting_to_save_myself = FALSE;
 
599
  update_pending_events (xsmp);
 
600
 
 
601
  if (send_interact_done)
 
602
    SmcInteractDone (xsmp->connection, False);
 
603
  if (send_save_yourself_done)
 
604
    SmcSaveYourselfDone (xsmp->connection, True);
 
605
 
 
606
  xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
 
607
}
 
608
 
 
609
/* SM callbacks */
 
610
 
 
611
static void
 
612
xsmp_save_yourself (SmcConn   smc_conn,
 
613
                    SmPointer client_data,
 
614
                    int       save_type,
 
615
                    Bool      shutdown,
 
616
                    int       interact_style,
 
617
                    Bool      fast)
 
618
{
 
619
  EggSMClientXSMP *xsmp = client_data;
 
620
  gboolean wants_quit_requested;
 
621
 
 
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));
 
630
 
 
631
  if (xsmp->state != XSMP_STATE_IDLE &&
 
632
      xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
 
633
    {
 
634
      fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
 
635
      return;
 
636
    }
 
637
 
 
638
  if (xsmp->waiting_to_set_initial_properties)
 
639
    sm_client_xsmp_set_initial_properties (xsmp);
 
640
 
 
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.
 
643
   */
 
644
  if (xsmp->expecting_initial_save_yourself)
 
645
    {
 
646
      xsmp->expecting_initial_save_yourself = FALSE;
 
647
 
 
648
      if (save_type == SmSaveLocal &&
 
649
          interact_style == SmInteractStyleNone &&
 
650
          !shutdown && !fast)
 
651
        {
 
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.
 
657
           */
 
658
          xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 
659
          return;
 
660
        }
 
661
      else
 
662
        g_warning ("First SaveYourself was not the expected one!");
 
663
    }
 
664
 
 
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:
 
668
   *
 
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)
 
676
   *
 
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
 
680
   *      for confirmation).
 
681
   *
 
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.
 
685
   *
 
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".
 
689
   *
 
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.
 
695
   *
 
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.)
 
701
   */
 
702
 
 
703
  /* As an optimization, we don't actually request interaction and
 
704
   * emit ::quit_requested if the application isn't listening to the
 
705
   * signal.
 
706
   */
 
707
  wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
 
708
 
 
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);
 
713
 
 
714
  xsmp->shutting_down       = shutdown;
 
715
 
 
716
  do_save_yourself (xsmp);
 
717
}
 
718
 
 
719
static void
 
720
do_save_yourself (EggSMClientXSMP *xsmp)
 
721
{
 
722
  if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
 
723
    {
 
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.
 
727
       */
 
728
      xsmp->waiting_to_save_myself = TRUE;
 
729
      update_pending_events (xsmp);
 
730
      return;
 
731
    }
 
732
 
 
733
  if (xsmp->need_quit_requested)
 
734
    {
 
735
      xsmp->state = XSMP_STATE_INTERACT_REQUEST;
 
736
 
 
737
      g_debug ("Sending InteractRequest(%s)",
 
738
               xsmp->interact_errors ? "Error" : "Normal");
 
739
      SmcInteractRequest (xsmp->connection,
 
740
                          xsmp->interact_errors ? SmDialogError : SmDialogNormal,
 
741
                          xsmp_interact,
 
742
                          xsmp);
 
743
      return;
 
744
    }
 
745
 
 
746
  if (xsmp->need_save_state)
 
747
    {
 
748
      save_state (xsmp);
 
749
 
 
750
      /* Though unlikely, the client could have been disconnected
 
751
       * while the application was saving its state.
 
752
       */
 
753
      if (!xsmp->connection)
 
754
         return;
 
755
    }
 
756
 
 
757
  g_debug ("Sending SaveYourselfDone(True)");
 
758
  SmcSaveYourselfDone (xsmp->connection, True);
 
759
 
 
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
 
763
   * disagrees.
 
764
   */
 
765
  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 
766
}
 
767
 
 
768
static void
 
769
save_state (EggSMClientXSMP *xsmp)
 
770
{
 
771
  GKeyFile *state_file;
 
772
  char *state_file_path, *data;
 
773
  EggDesktopFile *desktop_file;
 
774
  GPtrArray *restart;
 
775
  int offset, fd;
 
776
 
 
777
  /* We set xsmp->state before emitting save_state, but our caller is
 
778
   * responsible for setting it back afterward.
 
779
   */
 
780
  xsmp->state = XSMP_STATE_SAVE_YOURSELF;
 
781
 
 
782
  state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
 
783
  if (!state_file)
 
784
    {
 
785
      restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
 
786
      set_properties (xsmp,
 
787
                      ptrarray_prop (SmRestartCommand, restart),
 
788
                      NULL);
 
789
      g_ptr_array_free (restart, TRUE);
 
790
      delete_properties (xsmp, SmDiscardCommand, NULL);
 
791
      return;
 
792
    }
 
793
 
 
794
  desktop_file = egg_get_desktop_file ();
 
795
  if (desktop_file)
 
796
    {
 
797
      GKeyFile *merged_file;
 
798
 
 
799
      merged_file = g_key_file_new ();
 
800
      if (g_key_file_load_from_file (merged_file,
 
801
                                     egg_desktop_file_get_source (desktop_file),
 
802
                                     G_KEY_FILE_KEEP_COMMENTS |
 
803
                                     G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
 
804
        {
 
805
          int g, k, i;
 
806
          char **groups, **keys, *value, *exec;
 
807
 
 
808
          groups = g_key_file_get_groups (state_file, NULL);
 
809
          for (g = 0; groups[g]; g++)
 
810
            {
 
811
              keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
 
812
              for (k = 0; keys[k]; k++)
 
813
                {
 
814
                  value = g_key_file_get_value (state_file, groups[g],
 
815
                                                keys[k], NULL);
 
816
                  if (value)
 
817
                    {
 
818
                      g_key_file_set_value (merged_file, groups[g],
 
819
                                            keys[k], value);
 
820
                      g_free (value);
 
821
                    }
 
822
                }
 
823
              g_strfreev (keys);
 
824
            }
 
825
          g_strfreev (groups);
 
826
 
 
827
          g_key_file_free (state_file);
 
828
          state_file = merged_file;
 
829
 
 
830
          /* Update Exec key using "--sm-client-state-file %k" */
 
831
          restart = generate_command (xsmp->restart_command,
 
832
                                      NULL, "%k");
 
833
          for (i = 0; i < restart->len; i++)
 
834
            restart->pdata[i] = g_shell_quote (restart->pdata[i]);
 
835
          g_ptr_array_add (restart, NULL);
 
836
          exec = g_strjoinv (" ", (char **)restart->pdata);
 
837
          g_strfreev ((char **)restart->pdata);
 
838
          g_ptr_array_free (restart, FALSE);
 
839
 
 
840
          g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
 
841
                                 EGG_DESKTOP_FILE_KEY_EXEC,
 
842
                                 exec);
 
843
          g_free (exec);
 
844
 
 
845
        }
 
846
    }
 
847
 
 
848
  /* Now write state_file to disk. (We can't use mktemp(), because
 
849
   * that requires the filename to end with "XXXXXX", and we want
 
850
   * it to end with ".desktop".)
 
851
   */
 
852
 
 
853
  data = g_key_file_to_data (state_file, NULL, NULL);
 
854
  g_key_file_free (state_file);
 
855
 
 
856
  offset = 0;
 
857
  while (1)
 
858
    {
 
859
      state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
 
860
                                         g_get_user_config_dir (),
 
861
                                         G_DIR_SEPARATOR, G_DIR_SEPARATOR,
 
862
                                         g_get_prgname (),
 
863
                                         (long)time (NULL) + offset,
 
864
                                         desktop_file ? "desktop" : "state");
 
865
 
 
866
      fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
 
867
      if (fd == -1)
 
868
        {
 
869
          if (errno == EEXIST)
 
870
            {
 
871
              offset++;
 
872
              g_free (state_file_path);
 
873
              continue;
 
874
            }
 
875
          else if (errno == ENOTDIR || errno == ENOENT)
 
876
            {
 
877
              char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
 
878
 
 
879
              *sep = '\0';
 
880
              if (g_mkdir_with_parents (state_file_path, 0755) != 0)
 
881
                {
 
882
                  g_warning ("Could not create directory '%s'",
 
883
                             state_file_path);
 
884
                  g_free (state_file_path);
 
885
                  state_file_path = NULL;
 
886
                  break;
 
887
                }
 
888
 
 
889
              continue;
 
890
            }
 
891
 
 
892
          g_warning ("Could not create file '%s': %s",
 
893
                     state_file_path, g_strerror (errno));
 
894
          g_free (state_file_path);
 
895
          state_file_path = NULL;
 
896
          break;
 
897
        }
 
898
 
 
899
      close (fd);
 
900
      g_file_set_contents (state_file_path, data, -1, NULL);
 
901
      break;
 
902
    }
 
903
  g_free (data);
 
904
 
 
905
  restart = generate_command (xsmp->restart_command, xsmp->client_id,
 
906
                              state_file_path);
 
907
  set_properties (xsmp,
 
908
                  ptrarray_prop (SmRestartCommand, restart),
 
909
                  NULL);
 
910
  g_ptr_array_free (restart, TRUE);
 
911
 
 
912
  if (state_file_path)
 
913
    {
 
914
      set_properties (xsmp,
 
915
                      array_prop (SmDiscardCommand,
 
916
                                  "/bin/rm", "-rf", state_file_path,
 
917
                                  NULL),
 
918
                      NULL);
 
919
      g_free (state_file_path);
 
920
    }
 
921
}
 
922
 
 
923
static void
 
924
xsmp_interact (SmcConn   smc_conn,
 
925
               SmPointer client_data)
 
926
{
 
927
  EggSMClientXSMP *xsmp = client_data;
 
928
  EggSMClient *client = client_data;
 
929
 
 
930
  g_debug ("Received Interact message in state %s",
 
931
           EGG_SM_CLIENT_XSMP_STATE (xsmp));
 
932
 
 
933
  if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
 
934
    {
 
935
      fix_broken_state (xsmp, "Interact", TRUE, TRUE);
 
936
      return;
 
937
    }
 
938
 
 
939
  xsmp->state = XSMP_STATE_INTERACT;
 
940
  egg_sm_client_quit_requested (client);
 
941
}
 
942
 
 
943
static void
 
944
xsmp_die (SmcConn   smc_conn,
 
945
          SmPointer client_data)
 
946
{
 
947
  EggSMClientXSMP *xsmp = client_data;
 
948
  EggSMClient *client = client_data;
 
949
 
 
950
  g_debug ("Received Die message in state %s",
 
951
           EGG_SM_CLIENT_XSMP_STATE (xsmp));
 
952
 
 
953
  sm_client_xsmp_disconnect (xsmp);
 
954
  egg_sm_client_quit (client);
 
955
}
 
956
 
 
957
static void
 
958
xsmp_save_complete (SmcConn   smc_conn,
 
959
                    SmPointer client_data)
 
960
{
 
961
  EggSMClientXSMP *xsmp = client_data;
 
962
 
 
963
  g_debug ("Received SaveComplete message in state %s",
 
964
           EGG_SM_CLIENT_XSMP_STATE (xsmp));
 
965
 
 
966
  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
 
967
    xsmp->state = XSMP_STATE_IDLE;
 
968
  else
 
969
    fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
 
970
}
 
971
 
 
972
static void
 
973
xsmp_shutdown_cancelled (SmcConn   smc_conn,
 
974
                         SmPointer client_data)
 
975
{
 
976
  EggSMClientXSMP *xsmp = client_data;
 
977
  EggSMClient *client = client_data;
 
978
 
 
979
  g_debug ("Received ShutdownCancelled message in state %s",
 
980
           EGG_SM_CLIENT_XSMP_STATE (xsmp));
 
981
 
 
982
  xsmp->shutting_down = FALSE;
 
983
 
 
984
  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
 
985
    {
 
986
      /* We've finished interacting and now the SM has agreed to
 
987
       * cancel the shutdown.
 
988
       */
 
989
      xsmp->state = XSMP_STATE_IDLE;
 
990
      egg_sm_client_quit_cancelled (client);
 
991
    }
 
992
  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
 
993
    {
 
994
      /* Hm... ok, so we got a shutdown SaveYourself, which got
 
995
       * cancelled, but the application was still interacting, so we
 
996
       * didn't tell it yet, and then *another* SaveYourself arrived,
 
997
       * which we must still be waiting to tell the app about, except
 
998
       * that now that SaveYourself has been cancelled too! Dizzy yet?
 
999
       */
 
1000
      xsmp->waiting_to_save_myself = FALSE;
 
1001
      update_pending_events (xsmp);
 
1002
    }
 
1003
  else
 
1004
    {
 
1005
      g_debug ("Sending SaveYourselfDone(False)");
 
1006
      SmcSaveYourselfDone (xsmp->connection, False);
 
1007
 
 
1008
      if (xsmp->state == XSMP_STATE_INTERACT)
 
1009
        {
 
1010
          /* The application is currently interacting, so we can't
 
1011
           * tell it about the cancellation yet; we will wait until
 
1012
           * after it calls egg_sm_client_will_quit().
 
1013
           */
 
1014
          xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
 
1015
        }
 
1016
      else
 
1017
        {
 
1018
          /* The shutdown was cancelled before the application got a
 
1019
           * chance to interact.
 
1020
           */
 
1021
          xsmp->state = XSMP_STATE_IDLE;
 
1022
        }
 
1023
    }
 
1024
}
 
1025
 
 
1026
/* Utilities */
 
1027
 
 
1028
/* Create a restart/clone/Exec command based on @restart_command.
 
1029
 * If @client_id is non-%NULL, add "--sm-client-id @client_id".
 
1030
 * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
 
1031
 *
 
1032
 * None of the input strings are g_strdup()ed; the caller must keep
 
1033
 * them around until it is done with the returned GPtrArray, and must
 
1034
 * then free the array, but not its contents.
 
1035
 */
 
1036
static GPtrArray *
 
1037
generate_command (char **restart_command, const char *client_id,
 
1038
                  const char *state_file)
 
1039
{
 
1040
  GPtrArray *cmd;
 
1041
  int i;
 
1042
 
 
1043
  cmd = g_ptr_array_new ();
 
1044
  g_ptr_array_add (cmd, restart_command[0]);
 
1045
 
 
1046
  if (client_id)
 
1047
    {
 
1048
      g_ptr_array_add (cmd, "--sm-client-id");
 
1049
      g_ptr_array_add (cmd, (char *)client_id);
 
1050
    }
 
1051
 
 
1052
  if (state_file)
 
1053
    {
 
1054
      g_ptr_array_add (cmd, "--sm-client-state-file");
 
1055
      g_ptr_array_add (cmd, (char *)state_file);
 
1056
    }
 
1057
 
 
1058
  for (i = 1; restart_command[i]; i++)
 
1059
    g_ptr_array_add (cmd, restart_command[i]);
 
1060
 
 
1061
  return cmd;
 
1062
}
 
1063
 
 
1064
/* Takes a NULL-terminated list of SmProp * values, created by
 
1065
 * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
 
1066
 * frees them.
 
1067
 */
 
1068
static void
 
1069
set_properties (EggSMClientXSMP *xsmp, ...)
 
1070
{
 
1071
  GPtrArray *props;
 
1072
  SmProp *prop;
 
1073
  va_list ap;
 
1074
  int i;
 
1075
 
 
1076
  props = g_ptr_array_new ();
 
1077
 
 
1078
  va_start (ap, xsmp);
 
1079
  while ((prop = va_arg (ap, SmProp *)))
 
1080
    g_ptr_array_add (props, prop);
 
1081
  va_end (ap);
 
1082
 
 
1083
  if (xsmp->connection)
 
1084
    {
 
1085
      SmcSetProperties (xsmp->connection, props->len,
 
1086
                        (SmProp **)props->pdata);
 
1087
    }
 
1088
 
 
1089
  for (i = 0; i < props->len; i++)
 
1090
    {
 
1091
      prop = props->pdata[i];
 
1092
      g_free (prop->vals);
 
1093
      g_free (prop);
 
1094
    }
 
1095
  g_ptr_array_free (props, TRUE);
 
1096
}
 
1097
 
 
1098
/* Takes a NULL-terminated list of property names and deletes them. */
 
1099
static void
 
1100
delete_properties (EggSMClientXSMP *xsmp, ...)
 
1101
{
 
1102
  GPtrArray *props;
 
1103
  char *prop;
 
1104
  va_list ap;
 
1105
 
 
1106
  if (!xsmp->connection)
 
1107
    return;
 
1108
 
 
1109
  props = g_ptr_array_new ();
 
1110
 
 
1111
  va_start (ap, xsmp);
 
1112
  while ((prop = va_arg (ap, char *)))
 
1113
    g_ptr_array_add (props, prop);
 
1114
  va_end (ap);
 
1115
 
 
1116
  SmcDeleteProperties (xsmp->connection, props->len,
 
1117
                       (char **)props->pdata);
 
1118
 
 
1119
  g_ptr_array_free (props, TRUE);
 
1120
}
 
1121
 
 
1122
/* Takes an array of strings and creates a LISTofARRAY8 property. The
 
1123
 * strings are neither dupped nor freed; they need to remain valid
 
1124
 * until you're done with the SmProp.
 
1125
 */
 
1126
static SmProp *
 
1127
array_prop (const char *name, ...) 
 
1128
{
 
1129
  SmProp *prop;
 
1130
  SmPropValue pv;
 
1131
  GArray *vals;
 
1132
  char *value;
 
1133
  va_list ap;
 
1134
 
 
1135
  prop = g_new (SmProp, 1);
 
1136
  prop->name = (char *)name;
 
1137
  prop->type = SmLISTofARRAY8;
 
1138
 
 
1139
  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
 
1140
 
 
1141
  va_start (ap, name);
 
1142
  while ((value = va_arg (ap, char *)))
 
1143
    {
 
1144
      pv.length = strlen (value);
 
1145
      pv.value = value;
 
1146
      g_array_append_val (vals, pv);
 
1147
    }
 
1148
 
 
1149
  prop->num_vals = vals->len;
 
1150
  prop->vals = (SmPropValue *)vals->data;
 
1151
 
 
1152
  g_array_free (vals, FALSE);
 
1153
 
 
1154
  return prop;
 
1155
}
 
1156
 
 
1157
/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
 
1158
 * The array contents are neither dupped nor freed; they need to
 
1159
 * remain valid until you're done with the SmProp.
 
1160
 */
 
1161
static SmProp *
 
1162
ptrarray_prop (const char *name, GPtrArray *values)
 
1163
{
 
1164
  SmProp *prop;
 
1165
  SmPropValue pv;
 
1166
  GArray *vals;
 
1167
  int i;
 
1168
 
 
1169
  prop = g_new (SmProp, 1);
 
1170
  prop->name = (char *)name;
 
1171
  prop->type = SmLISTofARRAY8;
 
1172
 
 
1173
  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
 
1174
 
 
1175
  for (i = 0; i < values->len; i++)
 
1176
    {
 
1177
      pv.length = strlen (values->pdata[i]);
 
1178
      pv.value = values->pdata[i];
 
1179
      g_array_append_val (vals, pv);
 
1180
    }
 
1181
 
 
1182
  prop->num_vals = vals->len;
 
1183
  prop->vals = (SmPropValue *)vals->data;
 
1184
 
 
1185
  g_array_free (vals, FALSE);
 
1186
 
 
1187
  return prop;
 
1188
}
 
1189
 
 
1190
/* Takes a string and creates an ARRAY8 property. The string is
 
1191
 * neither dupped nor freed; it needs to remain valid until you're
 
1192
 * done with the SmProp.
 
1193
 */
 
1194
static SmProp *
 
1195
string_prop (const char *name, const char *value)
 
1196
{
 
1197
  SmProp *prop;
 
1198
 
 
1199
  prop = g_new (SmProp, 1);
 
1200
  prop->name = (char *)name;
 
1201
  prop->type = SmARRAY8;
 
1202
 
 
1203
  prop->num_vals = 1;
 
1204
  prop->vals = g_new (SmPropValue, 1);
 
1205
 
 
1206
  prop->vals[0].length = strlen (value);
 
1207
  prop->vals[0].value = (char *)value;
 
1208
 
 
1209
  return prop;
 
1210
}
 
1211
 
 
1212
/* Takes a char and creates a CARD8 property. */
 
1213
static SmProp *
 
1214
card8_prop (const char *name, unsigned char value)
 
1215
{
 
1216
  SmProp *prop;
 
1217
  char *card8val;
 
1218
 
 
1219
  /* To avoid having to allocate and free prop->vals[0], we cheat and
 
1220
   * make vals a 2-element-long array and then use the second element
 
1221
   * to store value.
 
1222
   */
 
1223
 
 
1224
  prop = g_new (SmProp, 1);
 
1225
  prop->name = (char *)name;
 
1226
  prop->type = SmCARD8;
 
1227
 
 
1228
  prop->num_vals = 1;
 
1229
  prop->vals = g_new (SmPropValue, 2);
 
1230
  card8val = (char *)(&prop->vals[1]);
 
1231
  card8val[0] = value;
 
1232
 
 
1233
  prop->vals[0].length = 1;
 
1234
  prop->vals[0].value = card8val;
 
1235
 
 
1236
  return prop;
 
1237
}
 
1238
 
 
1239
/* ICE code. This makes no effort to play nice with anyone else trying
 
1240
 * to use libICE. Fortunately, no one uses libICE for anything other
 
1241
 * than SM. (DCOP uses ICE, but it has its own private copy of
 
1242
 * libICE.)
 
1243
 *
 
1244
 * When this moves to gtk, it will need to be cleverer, to avoid
 
1245
 * tripping over old apps that use GnomeClient or that use libSM
 
1246
 * directly.
 
1247
 */
 
1248
 
 
1249
#include <X11/ICE/ICElib.h>
 
1250
#include <fcntl.h>
 
1251
 
 
1252
static void        ice_error_handler    (IceConn        ice_conn,
 
1253
                                         Bool           swap,
 
1254
                                         int            offending_minor_opcode,
 
1255
                                         unsigned long  offending_sequence,
 
1256
                                         int            error_class,
 
1257
                                         int            severity,
 
1258
                                         IcePointer     values);
 
1259
static void        ice_io_error_handler (IceConn        ice_conn);
 
1260
static void        ice_connection_watch (IceConn        ice_conn,
 
1261
                                         IcePointer     client_data,
 
1262
                                         Bool           opening,
 
1263
                                         IcePointer    *watch_data);
 
1264
 
 
1265
static void
 
1266
ice_init (void)
 
1267
{
 
1268
  IceSetIOErrorHandler (ice_io_error_handler);
 
1269
  IceSetErrorHandler (ice_error_handler);
 
1270
  IceAddConnectionWatch (ice_connection_watch, NULL);
 
1271
}
 
1272
 
 
1273
static gboolean
 
1274
process_ice_messages (IceConn ice_conn)
 
1275
{
 
1276
  IceProcessMessagesStatus status;
 
1277
 
 
1278
  gdk_threads_enter ();
 
1279
  status = IceProcessMessages (ice_conn, NULL, NULL);
 
1280
  gdk_threads_leave ();
 
1281
 
 
1282
  switch (status)
 
1283
    {
 
1284
    case IceProcessMessagesSuccess:
 
1285
      return TRUE;
 
1286
 
 
1287
    case IceProcessMessagesIOError:
 
1288
      sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
 
1289
      return FALSE;
 
1290
 
 
1291
    case IceProcessMessagesConnectionClosed:
 
1292
      return FALSE;
 
1293
 
 
1294
    default:
 
1295
      g_assert_not_reached ();
 
1296
    }
 
1297
}
 
1298
 
 
1299
static gboolean
 
1300
ice_iochannel_watch (GIOChannel   *channel,
 
1301
                     GIOCondition  condition,
 
1302
                     gpointer      client_data)
 
1303
{
 
1304
  return process_ice_messages (client_data);
 
1305
}
 
1306
 
 
1307
static void
 
1308
ice_connection_watch (IceConn     ice_conn,
 
1309
                      IcePointer  client_data,
 
1310
                      Bool        opening,
 
1311
                      IcePointer *watch_data)
 
1312
{
 
1313
  guint watch_id;
 
1314
 
 
1315
  if (opening)
 
1316
    {
 
1317
      GIOChannel *channel;
 
1318
      int fd = IceConnectionNumber (ice_conn);
 
1319
 
 
1320
      fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
 
1321
      channel = g_io_channel_unix_new (fd);
 
1322
      watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
 
1323
                                 ice_iochannel_watch, ice_conn);
 
1324
      g_io_channel_unref (channel);
 
1325
 
 
1326
      *watch_data = GUINT_TO_POINTER (watch_id);
 
1327
    }
 
1328
  else
 
1329
    {
 
1330
      watch_id = GPOINTER_TO_UINT (*watch_data);
 
1331
      g_source_remove (watch_id);
 
1332
    }
 
1333
}
 
1334
 
 
1335
static void
 
1336
ice_error_handler (IceConn       ice_conn,
 
1337
                   Bool          swap,
 
1338
                   int           offending_minor_opcode,
 
1339
                   unsigned long offending_sequence,
 
1340
                   int           error_class,
 
1341
                   int           severity,
 
1342
                   IcePointer    values)
 
1343
{
 
1344
  /* Do nothing */
 
1345
 
1346
 
 
1347
static void
 
1348
ice_io_error_handler (IceConn ice_conn)
 
1349
{
 
1350
  /* Do nothing */
 
1351
 
1352
 
 
1353
static void
 
1354
smc_error_handler (SmcConn       smc_conn,
 
1355
                   Bool          swap,
 
1356
                   int           offending_minor_opcode,
 
1357
                   unsigned long offending_sequence,
 
1358
                   int           error_class,
 
1359
                   int           severity,
 
1360
                   SmPointer     values)
 
1361
{
 
1362
  /* Do nothing */
 
1363
}