2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2009 Kim Woelders
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #include "e16-ecore_hints.h"
40 #ifdef USE_EXT_INIT_WIN
41 static Window new_init_win_ext = None;
44 #ifndef DEFAULT_SH_PATH
47 * It appears that SGI (at least IRIX 6.4) uses ksh as their sh, and it
48 * seems to run in restricted mode, so things like restart fail miserably.
49 * Let's use csh instead
52 #define DEFAULT_SH_PATH "/sbin/csh"
54 #define DEFAULT_SH_PATH "/bin/sh"
58 /* True if we are saving state for a doExit("restart") */
59 static int restarting = False;
63 #include <X11/SM/SMlib.h>
66 * NB! If the discard property is revived, the dual use of buf must be fixed.
68 #define USE_DISCARD_PROPERTY 0
70 static char *sm_client_id = NULL;
71 static SmcConn sm_conn = NULL;
73 static EventFdDesc *sm_efd = NULL;
76 set_save_props(SmcConn smc_conn, int master_flag)
84 SmPropValue programVal;
85 SmPropValue userIDVal;
87 #if USE_DISCARD_PROPERTY
88 const char *sh = "sh";
91 SmPropValue discardVal[3];
94 #ifdef USE_EXT_INIT_WIN
97 SmPropValue restartVal[32];
99 SmPropValue priorityVal;
107 char bufs[32], bufm[32];
109 if (EDebug(EDBUG_TYPE_SESSION))
110 Eprintf("set_save_props\n");
112 programProp.name = (char *)SmProgram;
113 programProp.type = (char *)SmARRAY8;
114 programProp.num_vals = 1;
115 programProp.vals = &programVal;
117 userIDProp.name = (char *)SmUserID;
118 userIDProp.type = (char *)SmARRAY8;
119 userIDProp.num_vals = 1;
120 userIDProp.vals = &userIDVal;
122 #if USE_DISCARD_PROPERTY
123 discardProp.name = (char *)SmDiscardCommand;
124 discardProp.type = (char *)SmLISTofARRAY8;
125 discardProp.num_vals = 3;
126 discardProp.vals = discardVal;
129 restartProp.name = (char *)SmRestartCommand;
130 restartProp.type = (char *)SmLISTofARRAY8;
131 restartProp.vals = restartVal;
133 cloneProp.name = (char *)SmCloneCommand;
134 cloneProp.type = (char *)SmLISTofARRAY8;
135 cloneProp.vals = restartVal;
137 styleProp.name = (char *)SmRestartStyleHint;
138 styleProp.type = (char *)SmCARD8;
139 styleProp.num_vals = 1;
140 styleProp.vals = &styleVal;
142 priorityProp.name = (char *)"_GSM_Priority";
143 priorityProp.type = (char *)SmCARD8;
144 priorityProp.num_vals = 1;
145 priorityProp.vals = &priorityVal;
148 /* Master WM restarts immediately for a doExit("restart") */
149 style = restarting ? SmRestartImmediately : SmRestartIfRunning;
151 /* Slave WMs never restart */
152 style = SmRestartNever;
154 user = username(getuid());
155 /* The SM specs state that the SmProgram should be the argument passed
156 * to execve. Passing argv[0] is close enough. */
157 program = Mode.wm.exec_name;
159 userIDVal.length = (user) ? strlen(user) : 0;
160 userIDVal.value = user;
161 programVal.length = strlen(program);
162 programVal.value = (char *)program;
164 styleVal.value = &style;
165 priorityVal.length = 1;
166 priorityVal.value = &priority;
168 #if USE_DISCARD_PROPERTY
169 /* Tell session manager how to clean up our old data */
170 sm_file = EGetSavePrefix();
171 Esnprintf(buf, sizeof(buf), "rm %s*.clients.*", sm_file);
173 discardVal[0].length = strlen(sh);
174 discardVal[0].value = sh;
175 discardVal[1].length = strlen(c);
176 discardVal[1].value = c;
177 discardVal[2].length = strlen(buf);
178 discardVal[2].value = buf; /* ??? Also used in restartVal ??? */
182 restartVal[n++].value = (char *)program;
185 Esnprintf(bufs, sizeof(bufs), "%i", Mode.wm.master_screen);
186 restartVal[n++].value = (char *)"-s";
187 restartVal[n++].value = (char *)bufs;
189 else if (restarting && !Mode.wm.master)
191 Esnprintf(bufm, sizeof(bufm), "%i", Mode.wm.master_screen);
192 restartVal[n++].value = (char *)"-m";
193 restartVal[n++].value = bufm;
195 #ifdef USE_EXT_INIT_WIN
198 Esnprintf(bufx, sizeof(bufx), "%#lx", new_init_win_ext);
199 restartVal[n++].value = (char *)"-X";
200 restartVal[n++].value = bufx;
204 restartVal[n++].value = (char *)smfile;
205 restartVal[n++].value = (char *)sm_file;
210 restartVal[n++].value = (char *)"-p";
211 restartVal[n++].value = (char *)s;
216 restartVal[n++].value = (char *)"-P";
217 restartVal[n++].value = (char *)s;
219 s = Mode.conf.cache_dir;
222 restartVal[n++].value = (char *)"-Q";
223 restartVal[n++].value = (char *)s;
226 restartVal[n++].value = (char *)"-S";
227 restartVal[n++].value = (char *)s;
229 for (i = 0; i < n; i++)
230 restartVal[i].length = strlen((const char *)restartVal[i].value);
232 restartProp.num_vals = n;
234 /* SM specs require SmCloneCommand excludes "--sm-client-id" option */
235 cloneProp.num_vals = restartProp.num_vals - 2;
237 if (EDebug(EDBUG_TYPE_SESSION))
238 for (i = 0; i < restartProp.num_vals; i++)
239 Eprintf("restartVal[i]: %2d: %s\n", restartVal[i].length,
240 (char *)restartVal[i].value);
243 props[n++] = &programProp;
244 props[n++] = &userIDProp;
245 #if USE_DISCARD_PROPERTY
246 props[n++] = &discardProp;
248 props[n++] = &restartProp;
249 props[n++] = &cloneProp;
250 props[n++] = &styleProp;
251 props[n++] = &priorityProp;
253 SmcSetProperties(smc_conn, n, props);
257 /* This function is usually exclusively devoted to saving data.
258 * However, E sometimes wants to save state and exit immediately afterwards
259 * so that the SM will restart it in a different theme. Therefore, we include
260 * a suicide clause at the end.
263 callback_save_yourself2(SmcConn smc_conn, SmPointer client_data __UNUSED__)
265 if (EDebug(EDBUG_TYPE_SESSION))
266 Eprintf("callback_save_yourself2\n");
268 set_save_props(smc_conn, Mode.wm.master);
269 SmcSaveYourselfDone(smc_conn, True);
275 callback_save_yourself(SmcConn smc_conn, SmPointer client_data __UNUSED__,
276 int save_style __UNUSED__, Bool shutdown __UNUSED__,
277 int interact_style __UNUSED__, Bool fast __UNUSED__)
279 if (EDebug(EDBUG_TYPE_SESSION))
280 Eprintf("callback_save_yourself\n");
282 SmcRequestSaveYourselfPhase2(smc_conn, callback_save_yourself2, NULL);
286 callback_die(SmcConn smc_conn __UNUSED__, SmPointer client_data __UNUSED__)
288 if (EDebug(EDBUG_TYPE_SESSION))
289 Eprintf("callback_die\n");
291 SessionExit(EEXIT_EXIT, NULL);
295 callback_save_complete(SmcConn smc_conn __UNUSED__,
296 SmPointer client_data __UNUSED__)
298 if (EDebug(EDBUG_TYPE_SESSION))
299 Eprintf("callback_save_complete\n");
303 callback_shutdown_cancelled(SmcConn smc_conn, SmPointer client_data __UNUSED__)
305 if (EDebug(EDBUG_TYPE_SESSION))
306 Eprintf("callback_shutdown_cancelled\n");
308 SmcSaveYourselfDone(smc_conn, False);
312 static Atom atom_sm_client_id;
315 static IceConn ice_conn;
318 ice_io_error_handler(IceConn connection __UNUSED__)
320 if (EDebug(EDBUG_TYPE_SESSION))
321 Eprintf("ice_io_error_handler\n");
323 /* The less we do here the better - the default handler does an
324 * exit(1) instead of closing the losing connection. */
330 SmcCloseConnection(sm_conn, 0, NULL);
332 EventFdUnregister(sm_efd);
336 ice_msgs_process(void)
338 IceProcessMessagesStatus status;
340 status = IceProcessMessages(ice_conn, NULL, NULL);
341 if (status == IceProcessMessagesIOError)
343 /* Less of the hope.... E survives */
344 Alert(_("ERROR!\n" "\n"
345 "Lost the Session Manager that was there?\n"
346 "Here here session manager... come here... want a bone?\n"
347 "Oh come now! Stop sulking! Bugger. Oh well. "
348 "Will continue without\n" "a session manager.\n" "\n"
349 "I'll survive somehow.\n" "\n" "\n" "... I hope.\n"));
357 static SmPointer context;
358 SmcCallbacks callbacks;
359 char error_string_ret[4096];
362 SmPropValue styleVal;
367 if (!getenv("SESSION_MANAGER"))
370 IceSetIOErrorHandler(ice_io_error_handler);
372 callbacks.save_yourself.callback = callback_save_yourself;
373 callbacks.die.callback = callback_die;
374 callbacks.save_complete.callback = callback_save_complete;
375 callbacks.shutdown_cancelled.callback = callback_shutdown_cancelled;
377 callbacks.save_yourself.client_data = callbacks.die.client_data =
378 callbacks.save_complete.client_data =
379 callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
381 client_id = Estrdup(sm_client_id);
383 error_string_ret[0] = '\0';
386 SmcOpenConnection(NULL, &context, SmProtoMajor, SmProtoMinor,
387 SmcSaveYourselfProcMask | SmcDieProcMask |
388 SmcSaveCompleteProcMask |
389 SmcShutdownCancelledProcMask, &callbacks,
390 client_id, &sm_client_id, 4096, error_string_ret);
393 if (error_string_ret[0])
394 Eprintf("While connecting to session manager: %s.", error_string_ret);
399 style[0] = SmRestartIfRunning;
403 styleVal.value = style;
405 styleProp.name = (char *)SmRestartStyleHint;
406 styleProp.type = (char *)SmCARD8;
407 styleProp.num_vals = 1;
408 styleProp.vals = &styleVal;
410 props[0] = &styleProp;
412 ice_conn = SmcGetIceConnection(sm_conn);
413 sm_fd = IceConnectionNumber(ice_conn);
414 /* Just in case we are a copy of E created by a doExit("restart") */
415 SmcSetProperties(sm_conn, 1, props);
416 fcntl(sm_fd, F_SETFD, fcntl(sm_fd, F_GETFD, 0) | FD_CLOEXEC);
418 sm_efd = EventFdRegister(sm_fd, ice_msgs_process);
430 atom_sm_client_id = EInternAtom("SM_CLIENT_ID");
437 if (!Conf.session.script)
438 Conf.session.script = Estrdup("$EROOT/scripts/session.sh");
439 if (!Conf.session.cmd_reboot)
440 Conf.session.cmd_reboot = Estrdup("reboot");
441 if (!Conf.session.cmd_halt)
442 Conf.session.cmd_halt = Estrdup("poweroff");
446 SessionGetInfo(EWin * ewin __UNUSED__)
450 if (atom_sm_client_id == None)
452 _EFREE(ewin->session_id);
453 if (ewin->icccm.client_leader != None)
455 ecore_x_window_prop_string_get(ewin->icccm.client_leader,
464 SetSMID(const char *smid)
467 sm_client_id = Estrdup(smid);
474 SessionSave(int shutdown)
476 if (EDebug(EDBUG_TYPE_SESSION))
477 Eprintf("SessionSave(%d)\n", shutdown);
479 SnapshotsSaveReal(NULL);
482 if (shutdown && sm_conn)
488 * Normally, the SM will throw away all the session data for a client
489 * that breaks its connection unexpectedly. In order to avoid this we
490 * have to let the SM handle the restart (by setting a SmRestartStyleHint
491 * of SmRestartImmediately). Rather than forcing all SM clients to do a
492 * checkpoint (which would be a bit cleaner) we just save our own state
493 * and then restore it on restart. We grab X input via the ext_init_win
494 * so the our clients remain frozen while we are down.
496 __NORETURN__ static void
497 doSMExit(int mode, const char *params)
503 if (EDebug(EDBUG_TYPE_SESSION))
504 Eprintf("doSMExit: mode=%d prm=%p\n", mode, params);
510 if (mode != EEXIT_THEME && mode != EEXIT_RESTART)
511 SessionHelper(ESESSION_STOP);
517 /* We may get here from HandleXIOError */
519 ESelectInput(VROOT, 0);
523 /* Forget about cleaning up if no disp */
524 ModulesSignal(ESIGNAL_EXIT, NULL);
531 SoundPlay(SOUND_EXIT);
534 Esnprintf(s, sizeof(s), "exec %s", params);
535 if (EDebug(EDBUG_TYPE_SESSION))
536 Eprintf("doSMExit: %s\n", s);
537 execl(DEFAULT_SH_PATH, DEFAULT_SH_PATH, "-c", s, NULL);
543 SoundPlay(SOUND_WAIT);
544 #ifdef USE_EXT_INIT_WIN
545 if (disp && !Mode.wm.window)
546 new_init_win_ext = ExtInitWinCreate();
551 l += Esnprintf(s + l, sizeof(s) - l, "exec %s -f", Mode.wm.exec_name);
553 l += Esnprintf(s + l, sizeof(s) - l, " -s %d", Dpy.screen);
554 else if (!Mode.wm.master)
556 Esnprintf(s + l, sizeof(s) - l, " -m %d", Mode.wm.master_screen);
558 l += Esnprintf(s + l, sizeof(s) - l, " -w %dx%d",
559 WinGetW(VROOT), WinGetH(VROOT));
562 l += Esnprintf(s + l, sizeof(s) - l, " -S %s", sm_client_id);
564 #ifdef USE_EXT_INIT_WIN
565 if (new_init_win_ext != None)
566 l += Esnprintf(s + l, sizeof(s) - l, " -X %#lx", new_init_win_ext);
569 l += Esnprintf(s + l, sizeof(s) - l, " -t %s", ss);
571 if (EDebug(EDBUG_TYPE_SESSION))
572 Eprintf("doSMExit: %s\n", s);
574 execl(DEFAULT_SH_PATH, DEFAULT_SH_PATH, "-c", s, NULL);
579 SoundPlay(SOUND_EXIT);
589 SmcRequestSaveYourself(sm_conn, SmSaveBoth, True, SmInteractStyleAny,
595 SessionExit(EEXIT_EXIT, NULL);
601 LogoutCB(Dialog * d, int val, void *data __UNUSED__)
611 /* 0:LogOut -: No -or- */
612 /* 0:Halt 1:Reboot 2:LogOut -:No */
618 SessionExit(EEXIT_EXIT, NULL);
621 SessionExit(EEXIT_EXEC, Conf.session.cmd_reboot);
624 SessionExit(EEXIT_EXEC, Conf.session.cmd_halt);
633 SessionLogoutConfirm(void)
638 d = DialogFind("LOGOUT_DIALOG");
641 SoundPlay(SOUND_LOGOUT);
642 d = DialogCreate("LOGOUT_DIALOG");
643 table = DialogInitItem(d);
644 DialogSetTitle(d, _("Are you sure?"));
645 di = DialogAddItem(table, DITEM_TEXT);
646 DialogItemSetText(di, _("\n\n"
647 " Are you sure you wish to log out ? \n"
649 table = DialogAddItem(table, DITEM_TABLE);
650 DialogItemSetAlign(table, 512, 0);
651 DialogItemSetFill(table, 0, 0);
652 DialogItemTableSetOptions(table, 2, 0, 1, 0);
653 if (Conf.session.enable_reboot_halt)
655 DialogItemTableSetOptions(table, 4, 0, 1, 0);
656 DialogItemAddButton(table, _(" Yes, Shut Down "), LogoutCB, 3,
658 DialogItemAddButton(table, _(" Yes, Reboot "), LogoutCB, 2,
661 DialogItemAddButton(table, _(" Yes, Log Out "), LogoutCB, 1,
663 DialogItemAddButton(table, _(" No "), NULL, 0, 1, DLG_BUTTON_CANCEL);
664 DialogBindKey(d, "Escape", DialogCallbackClose, 0, NULL);
665 DialogBindKey(d, "Return", LogoutCB, 1, NULL);
668 DialogShowCentered(d);
670 #endif /* ENABLE_DIALOGS */
673 SessionExit(int mode, const char *param)
675 /* We do not want to be exited by children. */
676 if (getpid() != Mode.wm.pid)
679 if (EDebug(EDBUG_TYPE_SESSION))
680 Eprintf("SessionExit: mode=%d(%d) prm=%s\n", mode, Mode.wm.exit_mode,
686 if (Mode.wm.startup || Mode.wm.exit_now)
692 /* In event loop - Set exit mode */
693 Mode.wm.exit_mode = mode;
694 Mode.wm.exit_param = Estrdup(param);
698 mode = Mode.wm.exit_mode;
699 param = Mode.wm.exit_param;
703 if (!Mode.wm.exiting)
705 /* This may be possible during nested signal handling */
706 Eprintf("SessionExit already in progress ... now exiting\n");
712 if (Conf.session.enable_logout_dialog)
713 SessionLogoutConfirm();
722 doSMExit(mode, param);
726 SessionRunProg(const char *prog, const char *params)
733 Esnprintf(buf, sizeof(buf), "%s %s", prog, params);
740 if (EDebug(EDBUG_TYPE_SESSION))
741 Eprintf("SessionRunProg: %s\n", s);
746 SessionHelper(int when)
751 if (Conf.session.enable_script && Conf.session.script)
752 SessionRunProg(Conf.session.script, "init");
755 if (Conf.session.enable_script && Conf.session.script)
756 SessionRunProg(Conf.session.script, "start");
759 if (Conf.session.enable_script && Conf.session.script)
760 SessionRunProg(Conf.session.script, "stop");
769 static char tmp_session_script;
770 static char tmp_logout_dialog;
771 static char tmp_reboot_halt;
774 CB_ConfigureSession(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
778 Conf.session.enable_script = tmp_session_script;
779 Conf.session.enable_logout_dialog = tmp_logout_dialog;
780 Conf.session.enable_reboot_halt = tmp_reboot_halt;
786 _DlgFillSession(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
790 tmp_session_script = Conf.session.enable_script;
791 tmp_logout_dialog = Conf.session.enable_logout_dialog;
792 tmp_reboot_halt = Conf.session.enable_reboot_halt;
794 DialogItemTableSetOptions(table, 2, 0, 0, 0);
796 di = DialogAddItem(table, DITEM_CHECKBUTTON);
797 DialogItemSetColSpan(di, 2);
798 DialogItemSetText(di, _("Enable Session Script"));
799 DialogItemCheckButtonSetPtr(di, &tmp_session_script);
801 di = DialogAddItem(table, DITEM_CHECKBUTTON);
802 DialogItemSetColSpan(di, 2);
803 DialogItemSetText(di, _("Enable Logout Dialog"));
804 DialogItemCheckButtonSetPtr(di, &tmp_logout_dialog);
806 di = DialogAddItem(table, DITEM_CHECKBUTTON);
807 DialogItemSetColSpan(di, 2);
808 DialogItemSetText(di, _("Enable Reboot/Halt on Logout"));
809 DialogItemCheckButtonSetPtr(di, &tmp_reboot_halt);
812 const DialogDef DlgSession = {
815 N_("Session Settings"),
816 SOUND_SETTINGS_SESSION,
817 "pix/miscellaneous.png",
818 N_("Enlightenment Session\n" "Settings Dialog\n"),
820 DLG_OAC, CB_ConfigureSession,
822 #endif /* ENABLE_DIALOGS */