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