~ubuntu-branches/ubuntu/saucy/totem/saucy-proposed

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