Fix -Wwrite-strings warnings in fakemail.c.
[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
JD
89
90#define NOSPLASH_OPT "--no-splash"
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;
7d0393cf 201
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
JD
206 props[props_idx] = &prop_ptr[props_idx];
207 props[props_idx]->name = SmCloneCommand;
208 props[props_idx]->type = SmLISTofARRAY8;
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
JD
216 props[props_idx] = &prop_ptr[props_idx];
217 props[props_idx]->name = SmProgram;
218 props[props_idx]->type = SmARRAY8;
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
JD
226 props[props_idx] = &prop_ptr[props_idx];
227 props[props_idx]->name = SmRestartCommand;
228 props[props_idx]->type = 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
JD
260 props[props_idx] = &prop_ptr[props_idx];
261 props[props_idx]->name = SmUserID;
262 props[props_idx]->type = SmARRAY8;
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];
273 props[props_idx]->name = SmCurrentDirectory;
274 props[props_idx]->type = SmARRAY8;
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);
c187839d 289
4f2f546e 290 /* See if we maybe shall interact with the user. */
afb4ecad
JD
291 if (interactStyle != SmInteractStyleAny
292 || ! shutdown
293 || saveType == SmSaveLocal
294 || ! SmcInteractRequest (smcConn, SmDialogNormal, smc_interact_CB, 0))
295 {
4f2f546e 296 /* No interaction, we are done saving ourself. */
afb4ecad
JD
297 SmcSaveYourselfDone (smcConn, True);
298 }
299}
300
4f2f546e
JD
301/* According to the SM specification, this shall close the connection. */
302
afb4ecad 303static void
971de7fb 304smc_die_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
305{
306 SmcCloseConnection (smcConn, 0, 0);
182659ae 307 ice_connection_closed ();
afb4ecad
JD
308}
309
310/* We don't use the next two but they are mandatory, leave them empty.
311 According to the SM specification, we should not interact with the
312 user between smc_save_yourself_CB is called and until smc_save_complete_CB
313 is called. It seems like a lot of job to implement this and it doesn't
4f2f546e
JD
314 even seem necessary. */
315
afb4ecad 316static void
971de7fb 317smc_save_complete_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
318{
319 /* Empty */
320}
321
322static void
971de7fb 323smc_shutdown_cancelled_CB (SmcConn smcConn, SmPointer clientData)
afb4ecad
JD
324{
325 /* Empty */
326}
327
f25dcaa0 328/* Error handlers for SM and ICE. We don't want to exit Emacs just
4f2f546e
JD
329 because there is some error in the session management. */
330
afb4ecad 331static void
dd4c5104
DN
332smc_error_handler (SmcConn smcConn,
333 Bool swap,
334 int offendingMinorOpcode,
335 unsigned long offendingSequence,
336 int errorClass,
337 int severity,
338 SmPointer values)
afb4ecad 339{
4f2f546e 340 /* Empty */
afb4ecad
JD
341}
342
343static void
dd4c5104
DN
344ice_error_handler (IceConn iceConn,
345 Bool swap,
346 int offendingMinorOpcode,
347 unsigned long offendingSequence,
348 int errorClass,
349 int severity,
350 IcePointer values)
afb4ecad 351{
4f2f546e 352 /* Empty */
afb4ecad
JD
353}
354
355
356static void
971de7fb 357ice_io_error_handler (IceConn iceConn)
afb4ecad 358{
4f2f546e 359 /* Connection probably gone. */
182659ae 360 ice_connection_closed ();
afb4ecad
JD
361}
362
363/* This is called when the ICE connection is created or closed. The SM library
4f2f546e
JD
364 uses ICE as it transport protocol. */
365
afb4ecad 366static void
971de7fb 367ice_conn_watch_CB (IceConn iceConn, IcePointer clientData, int opening, IcePointer *watchData)
afb4ecad
JD
368{
369 if (! opening)
370 {
182659ae 371 ice_connection_closed ();
afb4ecad
JD
372 return;
373 }
7d0393cf 374
afb4ecad 375 ice_fd = IceConnectionNumber (iceConn);
afb4ecad 376#ifdef F_SETOWN
afb4ecad 377 fcntl (ice_fd, F_SETOWN, getpid ());
afb4ecad 378#endif /* ! defined (F_SETOWN) */
afb4ecad
JD
379
380#ifdef SIGIO
381 if (interrupt_input)
382 init_sigio (ice_fd);
383#endif /* ! defined (SIGIO) */
182659ae
JD
384
385 add_keyboard_wait_descriptor (ice_fd);
afb4ecad
JD
386}
387
231d6cfb 388/* Create the client leader window. */
4f2f546e 389
e4c8d29a 390#ifndef USE_GTK
231d6cfb 391static void
971de7fb 392create_client_leader_window (struct x_display_info *dpyinfo, char *client_id)
231d6cfb
JD
393{
394 Window w;
395 XClassHint class_hints;
396 Atom sm_id;
397
398 w = XCreateSimpleWindow (dpyinfo->display,
399 dpyinfo->root_window,
400 -1, -1, 1, 1,
401 CopyFromParent, CopyFromParent, CopyFromParent);
402
403 class_hints.res_name = (char *) SDATA (Vx_resource_name);
404 class_hints.res_class = (char *) SDATA (Vx_resource_class);
405 XSetClassHint (dpyinfo->display, w, &class_hints);
406 XStoreName (dpyinfo->display, w, class_hints.res_name);
407
408 sm_id = XInternAtom (dpyinfo->display, "SM_CLIENT_ID", False);
409 XChangeProperty (dpyinfo->display, w, sm_id, XA_STRING, 8, PropModeReplace,
e4c8d29a 410 (unsigned char *)client_id, strlen (client_id));
231d6cfb
JD
411
412 dpyinfo->client_leader_window = w;
413}
e4c8d29a
J
414#endif /* ! USE_GTK */
415
231d6cfb 416
4f2f546e
JD
417/* Try to open a connection to the session manager. */
418
afb4ecad 419void
971de7fb 420x_session_initialize (struct x_display_info *dpyinfo)
afb4ecad
JD
421{
422#define SM_ERRORSTRING_LEN 512
423 char errorstring[SM_ERRORSTRING_LEN];
424 char* previous_id = NULL;
425 SmcCallbacks callbacks;
426 int name_len = 0;
7d0393cf 427
182659ae
JD
428 ice_fd = -1;
429 doing_interact = False;
430
afb4ecad 431 /* Check if we where started by the session manager. If so, we will
4f2f546e 432 have a previous id. */
afb4ecad 433 if (! EQ (Vx_session_previous_id, Qnil) && STRINGP (Vx_session_previous_id))
e4c8d29a 434 previous_id = SSDATA (Vx_session_previous_id);
afb4ecad 435
4f2f546e 436 /* Construct the path to the Emacs program. */
afb4ecad 437 if (! EQ (Vinvocation_directory, Qnil))
e4c8d29a
J
438 name_len += strlen (SSDATA (Vinvocation_directory));
439 name_len += strlen (SSDATA (Vinvocation_name));
afb4ecad
JD
440
441 /* This malloc will not be freed, but it is only done once, and hopefully
4f2f546e 442 not very large */
afb4ecad
JD
443 emacs_program = xmalloc (name_len + 1);
444 emacs_program[0] = '\0';
445
446 if (! EQ (Vinvocation_directory, Qnil))
e4c8d29a
J
447 strcpy (emacs_program, SSDATA (Vinvocation_directory));
448 strcat (emacs_program, SSDATA (Vinvocation_name));
7d0393cf 449
afb4ecad 450 /* The SM protocol says all callbacks are mandatory, so set up all
4f2f546e 451 here and in the mask passed to SmcOpenConnection. */
afb4ecad
JD
452 callbacks.save_yourself.callback = smc_save_yourself_CB;
453 callbacks.save_yourself.client_data = 0;
454 callbacks.die.callback = smc_die_CB;
455 callbacks.die.client_data = 0;
456 callbacks.save_complete.callback = smc_save_complete_CB;
457 callbacks.save_complete.client_data = 0;
458 callbacks.shutdown_cancelled.callback = smc_shutdown_cancelled_CB;
459 callbacks.shutdown_cancelled.client_data = 0;
460
4f2f546e 461 /* Set error handlers. */
afb4ecad
JD
462 SmcSetErrorHandler (smc_error_handler);
463 IceSetErrorHandler (ice_error_handler);
464 IceSetIOErrorHandler (ice_io_error_handler);
465
4f2f546e 466 /* Install callback for when connection status changes. */
afb4ecad
JD
467 IceAddConnectionWatch (ice_conn_watch_CB, 0);
468
469 /* Open the connection to the session manager. A failure is not
f25dcaa0 470 critical, it usually means that no session manager is running.
4f2f546e 471 The errorstring is here for debugging. */
afb4ecad
JD
472 smc_conn = SmcOpenConnection (NULL, NULL, 1, 0,
473 (SmcSaveYourselfProcMask|
474 SmcDieProcMask|
475 SmcSaveCompleteProcMask|
476 SmcShutdownCancelledProcMask),
477 &callbacks,
478 previous_id,
479 &client_id,
480 SM_ERRORSTRING_LEN,
481 errorstring);
482
483 if (smc_conn != 0)
231d6cfb
JD
484 {
485 Vx_session_id = make_string (client_id, strlen (client_id));
486
487#ifdef USE_GTK
488 /* GTK creats a leader window by itself, but we need to tell
489 it about our client_id. */
490 gdk_set_sm_client_id (client_id);
491#else
492 create_client_leader_window (dpyinfo, client_id);
493#endif
494 }
afb4ecad
JD
495}
496
d51abf22
KL
497/* Ensure that the session manager is not contacted again. */
498
499void
971de7fb 500x_session_close (void)
d51abf22 501{
182659ae 502 ice_connection_closed ();
d51abf22
KL
503}
504
afb4ecad
JD
505
506DEFUN ("handle-save-session", Fhandle_save_session,
507 Shandle_save_session, 1, 1, "e",
508 doc: /* Handle the save_yourself event from a session manager.
7d0393cf 509A session manager can tell Emacs that the window system is shutting down
afb4ecad
JD
510by sending Emacs a save_yourself message. Emacs executes this function when
511such an event occurs. This function then executes `emacs-session-save'.
512After that, this function informs the session manager that it can continue
513or abort shutting down the window system depending on the return value
514from `emacs-session-save' If the return value is non-nil the session manager
515is told to abort the window system shutdown.
516
517Do not call this function yourself. */)
5842a27b 518 (Lisp_Object event)
afb4ecad
JD
519{
520 /* Check doing_interact so that we don't do anything if someone called
521 this at the wrong time. */
522 if (doing_interact)
523 {
524 Bool cancel_shutdown = False;
525
526 cancel_shutdown = ! EQ (call0 (intern ("emacs-session-save")), Qnil);
527
528 SmcInteractDone (smc_conn, cancel_shutdown);
529 SmcSaveYourselfDone (smc_conn, True);
530
531 doing_interact = False;
532 }
ad4ace7a
JD
533
534 return Qnil;
afb4ecad
JD
535}
536
537\f
538/***********************************************************************
539 Initialization
540 ***********************************************************************/
541void
971de7fb 542syms_of_xsmfns (void)
afb4ecad
JD
543{
544 DEFVAR_LISP ("x-session-id", &Vx_session_id,
545 doc: /* The session id Emacs got from the session manager for this session.
546Changing the value does not change the session id used by Emacs.
547The value is nil if no session manager is running.
548See also `x-session-previous-id', `emacs-save-session-functions',
549`emacs-session-save' and `emacs-session-restore'." */);
550 Vx_session_id = Qnil;
551
552 DEFVAR_LISP ("x-session-previous-id", &Vx_session_previous_id,
553 doc: /* The previous session id Emacs got from session manager.
7d0393cf 554If Emacs is running on a window system that has a session manager, the
fbcdaefb 555session manager gives Emacs a session id. It is feasible for Emacs Lisp
7d0393cf
JB
556code to use the session id to save configuration in, for example, a file
557with a file name based on the session id. If Emacs is running when the
558window system is shut down, the session manager remembers that Emacs was
afb4ecad
JD
559running and saves the session id Emacs had.
560
7d0393cf
JB
561When the window system is started again, the session manager restarts
562Emacs and hands Emacs the session id it had the last time it was
563running. This is now the previous session id and the value of this
564variable. If configuration was saved in a file as stated above, the
afb4ecad
JD
565previous session id shall be used to reconstruct the file name.
566
7d0393cf 567The session id Emacs has while it is running is in the variable
afb4ecad
JD
568`x-session-id'. The value of this variable and `x-session-id' may be the
569same, depending on how the session manager works.
570
571See also `emacs-save-session-functions', `emacs-session-save' and
572`emacs-session-restore'." */);
573 Vx_session_previous_id = Qnil;
7d0393cf 574
afb4ecad
JD
575 defsubr (&Shandle_save_session);
576}
577
578#endif /* HAVE_X_SM */
ab5796a9
MB
579
580/* arch-tag: 56a2c58c-adfa-430a-b772-130abd29fd2e
581 (do not change this comment) */