Merge from trunk
[bpt/emacs.git] / src / xsmfns.c
CommitLineData
afb4ecad
JD
1/* Session management module for systems which understand the X Session
2 management protocol.
114f9c96 3 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
8cabe764 4 Free Software Foundation, Inc.
afb4ecad
JD
5
6This file is part of GNU Emacs.
7
9ec0b715 8GNU Emacs is free software: you can redistribute it and/or modify
afb4ecad 9it under the terms of the GNU General Public License as published by
9ec0b715
GM
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
afb4ecad
JD
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
9ec0b715 19along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
afb4ecad
JD
20
21#include <config.h>
22
23#ifdef HAVE_X_SM
24
25#include <X11/SM/SMlib.h>
231d6cfb
JD
26#include <X11/Xlib.h>
27#include <X11/Xutil.h>
28
afb4ecad
JD
29#ifdef HAVE_UNISTD_H
30#include <unistd.h>
31#endif
afb4ecad
JD
32
33#include <sys/param.h>
656132eb 34#include <stdio.h>
d7306fe6 35#include <setjmp.h>
afb4ecad 36
fa8459a3 37#include "lisp.h"
afb4ecad
JD
38#include "systime.h"
39#include "sysselect.h"
428a555e 40#include "frame.h"
afb4ecad 41#include "termhooks.h"
656132eb 42#include "termopts.h"
231d6cfb 43#include "xterm.h"
afb4ecad 44
e4c8d29a
J
45/* Avoid "differ in sign" warnings */
46#define SSDATA(x) ((char *) SDATA (x))
47
3b8f9651 48/* This is the event used when SAVE_SESSION_EVENT occurs. */
afb4ecad
JD
49
50static struct input_event emacs_event;
51
3b8f9651 52/* The descriptor that we use to check for data from the session manager. */
afb4ecad 53
182659ae 54static int ice_fd;
afb4ecad 55
3b8f9651 56/* A flag that says if we are in shutdown interactions or not. */
afb4ecad 57
182659ae 58static int doing_interact;
afb4ecad 59
4f2f546e 60/* The session manager object for the session manager connection. */
afb4ecad
JD
61
62static SmcConn smc_conn;
63
4f2f546e
JD
64/* The client session id for this session. */
65
afb4ecad
JD
66static char *client_id;
67
4f2f546e
JD
68/* The full path name to the Emacs program. */
69
afb4ecad
JD
70static char *emacs_program;
71
4f2f546e 72/* The client session id for this session as a lisp object. */
afb4ecad
JD
73
74Lisp_Object Vx_session_id;
75
76/* The id we had the previous session. This is only available if we
4f2f546e 77 have been started by the session manager with SMID_OPT. */
afb4ecad
JD
78
79Lisp_Object Vx_session_previous_id;
80
81/* The option we tell the session manager to start Emacs with when
4f2f546e 82 restarting Emacs. The client_id is appended. */
afb4ecad
JD
83
84#define SMID_OPT "--smid="
85
86
ca2417b9 87/* The option to start Emacs without the splash screen when
4f2f546e 88 restarting Emacs. */
ca2417b9 89
42ca4633 90static char NOSPLASH_OPT[] = "--no-splash";
ca2417b9 91
f63d0028
JD
92/* The option to make Emacs start in the given directory. */
93
94#define CHDIR_OPT "--chdir="
95
182659ae 96static void
971de7fb 97ice_connection_closed (void)
182659ae
JD
98{
99 if (ice_fd >= 0)
100 delete_keyboard_wait_descriptor (ice_fd);
101 ice_fd = -1;
102}
103
ca2417b9 104
afb4ecad
JD
105/* Handle any messages from the session manager. If no connection is
106 open to a session manager, just return 0.
a6950dfa 107 Otherwise returns 1 if SAVE_SESSION_EVENT is stored in buffer BUFP. */
4f2f546e 108
afb4ecad 109int
971de7fb 110x_session_check_input (struct input_event *bufp)
afb4ecad
JD
111{
112 SELECT_TYPE read_fds;
113 EMACS_TIME tmout;
182659ae 114 int ret;
7d0393cf 115
afb4ecad 116 if (ice_fd == -1) return 0;
afb4ecad
JD
117 FD_ZERO (&read_fds);
118 FD_SET (ice_fd, &read_fds);
7d0393cf 119
afb4ecad
JD
120 tmout.tv_sec = 0;
121 tmout.tv_usec = 0;
7d0393cf 122
afb4ecad
JD
123 /* Reset this so wo can check kind after callbacks have been called by
124 IceProcessMessages. The smc_interact_CB sets the kind to
3b8f9651 125 SAVE_SESSION_EVENT, but we don't know beforehand if that callback
4f2f546e 126 will be called. */
3b8f9651 127 emacs_event.kind = NO_EVENT;
afb4ecad 128
182659ae
JD
129 ret = select (ice_fd+1, &read_fds,
130 (SELECT_TYPE *)0, (SELECT_TYPE *)0, &tmout);
131
132 if (ret < 0)
afb4ecad 133 {
182659ae
JD
134 ice_connection_closed ();
135 }
136 else if (ret > 0 && FD_ISSET (ice_fd, &read_fds))
137 {
138 ret = IceProcessMessages (SmcGetIceConnection (smc_conn),
139 (IceReplyWaitInfo *)0, (Bool *)0);
140 if (ret != IceProcessMessagesSuccess)
141 {
142 /* Either IO error or Connection closed. */
143 if (ret == IceProcessMessagesIOError)
144 IceCloseConnection (SmcGetIceConnection (smc_conn));
145
146 ice_connection_closed ();
147 }
afb4ecad 148 }
7d0393cf 149
afb4ecad 150 /* Check if smc_interact_CB was called and we shall generate a
4f2f546e 151 SAVE_SESSION_EVENT. */
182659ae 152 if (emacs_event.kind != NO_EVENT)
72af86bd 153 memcpy (bufp, &emacs_event, sizeof (struct input_event));
afb4ecad 154
182659ae 155 return emacs_event.kind != NO_EVENT ? 1 : 0;
afb4ecad
JD
156}
157
4f2f546e
JD
158/* Return non-zero if we have a connection to a session manager. */
159
afb4ecad 160int
971de7fb 161x_session_have_connection (void)
afb4ecad
JD
162{
163 return ice_fd != -1;
164}
165
166/* This is called when the session manager says it is OK to interact with the
3b8f9651 167 user. Here we set the kind to SAVE_SESSION_EVENT so an event is generated.
4f2f546e
JD
168 Then lisp code can interact with the user. */
169
afb4ecad 170static void
971de7fb 171smc_interact_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
172{
173 doing_interact = True;
3b8f9651 174 emacs_event.kind = SAVE_SESSION_EVENT;
afb4ecad
JD
175}
176
f25dcaa0 177/* This is called when the session manager tells us to save ourselves.
afb4ecad
JD
178 We set the required properties so the session manager can restart us,
179 plus the current working directory property (not mandatory) so we
180 are started in the correct directory.
181
182 If this is a shutdown and we can request to interact with the user,
4f2f546e
JD
183 we do so, because we don't know what the lisp code might do. */
184
afb4ecad 185static void
dd4c5104
DN
186smc_save_yourself_CB (SmcConn smcConn,
187 SmPointer clientData,
188 int saveType,
189 Bool shutdown,
190 int interactStyle,
191 Bool fast)
afb4ecad
JD
192{
193#define NR_PROPS 5
7d0393cf 194
afb4ecad
JD
195 SmProp *props[NR_PROPS];
196 SmProp prop_ptr[NR_PROPS];
7d0393cf 197
afb4ecad
JD
198 SmPropValue values[20];
199 int val_idx = 0;
200 int props_idx = 0;
42ca4633 201 int i;
c187839d 202 char *cwd = NULL;
f63d0028 203 char *smid_opt, *chdir_opt = NULL;
afb4ecad 204
4f2f546e 205 /* How to start a new instance of Emacs. */
afb4ecad 206 props[props_idx] = &prop_ptr[props_idx];
42ca4633
J
207 props[props_idx]->name = xstrdup (SmCloneCommand);
208 props[props_idx]->type = xstrdup (SmLISTofARRAY8);
afb4ecad
JD
209 props[props_idx]->num_vals = 1;
210 props[props_idx]->vals = &values[val_idx++];
211 props[props_idx]->vals[0].length = strlen (emacs_program);
212 props[props_idx]->vals[0].value = emacs_program;
213 ++props_idx;
214
4f2f546e 215 /* The name of the program. */
afb4ecad 216 props[props_idx] = &prop_ptr[props_idx];
42ca4633
J
217 props[props_idx]->name = xstrdup (SmProgram);
218 props[props_idx]->type = xstrdup (SmARRAY8);
afb4ecad
JD
219 props[props_idx]->num_vals = 1;
220 props[props_idx]->vals = &values[val_idx++];
e4c8d29a 221 props[props_idx]->vals[0].length = strlen (SSDATA (Vinvocation_name));
d5db4077 222 props[props_idx]->vals[0].value = SDATA (Vinvocation_name);
afb4ecad 223 ++props_idx;
7d0393cf 224
f63d0028 225 /* How to restart Emacs. */
afb4ecad 226 props[props_idx] = &prop_ptr[props_idx];
42ca4633
J
227 props[props_idx]->name = xstrdup (SmRestartCommand);
228 props[props_idx]->type = xstrdup (SmLISTofARRAY8);
f63d0028
JD
229 /* /path/to/emacs, --smid=xxx --no-splash --chdir=dir */
230 props[props_idx]->num_vals = 4;
afb4ecad
JD
231 props[props_idx]->vals = &values[val_idx];
232 props[props_idx]->vals[0].length = strlen (emacs_program);
233 props[props_idx]->vals[0].value = emacs_program;
234
235 smid_opt = xmalloc (strlen (SMID_OPT) + strlen (client_id) + 1);
236 strcpy (smid_opt, SMID_OPT);
237 strcat (smid_opt, client_id);
7d0393cf 238
afb4ecad
JD
239 props[props_idx]->vals[1].length = strlen (smid_opt);
240 props[props_idx]->vals[1].value = smid_opt;
ca2417b9
JD
241
242 props[props_idx]->vals[2].length = strlen (NOSPLASH_OPT);
243 props[props_idx]->vals[2].value = NOSPLASH_OPT;
f63d0028
JD
244
245 cwd = get_current_dir_name ();
246 if (cwd)
247 {
991be6d4 248 chdir_opt = xmalloc (strlen (CHDIR_OPT) + strlen (cwd) + 1);
f63d0028
JD
249 strcpy (chdir_opt, CHDIR_OPT);
250 strcat (chdir_opt, cwd);
251
252 props[props_idx]->vals[3].length = strlen (chdir_opt);
253 props[props_idx]->vals[3].value = chdir_opt;
254 }
255
256 val_idx += cwd ? 4 : 3;
afb4ecad
JD
257 ++props_idx;
258
4f2f546e 259 /* User id. */
afb4ecad 260 props[props_idx] = &prop_ptr[props_idx];
42ca4633
J
261 props[props_idx]->name = xstrdup (SmUserID);
262 props[props_idx]->type = xstrdup (SmARRAY8);
afb4ecad
JD
263 props[props_idx]->num_vals = 1;
264 props[props_idx]->vals = &values[val_idx++];
e4c8d29a 265 props[props_idx]->vals[0].length = strlen (SSDATA (Vuser_login_name));
d5db4077 266 props[props_idx]->vals[0].value = SDATA (Vuser_login_name);
afb4ecad
JD
267 ++props_idx;
268
c187839d
EZ
269
270 if (cwd)
afb4ecad
JD
271 {
272 props[props_idx] = &prop_ptr[props_idx];
42ca4633
J
273 props[props_idx]->name = xstrdup (SmCurrentDirectory);
274 props[props_idx]->type = xstrdup (SmARRAY8);
afb4ecad
JD
275 props[props_idx]->num_vals = 1;
276 props[props_idx]->vals = &values[val_idx++];
277 props[props_idx]->vals[0].length = strlen (cwd);
278 props[props_idx]->vals[0].value = cwd;
279 ++props_idx;
280 }
7d0393cf
JB
281
282
afb4ecad
JD
283 SmcSetProperties (smcConn, props_idx, props);
284
285 xfree (smid_opt);
f63d0028 286 xfree (chdir_opt);
afb4ecad 287
c2cd06e6 288 free (cwd);
42ca4633
J
289 for (i = 0; i < props_idx; ++i)
290 {
291 xfree (props[i]->type);
292 xfree (props[i]->name);
293 }
c187839d 294
4f2f546e 295 /* See if we maybe shall interact with the user. */
afb4ecad
JD
296 if (interactStyle != SmInteractStyleAny
297 || ! shutdown
298 || saveType == SmSaveLocal
299 || ! SmcInteractRequest (smcConn, SmDialogNormal, smc_interact_CB, 0))
300 {
4f2f546e 301 /* No interaction, we are done saving ourself. */
afb4ecad
JD
302 SmcSaveYourselfDone (smcConn, True);
303 }
304}
305
4f2f546e
JD
306/* According to the SM specification, this shall close the connection. */
307
afb4ecad 308static void
971de7fb 309smc_die_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
310{
311 SmcCloseConnection (smcConn, 0, 0);
182659ae 312 ice_connection_closed ();
afb4ecad
JD
313}
314
315/* We don't use the next two but they are mandatory, leave them empty.
316 According to the SM specification, we should not interact with the
317 user between smc_save_yourself_CB is called and until smc_save_complete_CB
318 is called. It seems like a lot of job to implement this and it doesn't
4f2f546e
JD
319 even seem necessary. */
320
afb4ecad 321static void
971de7fb 322smc_save_complete_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
323{
324 /* Empty */
325}
326
327static void
971de7fb 328smc_shutdown_cancelled_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
329{
330 /* Empty */
331}
332
f25dcaa0 333/* Error handlers for SM and ICE. We don't want to exit Emacs just
4f2f546e
JD
334 because there is some error in the session management. */
335
afb4ecad 336static void
dd4c5104
DN
337smc_error_handler (SmcConn smcConn,
338 Bool swap,
339 int offendingMinorOpcode,
340 unsigned long offendingSequence,
341 int errorClass,
342 int severity,
343 SmPointer values)
afb4ecad 344{
4f2f546e 345 /* Empty */
afb4ecad
JD
346}
347
348static void
dd4c5104
DN
349ice_error_handler (IceConn iceConn,
350 Bool swap,
351 int offendingMinorOpcode,
352 unsigned long offendingSequence,
353 int errorClass,
354 int severity,
355 IcePointer values)
afb4ecad 356{
4f2f546e 357 /* Empty */
afb4ecad
JD
358}
359
360
361static void
971de7fb 362ice_io_error_handler (IceConn iceConn)
afb4ecad 363{
4f2f546e 364 /* Connection probably gone. */
182659ae 365 ice_connection_closed ();
afb4ecad
JD
366}
367
368/* This is called when the ICE connection is created or closed. The SM library
4f2f546e
JD
369 uses ICE as it transport protocol. */
370
afb4ecad 371static void
971de7fb 372ice_conn_watch_CB (IceConn iceConn, IcePointer clientData, int opening, IcePointer *watchData)
afb4ecad
JD
373{
374 if (! opening)
375 {
182659ae 376 ice_connection_closed ();
afb4ecad
JD
377 return;
378 }
7d0393cf 379
afb4ecad 380 ice_fd = IceConnectionNumber (iceConn);
afb4ecad 381#ifdef F_SETOWN
afb4ecad 382 fcntl (ice_fd, F_SETOWN, getpid ());
afb4ecad 383#endif /* ! defined (F_SETOWN) */
afb4ecad
JD
384
385#ifdef SIGIO
386 if (interrupt_input)
387 init_sigio (ice_fd);
388#endif /* ! defined (SIGIO) */
182659ae
JD
389
390 add_keyboard_wait_descriptor (ice_fd);
afb4ecad
JD
391}
392
231d6cfb 393/* Create the client leader window. */
4f2f546e 394
e4c8d29a 395#ifndef USE_GTK
231d6cfb 396static void
971de7fb 397create_client_leader_window (struct x_display_info *dpyinfo, char *client_id)
231d6cfb
JD
398{
399 Window w;
400 XClassHint class_hints;
401 Atom sm_id;
402
403 w = XCreateSimpleWindow (dpyinfo->display,
404 dpyinfo->root_window,
405 -1, -1, 1, 1,
406 CopyFromParent, CopyFromParent, CopyFromParent);
407
408 class_hints.res_name = (char *) SDATA (Vx_resource_name);
409 class_hints.res_class = (char *) SDATA (Vx_resource_class);
410 XSetClassHint (dpyinfo->display, w, &class_hints);
411 XStoreName (dpyinfo->display, w, class_hints.res_name);
412
2d9074ba
JD
413 XChangeProperty (dpyinfo->display, w, dpyinfo->Xatom_SM_CLIENT_ID,
414 XA_STRING, 8, PropModeReplace,
e4c8d29a 415 (unsigned char *)client_id, strlen (client_id));
231d6cfb
JD
416
417 dpyinfo->client_leader_window = w;
418}
e4c8d29a
J
419#endif /* ! USE_GTK */
420
231d6cfb 421
4f2f546e
JD
422/* Try to open a connection to the session manager. */
423
afb4ecad 424void
971de7fb 425x_session_initialize (struct x_display_info *dpyinfo)
afb4ecad
JD
426{
427#define SM_ERRORSTRING_LEN 512
428 char errorstring[SM_ERRORSTRING_LEN];
429 char* previous_id = NULL;
430 SmcCallbacks callbacks;
431 int name_len = 0;
7d0393cf 432
182659ae
JD
433 ice_fd = -1;
434 doing_interact = False;
435
afb4ecad 436 /* Check if we where started by the session manager. If so, we will
4f2f546e 437 have a previous id. */
afb4ecad 438 if (! EQ (Vx_session_previous_id, Qnil) && STRINGP (Vx_session_previous_id))
e4c8d29a 439 previous_id = SSDATA (Vx_session_previous_id);
afb4ecad 440
4f2f546e 441 /* Construct the path to the Emacs program. */
afb4ecad 442 if (! EQ (Vinvocation_directory, Qnil))
e4c8d29a
J
443 name_len += strlen (SSDATA (Vinvocation_directory));
444 name_len += strlen (SSDATA (Vinvocation_name));
afb4ecad
JD
445
446 /* This malloc will not be freed, but it is only done once, and hopefully
4f2f546e 447 not very large */
afb4ecad
JD
448 emacs_program = xmalloc (name_len + 1);
449 emacs_program[0] = '\0';
450
451 if (! EQ (Vinvocation_directory, Qnil))
e4c8d29a
J
452 strcpy (emacs_program, SSDATA (Vinvocation_directory));
453 strcat (emacs_program, SSDATA (Vinvocation_name));
7d0393cf 454
afb4ecad 455 /* The SM protocol says all callbacks are mandatory, so set up all
4f2f546e 456 here and in the mask passed to SmcOpenConnection. */
afb4ecad
JD
457 callbacks.save_yourself.callback = smc_save_yourself_CB;
458 callbacks.save_yourself.client_data = 0;
459 callbacks.die.callback = smc_die_CB;
460 callbacks.die.client_data = 0;
461 callbacks.save_complete.callback = smc_save_complete_CB;
462 callbacks.save_complete.client_data = 0;
463 callbacks.shutdown_cancelled.callback = smc_shutdown_cancelled_CB;
464 callbacks.shutdown_cancelled.client_data = 0;
465
4f2f546e 466 /* Set error handlers. */
afb4ecad
JD
467 SmcSetErrorHandler (smc_error_handler);
468 IceSetErrorHandler (ice_error_handler);
469 IceSetIOErrorHandler (ice_io_error_handler);
470
4f2f546e 471 /* Install callback for when connection status changes. */
afb4ecad
JD
472 IceAddConnectionWatch (ice_conn_watch_CB, 0);
473
474 /* Open the connection to the session manager. A failure is not
f25dcaa0 475 critical, it usually means that no session manager is running.
4f2f546e 476 The errorstring is here for debugging. */
afb4ecad
JD
477 smc_conn = SmcOpenConnection (NULL, NULL, 1, 0,
478 (SmcSaveYourselfProcMask|
479 SmcDieProcMask|
480 SmcSaveCompleteProcMask|
481 SmcShutdownCancelledProcMask),
482 &callbacks,
483 previous_id,
484 &client_id,
485 SM_ERRORSTRING_LEN,
486 errorstring);
487
488 if (smc_conn != 0)
231d6cfb
JD
489 {
490 Vx_session_id = make_string (client_id, strlen (client_id));
491
492#ifdef USE_GTK
493 /* GTK creats a leader window by itself, but we need to tell
494 it about our client_id. */
495 gdk_set_sm_client_id (client_id);
496#else
497 create_client_leader_window (dpyinfo, client_id);
498#endif
499 }
afb4ecad
JD
500}
501
d51abf22
KL
502/* Ensure that the session manager is not contacted again. */
503
504void
971de7fb 505x_session_close (void)
d51abf22 506{
182659ae 507 ice_connection_closed ();
d51abf22
KL
508}
509
afb4ecad
JD
510
511DEFUN ("handle-save-session", Fhandle_save_session,
512 Shandle_save_session, 1, 1, "e",
513 doc: /* Handle the save_yourself event from a session manager.
7d0393cf 514A session manager can tell Emacs that the window system is shutting down
afb4ecad
JD
515by sending Emacs a save_yourself message. Emacs executes this function when
516such an event occurs. This function then executes `emacs-session-save'.
517After that, this function informs the session manager that it can continue
518or abort shutting down the window system depending on the return value
519from `emacs-session-save' If the return value is non-nil the session manager
520is told to abort the window system shutdown.
521
522Do not call this function yourself. */)
5842a27b 523 (Lisp_Object event)
afb4ecad
JD
524{
525 /* Check doing_interact so that we don't do anything if someone called
526 this at the wrong time. */
527 if (doing_interact)
528 {
529 Bool cancel_shutdown = False;
530
531 cancel_shutdown = ! EQ (call0 (intern ("emacs-session-save")), Qnil);
532
533 SmcInteractDone (smc_conn, cancel_shutdown);
534 SmcSaveYourselfDone (smc_conn, True);
535
536 doing_interact = False;
537 }
ad4ace7a
JD
538
539 return Qnil;
afb4ecad
JD
540}
541
542\f
543/***********************************************************************
544 Initialization
545 ***********************************************************************/
546void
971de7fb 547syms_of_xsmfns (void)
afb4ecad
JD
548{
549 DEFVAR_LISP ("x-session-id", &Vx_session_id,
550 doc: /* The session id Emacs got from the session manager for this session.
551Changing the value does not change the session id used by Emacs.
552The value is nil if no session manager is running.
553See also `x-session-previous-id', `emacs-save-session-functions',
554`emacs-session-save' and `emacs-session-restore'." */);
555 Vx_session_id = Qnil;
556
557 DEFVAR_LISP ("x-session-previous-id", &Vx_session_previous_id,
558 doc: /* The previous session id Emacs got from session manager.
7d0393cf 559If Emacs is running on a window system that has a session manager, the
fbcdaefb 560session manager gives Emacs a session id. It is feasible for Emacs Lisp
7d0393cf
JB
561code to use the session id to save configuration in, for example, a file
562with a file name based on the session id. If Emacs is running when the
563window system is shut down, the session manager remembers that Emacs was
afb4ecad
JD
564running and saves the session id Emacs had.
565
7d0393cf
JB
566When the window system is started again, the session manager restarts
567Emacs and hands Emacs the session id it had the last time it was
568running. This is now the previous session id and the value of this
569variable. If configuration was saved in a file as stated above, the
afb4ecad
JD
570previous session id shall be used to reconstruct the file name.
571
7d0393cf 572The session id Emacs has while it is running is in the variable
afb4ecad
JD
573`x-session-id'. The value of this variable and `x-session-id' may be the
574same, depending on how the session manager works.
575
576See also `emacs-save-session-functions', `emacs-session-save' and
577`emacs-session-restore'." */);
578 Vx_session_previous_id = Qnil;
7d0393cf 579
afb4ecad
JD
580 defsubr (&Shandle_save_session);
581}
582
583#endif /* HAVE_X_SM */
ab5796a9
MB
584
585/* arch-tag: 56a2c58c-adfa-430a-b772-130abd29fd2e
586 (do not change this comment) */