/* Session management module for systems which understand the X Session
management protocol.
- Copyright (C) 2002 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of GNU Emacs.
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include <config.h>
#ifdef HAVE_X_SM
#include <X11/SM/SMlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <sys/param.h>
#include <stdio.h>
+#include "lisp.h"
#include "systime.h"
#include "sysselect.h"
-#include "lisp.h"
#include "termhooks.h"
#include "termopts.h"
-
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 1024
-#endif /* not MAXPATHLEN */
-
+#include "xterm.h"
/* The user login name. */
static int doing_interact = False;
-/* The session manager object for the session manager connection */
+/* The session manager object for the session manager connection. */
static SmcConn smc_conn;
-/* The client session id for this session */
+/* The client session id for this session. */
+
static char *client_id;
-/* The full path name to the Emacs program */
+/* The full path name to the Emacs program. */
+
static char *emacs_program;
-/* The client session id for this session as a lisp object. */
+/* The client session id for this session as a lisp object. */
Lisp_Object Vx_session_id;
/* The id we had the previous session. This is only available if we
- have been started by the session manager with SMID_OPT. */
+ have been started by the session manager with SMID_OPT. */
Lisp_Object Vx_session_previous_id;
/* The option we tell the session manager to start Emacs with when
- restarting Emacs. The client_id is appended. */
+ restarting Emacs. The client_id is appended. */
#define SMID_OPT "--smid="
/* The option to start Emacs without the splash screen when
- restarting Emacs. */
+ restarting Emacs. */
#define NOSPLASH_OPT "--no-splash"
/* Handle any messages from the session manager. If no connection is
open to a session manager, just return 0.
- Otherwise returns the number of events stored in buffer BUFP,
- which can hold up to *NUMCHARS characters. At most one event is
- stored, a SAVE_SESSION_EVENT. */
+ Otherwise returns 1 if SAVE_SESSION_EVENT is stored in buffer BUFP. */
+
int
-x_session_check_input (bufp, numchars)
+x_session_check_input (bufp)
struct input_event *bufp;
- int *numchars;
{
SELECT_TYPE read_fds;
EMACS_TIME tmout;
/* Reset this so wo can check kind after callbacks have been called by
IceProcessMessages. The smc_interact_CB sets the kind to
SAVE_SESSION_EVENT, but we don't know beforehand if that callback
- will be called. */
+ will be called. */
emacs_event.kind = NO_EVENT;
if (select (ice_fd+1, &read_fds,
/* Check if smc_interact_CB was called and we shall generate a
- SAVE_SESSION_EVENT. */
- if (*numchars > 0 && emacs_event.kind != NO_EVENT)
- {
- bcopy (&emacs_event, bufp, sizeof (struct input_event));
- bufp++;
- (*numchars)--;
-
- return 1;
- }
+ SAVE_SESSION_EVENT. */
+ if (emacs_event.kind == NO_EVENT)
+ return 0;
- return 0;
+ bcopy (&emacs_event, bufp, sizeof (struct input_event));
+ return 1;
}
-/* Return non-zero if we have a connection to a session manager.*/
+/* Return non-zero if we have a connection to a session manager. */
+
int
x_session_have_connection ()
{
/* This is called when the session manager says it is OK to interact with the
user. Here we set the kind to SAVE_SESSION_EVENT so an event is generated.
- Then lisp code can interact with the user. */
+ Then lisp code can interact with the user. */
+
static void
smc_interact_CB (smcConn, clientData)
SmcConn smcConn;
are started in the correct directory.
If this is a shutdown and we can request to interact with the user,
- we do so, because we don't know what the lisp code might do. */
+ we do so, because we don't know what the lisp code might do. */
+
static void
smc_save_yourself_CB (smcConn,
clientData,
int val_idx = 0;
int props_idx = 0;
- char cwd[MAXPATHLEN+1];
+ char *cwd = NULL;
char *smid_opt;
- /* How to start a new instance of Emacs */
+ /* How to start a new instance of Emacs. */
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmCloneCommand;
props[props_idx]->type = SmLISTofARRAY8;
props[props_idx]->vals[0].value = emacs_program;
++props_idx;
- /* The name of the program */
+ /* The name of the program. */
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmProgram;
props[props_idx]->type = SmARRAY8;
props[props_idx]->vals[0].value = SDATA (Vinvocation_name);
++props_idx;
- /* How to restart Emacs (i.e.: /path/to/emacs --smid=xxxx --no-splash). */
+ /* How to restart Emacs (i.e.: /path/to/emacs --smid=xxxx --no-splash). */
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmRestartCommand;
props[props_idx]->type = SmLISTofARRAY8;
- props[props_idx]->num_vals = 3; /* /path/to/emacs, --smid=xxx --no-splash */
+ props[props_idx]->num_vals = 3; /* /path/to/emacs, --smid=xxx --no-splash */
props[props_idx]->vals = &values[val_idx];
props[props_idx]->vals[0].length = strlen (emacs_program);
props[props_idx]->vals[0].value = emacs_program;
val_idx += 3;
++props_idx;
- /* User id */
+ /* User id. */
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmUserID;
props[props_idx]->type = SmARRAY8;
props[props_idx]->vals[0].value = SDATA (Vuser_login_name);
++props_idx;
- /* The current directory property, not mandatory */
-#ifdef HAVE_GETCWD
- if (getcwd (cwd, MAXPATHLEN+1) != 0)
-#else
- if (getwd (cwd) != 0)
-#endif
+ cwd = get_current_dir_name ();
+
+ if (cwd)
{
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmCurrentDirectory;
xfree (smid_opt);
- /* See if we maybe shall interact with the user. */
+ if (cwd)
+ free (cwd);
+
+ /* See if we maybe shall interact with the user. */
if (interactStyle != SmInteractStyleAny
|| ! shutdown
|| saveType == SmSaveLocal
|| ! SmcInteractRequest (smcConn, SmDialogNormal, smc_interact_CB, 0))
{
- /* No interaction, we are done saving ourself. */
+ /* No interaction, we are done saving ourself. */
SmcSaveYourselfDone (smcConn, True);
}
}
-/* According to the SM specification, this shall close the connection */
+/* According to the SM specification, this shall close the connection. */
+
static void
smc_die_CB (smcConn, clientData)
SmcConn smcConn;
According to the SM specification, we should not interact with the
user between smc_save_yourself_CB is called and until smc_save_complete_CB
is called. It seems like a lot of job to implement this and it doesn't
- even seem necessary. */
+ even seem necessary. */
+
static void
smc_save_complete_CB (smcConn, clientData)
SmcConn smcConn;
}
/* Error handlers for SM and ICE. We don't want to exit Emacs just
- because there is some error in the session management. */
+ because there is some error in the session management. */
+
static void
smc_error_handler (smcConn,
swap,
int severity;
SmPointer values;
{
- /* Empty */
+ /* Empty */
}
static void
int severity;
IcePointer values;
{
- /* Empty */
+ /* Empty */
}
ice_io_error_handler (iceConn)
IceConn iceConn;
{
- /* Connection probably gone. */
+ /* Connection probably gone. */
ice_fd = -1;
}
/* This is called when the ICE connection is created or closed. The SM library
- uses ICE as it transport protocol. */
+ uses ICE as it transport protocol. */
+
static void
ice_conn_watch_CB (iceConn, clientData, opening, watchData)
IceConn iceConn;
#endif /* ! defined (SIGIO) */
}
-/* Try to open a connection to the session manager. */
+/* Create the client leader window. */
+
+static void
+create_client_leader_window (dpyinfo, client_id)
+ struct x_display_info *dpyinfo;
+ char *client_id;
+{
+ Window w;
+ XClassHint class_hints;
+ Atom sm_id;
+
+ w = XCreateSimpleWindow (dpyinfo->display,
+ dpyinfo->root_window,
+ -1, -1, 1, 1,
+ CopyFromParent, CopyFromParent, CopyFromParent);
+
+ class_hints.res_name = (char *) SDATA (Vx_resource_name);
+ class_hints.res_class = (char *) SDATA (Vx_resource_class);
+ XSetClassHint (dpyinfo->display, w, &class_hints);
+ XStoreName (dpyinfo->display, w, class_hints.res_name);
+
+ sm_id = XInternAtom (dpyinfo->display, "SM_CLIENT_ID", False);
+ XChangeProperty (dpyinfo->display, w, sm_id, XA_STRING, 8, PropModeReplace,
+ client_id, strlen (client_id));
+
+ dpyinfo->client_leader_window = w;
+}
+
+/* Try to open a connection to the session manager. */
+
void
-x_session_initialize ()
+x_session_initialize (dpyinfo)
+ struct x_display_info *dpyinfo;
{
#define SM_ERRORSTRING_LEN 512
char errorstring[SM_ERRORSTRING_LEN];
int name_len = 0;
/* Check if we where started by the session manager. If so, we will
- have a previous id. */
+ have a previous id. */
if (! EQ (Vx_session_previous_id, Qnil) && STRINGP (Vx_session_previous_id))
previous_id = SDATA (Vx_session_previous_id);
- /* Construct the path to the Emacs program. */
+ /* Construct the path to the Emacs program. */
if (! EQ (Vinvocation_directory, Qnil))
name_len += strlen (SDATA (Vinvocation_directory));
name_len += strlen (SDATA (Vinvocation_name));
/* This malloc will not be freed, but it is only done once, and hopefully
- not very large */
+ not very large */
emacs_program = xmalloc (name_len + 1);
emacs_program[0] = '\0';
strcat (emacs_program, SDATA (Vinvocation_name));
/* The SM protocol says all callbacks are mandatory, so set up all
- here and in the mask passed to SmcOpenConnection */
+ here and in the mask passed to SmcOpenConnection. */
callbacks.save_yourself.callback = smc_save_yourself_CB;
callbacks.save_yourself.client_data = 0;
callbacks.die.callback = smc_die_CB;
callbacks.shutdown_cancelled.callback = smc_shutdown_cancelled_CB;
callbacks.shutdown_cancelled.client_data = 0;
- /* Set error handlers. */
+ /* Set error handlers. */
SmcSetErrorHandler (smc_error_handler);
IceSetErrorHandler (ice_error_handler);
IceSetIOErrorHandler (ice_io_error_handler);
- /* Install callback for when connection status changes. */
+ /* Install callback for when connection status changes. */
IceAddConnectionWatch (ice_conn_watch_CB, 0);
/* Open the connection to the session manager. A failure is not
critical, it usually means that no session manager is running.
- The errorstring is here for debugging. */
+ The errorstring is here for debugging. */
smc_conn = SmcOpenConnection (NULL, NULL, 1, 0,
(SmcSaveYourselfProcMask|
SmcDieProcMask|
errorstring);
if (smc_conn != 0)
- Vx_session_id = make_string (client_id, strlen (client_id));
+ {
+ Vx_session_id = make_string (client_id, strlen (client_id));
+
+#ifdef USE_GTK
+ /* GTK creats a leader window by itself, but we need to tell
+ it about our client_id. */
+ gdk_set_sm_client_id (client_id);
+#else
+ create_client_leader_window (dpyinfo, client_id);
+#endif
+ }
}
DEFVAR_LISP ("x-session-previous-id", &Vx_session_previous_id,
doc: /* The previous session id Emacs got from session manager.
If Emacs is running on a window system that has a session manager, the
-session manager gives Emacs a session id. It is feasible for Emacs lisp
+session manager gives Emacs a session id. It is feasible for Emacs Lisp
code to use the session id to save configuration in, for example, a file
with a file name based on the session id. If Emacs is running when the
window system is shut down, the session manager remembers that Emacs was
}
#endif /* HAVE_X_SM */
+
+/* arch-tag: 56a2c58c-adfa-430a-b772-130abd29fd2e
+ (do not change this comment) */