hythmbox-2.98/lib/eggsmclient-xsmp.c

No issues found

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