Subject: bug#18375: 24.4.50; Emacs hangs X session manager logout in certain cases

From: Christoph Ruegge <[email-masked]>
Date: Mon, 1 Sep 2014 18:54:39 +0200

Ok, I wrote a patch that fixes the problem for me and implements the
behaviour I described earlier. Like I said, I don't really know what
I'm doing here, but maybe it can be helpful somehow ;-)

Best regards
diff --git a/lisp/startup.el b/lisp/startup.el
index bb55080..19bdb84 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -99,6 +99,11 @@ startup message unless he personally acts to inhibit it."
   :type 'boolean
   :group 'initialization)
 
+(defcustom inhibit-x-session-manager nil
+  "Non-nil inhibits connecting to the X session manager on startup."
+  :type 'boolean
+  :group 'initialization)
+
 (defvar command-switch-alist nil
   "Alist of command-line switches.
 Elements look like (SWITCH-STRING . HANDLER-FUNCTION).
@@ -1318,6 +1323,20 @@ Consider using a subdirectory instead, e.g.: %s" dir
   ;; If -batch, terminate after processing the command options.
   (if noninteractive (kill-emacs t))
 
+  (when (and (not inhibit-x-session-manager)
+             (or (get 'x 'window-system-initialized)
+                 (getenv "DISPLAY")))
+    (condition-case err
+        (progn
+          (unless (get 'x 'window-system-initialized)
+            (x-initialize-window-system)
+            (put 'x 'window-system-initialized t))
+          (x-session-initialize x-display-name))
+      (error (display-warning
+	    'initialization
+	    (format "Could not connect to X session manager: %s" err)
+	    :warning))))
+
   ;; In daemon mode, start the server to allow clients to connect.
   ;; This is done after loading the user's init file and after
   ;; processing all command line arguments to allow e.g. `server-name'
diff --git a/src/xsmfns.c b/src/xsmfns.c
index 81b0126..6670604 100644
--- a/src/xsmfns.c
+++ b/src/xsmfns.c
@@ -59,6 +59,10 @@ static int doing_interact;
 
 static SmcConn smc_conn;
 
+/* The X terminal on which the session manager connection is opened. */
+
+static struct terminal *smc_terminal;
+
 /* The client session id for this session.  */
 
 static char *client_id;
@@ -390,8 +394,14 @@ create_client_leader_window (struct x_display_info *dpyinfo, char *client_ID)
 
 /* Try to open a connection to the session manager.  */
 
-void
-x_session_initialize (struct x_display_info *dpyinfo)
+DEFUN ("x-session-initialize", Fx_session_initialize,
+       Sx_session_initialize, 0, 1, 0,
+       doc: /* Initialize the connection to the X session manager.
+This is done automatically on startup if `inhibit-x-session-manager'
+is non-nil.  The optional parameter TERMINAL should be the X terminal
+on which to open the connection.  X should be initialized using
+`x-initialize-window-system' before calling this. */)
+  (Lisp_Object terminal)
 {
 #define SM_ERRORSTRING_LEN 512
   char errorstring[SM_ERRORSTRING_LEN];
@@ -399,6 +409,8 @@ x_session_initialize (struct x_display_info *dpyinfo)
   SmcCallbacks callbacks;
   ptrdiff_t name_len = 0;
 
+  struct x_display_info *dpyinfo = check_x_display_info (terminal);
+
   ice_fd = -1;
   doing_interact = False;
 
@@ -465,15 +477,33 @@ x_session_initialize (struct x_display_info *dpyinfo)
 #else
       create_client_leader_window (dpyinfo, client_id);
 #endif
+
+      smc_terminal = dpyinfo->terminal;
+      smc_terminal->reference_count++;
     }
+
+  return Qnil;
 }
 
 /* Ensure that the session manager is not contacted again. */
 
-void
-x_session_close (void)
+DEFUN ("x-session-close", Fx_session_close,
+       Sx_session_close, 0, 0, 0,
+       doc: /* Close the connection to the X session manager.  */)
+  ()
 {
+  SmcCloseConnection (smc_conn, 0, 0);
   ice_connection_closed ();
+
+  smc_terminal->reference_count--;
+  if (smc_terminal->reference_count == 0)
+    {
+	Lisp_Object tmp;
+	XSETTERMINAL (tmp, smc_terminal);
+        Fdelete_terminal (tmp, Qnil);
+    }
+
+  return Qnil;
 }

@@ -563,6 +593,8 @@ See also `emacs-save-session-functions', `emacs-session-save' and
   Vx_session_previous_id = Qnil;
 
   defsubr (&Shandle_save_session);
+  defsubr (&Sx_session_initialize);
+  defsubr (&Sx_session_close);
 }
 
 #endif /* HAVE_X_SM */
diff --git a/src/xterm.c b/src/xterm.c
index beb7d78..42ff947 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -11180,14 +11180,6 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
 #endif
   }
 
-#ifdef HAVE_X_SM
-  /* Only do this for the very first display in the Emacs session.
-     Ignore X session management when Emacs was first started on a
-     tty.  */
-  if (terminal->id == 1)
-    x_session_initialize (dpyinfo);
-#endif
-
   unblock_input ();
 
   return dpyinfo;
@@ -11206,11 +11198,6 @@ x_delete_display (struct x_display_info *dpyinfo)
   for (t = terminal_list; t; t = t->next_terminal)
     if (t->type == output_x_window && t->display_info.x == dpyinfo)
       {
-#ifdef HAVE_X_SM
-        /* Close X session management when we close its display.  */
-        if (t->id == 1 && x_session_have_connection ())
-          x_session_close ();
-#endif
         delete_terminal (t);
         break;
       }
diff --git a/src/xterm.h b/src/xterm.h
index c867312..7f5e087 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1097,9 +1097,7 @@ extern void initialize_frame_menubar (struct frame *);
 
 /* Defined in xsmfns.c */
 #ifdef HAVE_X_SM
-extern void x_session_initialize (struct x_display_info *dpyinfo);
 extern int x_session_have_connection (void);
-extern void x_session_close (void);
 #endif
 
 /* Defined in xterm.c */