/*
 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
 * Copyright (C) 2004-2024 Kim Woelders
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "E.h"
#include "dialog.h"
#include "emodule.h"
#include "events.h"
#include "ewins.h"
#include "session.h"
#include "settings.h"
#include "snaps.h"
#include "user.h"
#include "xwin.h"
#if USE_DBUS
#include "edbus.h"
#endif

#ifdef USE_EXT_INIT_WIN
static EX_Window new_init_win_ext = NoXID;
#endif

/* True if we are saving state for a doExit("restart") */
static char     restarting = 0;

#if USE_SM

#include <fcntl.h>
#include <X11/SM/SMlib.h>

/*
 * NB! If the discard property is revived, the dual use of buf must be fixed.
 */
#define USE_DISCARD_PROPERTY 0

static SmcConn  sm_conn = NULL;

static int      sm_efd = 0;

static void
set_save_props(SmcConn smc_conn, int master_flag)
{
    const char     *s;
    const char     *user;
    const char     *program;
    char            priority = 10;
    char            style;
    int             i, n;
    SmPropValue     programVal;
    SmPropValue     userIDVal;

#if USE_DISCARD_PROPERTY
    const char     *sh = "sh";
    const char     *c = "-c";
    const char     *sm_file;
    SmPropValue     discardVal[3];
    SmProp          discardProp;
#endif
#ifdef USE_EXT_INIT_WIN
    char            bufx[32];
#endif
    SmPropValue     restartVal[32];
    SmPropValue     styleVal;
    SmPropValue     priorityVal;
    SmProp          programProp;
    SmProp          userIDProp;
    SmProp          restartProp;
    SmProp          cloneProp;
    SmProp          styleProp;
    SmProp          priorityProp;
    SmProp         *props[7];
    char            bufs[32], bufm[32];

    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);

    programProp.name = (char *)SmProgram;
    programProp.type = (char *)SmARRAY8;
    programProp.num_vals = 1;
    programProp.vals = &programVal;

    userIDProp.name = (char *)SmUserID;
    userIDProp.type = (char *)SmARRAY8;
    userIDProp.num_vals = 1;
    userIDProp.vals = &userIDVal;

#if USE_DISCARD_PROPERTY
    discardProp.name = (char *)SmDiscardCommand;
    discardProp.type = (char *)SmLISTofARRAY8;
    discardProp.num_vals = 3;
    discardProp.vals = discardVal;
#endif

    restartProp.name = (char *)SmRestartCommand;
    restartProp.type = (char *)SmLISTofARRAY8;
    restartProp.vals = restartVal;

    cloneProp.name = (char *)SmCloneCommand;
    cloneProp.type = (char *)SmLISTofARRAY8;
    cloneProp.vals = restartVal;

    styleProp.name = (char *)SmRestartStyleHint;
    styleProp.type = (char *)SmCARD8;
    styleProp.num_vals = 1;
    styleProp.vals = &styleVal;

    priorityProp.name = (char *)"_GSM_Priority";
    priorityProp.type = (char *)SmCARD8;
    priorityProp.num_vals = 1;
    priorityProp.vals = &priorityVal;

    if (master_flag)
        /* Master WM restarts immediately for a doExit("restart") */
        style = restarting ? SmRestartImmediately : SmRestartIfRunning;
    else
        /* Slave WMs never restart */
        style = SmRestartNever;

    user = username();
    /* The SM specs state that the SmProgram should be the argument passed
     * to execve. Passing argv[0] is close enough. */
    program = Mode.wm.exec_name;

    userIDVal.length = (user) ? strlen(user) : 0;
    userIDVal.value = (char *)user;
    programVal.length = strlen(program);
    programVal.value = (char *)program;
    styleVal.length = 1;
    styleVal.value = &style;
    priorityVal.length = 1;
    priorityVal.value = &priority;

#if USE_DISCARD_PROPERTY
    /* Tell session manager how to clean up our old data */
    sm_file = EGetSavePrefix();
    Esnprintf(buf, sizeof(buf), "rm %s*.clients.*", sm_file);

    discardVal[0].length = strlen(sh);
    discardVal[0].value = sh;
    discardVal[1].length = strlen(c);
    discardVal[1].value = c;
    discardVal[2].length = strlen(buf);
    discardVal[2].value = buf;  /* ??? Also used in restartVal ??? */
#endif

    n = 0;
    restartVal[n++].value = (char *)program;
    if (Mode.wm.single)
    {
        Esnprintf(bufs, sizeof(bufs), "%i", Mode.wm.master_screen);
        restartVal[n++].value = (char *)"-s";
        restartVal[n++].value = (char *)bufs;
    }
    else if (restarting && !Mode.wm.master)
    {
        Esnprintf(bufm, sizeof(bufm), "%i", Mode.wm.master_screen);
        restartVal[n++].value = (char *)"-m";
        restartVal[n++].value = bufm;
    }
#ifdef USE_EXT_INIT_WIN
    if (restarting)
    {
        Esnprintf(bufx, sizeof(bufx), "%#x", new_init_win_ext);
        restartVal[n++].value = (char *)"-X";
        restartVal[n++].value = bufx;
    }
#endif
#if 0
    restartVal[n++].value = (char *)smfile;
    restartVal[n++].value = (char *)sm_file;
#endif
    s = Mode.conf.name;
    if (s)
    {
        restartVal[n++].value = (char *)"-p";
        restartVal[n++].value = (char *)s;
    }
    s = EDirUserConf();
    if (s)
    {
        restartVal[n++].value = (char *)"-P";
        restartVal[n++].value = (char *)s;
    }
    s = EDirUserCache();
    if (s)
    {
        restartVal[n++].value = (char *)"-Q";
        restartVal[n++].value = (char *)s;
    }
    s = Mode.session.sm_client_id;
    restartVal[n++].value = (char *)"-S";
    restartVal[n++].value = (char *)s;

    for (i = 0; i < n; i++)
        restartVal[i].length = strlen((const char *)restartVal[i].value);

    restartProp.num_vals = n;

    /* SM specs require SmCloneCommand excludes "--sm-client-id" option */
    cloneProp.num_vals = restartProp.num_vals - 2;

    if (EDebug(EDBUG_TYPE_SESSION))
        for (i = 0; i < restartProp.num_vals; i++)
            Eprintf("restartVal[i]: %2d: %s\n", restartVal[i].length,
                    (char *)restartVal[i].value);

    n = 0;
    props[n++] = &programProp;
    props[n++] = &userIDProp;
#if USE_DISCARD_PROPERTY
    props[n++] = &discardProp;
#endif
    props[n++] = &restartProp;
    props[n++] = &cloneProp;
    props[n++] = &styleProp;
    props[n++] = &priorityProp;

    SmcSetProperties(smc_conn, n, props);
}

/* This function is usually exclusively devoted to saving data.
 * However, E sometimes wants to save state and exit immediately afterwards
 * so that the SM will restart it in a different theme. Therefore, we include
 * a suicide clause at the end.
 */
static void
callback_save_yourself2(SmcConn smc_conn, SmPointer client_data __UNUSED__)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);

    set_save_props(smc_conn, Mode.wm.master);
    SmcSaveYourselfDone(smc_conn, True);
    if (restarting)
        EExit(0);
}

static void
callback_save_yourself(SmcConn smc_conn, SmPointer client_data __UNUSED__,
                       int save_style __UNUSED__, Bool shutdown __UNUSED__,
                       int interact_style __UNUSED__, Bool fast __UNUSED__)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);

    SmcRequestSaveYourselfPhase2(smc_conn, callback_save_yourself2, NULL);
}

static void
callback_die(SmcConn smc_conn __UNUSED__, SmPointer client_data __UNUSED__)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);

    SessionExit(EEXIT_EXIT, NULL);
}

static void
callback_save_complete(SmcConn smc_conn __UNUSED__,
                       SmPointer client_data __UNUSED__)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);
}

static void
callback_shutdown_cancelled(SmcConn smc_conn, SmPointer client_data __UNUSED__)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);

    SmcSaveYourselfDone(smc_conn, False);
}

static IceConn  ice_conn;

static void
ice_io_error_handler(IceConn connection __UNUSED__)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s\n", __func__);

    /* The less we do here the better - the default handler does an
     * exit(1) instead of closing the losing connection. */
}

static void
ice_exit(void)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: smc=%p\n", __func__, sm_conn);

    SmcCloseConnection(sm_conn, 0, NULL);
    sm_conn = NULL;
    EventFdUnregister(sm_efd);
}

static void
ice_msgs_process(void)
{
    IceProcessMessagesStatus status;

    status = IceProcessMessages(ice_conn, NULL, NULL);
    if (status == IceProcessMessagesIOError)
    {
        /* Less of the hope.... E survives */
        Alert(_("ERROR!\n" "\n"
                "Lost the Session Manager that was there?\n"
                "Here here session manager... come here... want a bone?\n"
                "Oh come now! Stop sulking! Bugger. Oh well. "
                "Will continue without\n" "a session manager.\n" "\n"
                "I'll survive somehow.\n" "\n" "\n" "... I hope.\n"));
        ice_exit();
    }
}

static void
ice_init(void)
{
    static SmPointer context;
    SmcCallbacks    callbacks;
    char            error_string_ret[4096];
    char           *client_id;
    char            style[2];
    SmPropValue     styleVal;
    SmProp          styleProp;
    SmProp         *props[1];
    int             sm_fd;

    if (!getenv("SESSION_MANAGER"))
        goto done;

    IceSetIOErrorHandler(ice_io_error_handler);

    callbacks.save_yourself.callback = callback_save_yourself;
    callbacks.die.callback = callback_die;
    callbacks.save_complete.callback = callback_save_complete;
    callbacks.shutdown_cancelled.callback = callback_shutdown_cancelled;

    callbacks.save_yourself.client_data = callbacks.die.client_data =
        callbacks.save_complete.client_data =
        callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;

    error_string_ret[0] = '\0';

    sm_conn =
        SmcOpenConnection(NULL, &context, SmProtoMajor, SmProtoMinor,
                          SmcSaveYourselfProcMask | SmcDieProcMask |
                          SmcSaveCompleteProcMask |
                          SmcShutdownCancelledProcMask, &callbacks,
                          Mode.session.sm_client_id, &client_id,
                          4096, error_string_ret);

    EFREE_SET(Mode.session.sm_client_id, client_id);

    if (error_string_ret[0])
        Eprintf("While connecting to session manager: %s\n", error_string_ret);

    if (!sm_conn)
        goto done;

    style[0] = SmRestartIfRunning;
    style[1] = 0;

    styleVal.length = 1;
    styleVal.value = style;

    styleProp.name = (char *)SmRestartStyleHint;
    styleProp.type = (char *)SmCARD8;
    styleProp.num_vals = 1;
    styleProp.vals = &styleVal;

    props[0] = &styleProp;

    ice_conn = SmcGetIceConnection(sm_conn);
    sm_fd = IceConnectionNumber(ice_conn);
    /* Just in case we are a copy of E created by a doExit("restart") */
    SmcSetProperties(sm_conn, 1, props);
    fcntl(sm_fd, F_SETFD, fcntl(sm_fd, F_GETFD, 0) | FD_CLOEXEC);

    sm_efd = EventFdRegister(sm_fd, ice_msgs_process);

  done:
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: smc=%p\n", __func__, sm_conn);
}

#endif                          /* USE_SM */

void
SessionInit(void)
{
    if (!Conf.session.script)
        Conf.session.script = Estrdup("$EROOT/scripts/session.sh");
    if (!Conf.session.cmd_reboot)
        Conf.session.cmd_reboot = Estrdup("reboot");
    if (!Conf.session.cmd_halt)
        Conf.session.cmd_halt = Estrdup("poweroff");

    if (Mode.wm.window)
        return;

#if USE_SM
    ice_init();
#endif
}

/*
 * Normally, the SM will throw away all the session data for a client
 * that breaks its connection unexpectedly. In order to avoid this we 
 * have to let the SM handle the restart (by setting a SmRestartStyleHint
 * of SmRestartImmediately). Rather than forcing all SM clients to do a
 * checkpoint (which would be a bit cleaner) we just save our own state
 * and then restore it on restart. We grab X input via the ext_init_win
 * so the our clients remain frozen while we are down.
 */
__NORETURN__ static void
doSMExit(int mode, const char *params)
{
    int             l;
    char            s[1024];
    const char     *ss;

    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: mode=%d prm=%p disp=%p\n", __func__, mode, params, disp);

    restarting = 1;

    SnapshotsSaveReal();

#if USE_SM
    if (sm_conn)
        ice_exit();
#endif

    if (mode != EEXIT_THEME && mode != EEXIT_RESTART)
        SessionHelper(ESESSHLP_STOP);

    LangExit();

    if (disp)
    {
        /* We may get here from HandleXIOError */
        EwinsSetFree();
        ESelectInput(VROOT, 0);
        ExtInitWinKill();
        ESync(0);
    }

    /* NB! Exit functions must not do X stuff as the connection may be lost
     * (disp = NULL) */
    ModulesSignal(ESIGNAL_EXIT, NULL);

    ss = NULL;
    switch (mode)
    {
    case EEXIT_EXEC:
        if (!params || params[0] == '\0')
            break;
        SoundPlay(SOUND_EXIT);
        EDisplayClose();

        if (EDebug(EDBUG_TYPE_SESSION))
            Eprintf("%s: exec %s\n", __func__, params);
        Eexec(params);
        break;

    case EEXIT_THEME:
        ss = params;
        /* FALLTHROUGH */
    case EEXIT_RESTART:
        SoundPlay(SOUND_WAIT);
#ifdef USE_EXT_INIT_WIN
        if (disp)
            new_init_win_ext = ExtInitWinCreate();
#endif
        EDisplayClose();

        l = 0;
        l += Esnprintf(s + l, sizeof(s) - l, "%s -f", Mode.wm.exec_name);
        if (Mode.wm.single)
            l += Esnprintf(s + l, sizeof(s) - l, " -s %d", Dpy.screen);
        else if (!Mode.wm.master)
            l += Esnprintf(s + l, sizeof(s) - l, " -m %d",
                           Mode.wm.master_screen);
        if (Mode.wm.window)
            l += Esnprintf(s + l, sizeof(s) - l, " -w %dx%d",
                           WinGetW(VROOT), WinGetH(VROOT));
#if USE_SM
        if (Mode.session.sm_client_id)
            l += Esnprintf(s + l, sizeof(s) - l, " -S %s",
                           Mode.session.sm_client_id);
#endif
#ifdef USE_EXT_INIT_WIN
        if (new_init_win_ext != NoXID)
            l += Esnprintf(s + l, sizeof(s) - l, " -X %#x", new_init_win_ext);
#endif
        if (ss)
            Esnprintf(s + l, sizeof(s) - l, " -t %s", ss);

        if (EDebug(EDBUG_TYPE_SESSION))
            Eprintf("%s: exec %s\n", __func__, s);

        Eexec(s);
        break;
    }

    restarting = 0;
    SoundPlay(SOUND_EXIT);
    EExit(0);
}

#define LOGOUT_EXIT         1
#define LOGOUT_REBOOT       2
#define LOGOUT_HALT         3
#define LOGOUT_LOCK         4
#define LOGOUT_SUSPEND      5
#define LOGOUT_HIBERNATE    6

#if USE_DBUS
static int
_SessionExitDbus(int how)
{
    if (Mode.wm.window)
        return -1;

    switch (how)
    {
    default:
    case LOGOUT_LOCK:
    case LOGOUT_SUSPEND:
    case LOGOUT_HIBERNATE:
        return -1;

    case LOGOUT_EXIT:
        return DbusRequestLogout();

    case LOGOUT_REBOOT:
    case LOGOUT_HALT:
        return DbusRequestShutdown();
    }
}
#endif                          /* USE_DBUS */

static void
_SessionLogout(int how)
{
#ifdef USE_SM
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: how=%d smc=%p\n", __func__, how, sm_conn);
#else
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: how=%d\n", __func__, how);
#endif

#if USE_DBUS
    if (_SessionExitDbus(how) == 0)
        return;
#endif

#if USE_SM
    if (sm_conn)
    {
        SmcRequestSaveYourself(sm_conn, SmSaveBoth, True, SmInteractStyleAny,
                               False, True);
        return;
    }
#endif

    switch (how)
    {
    default:
        break;
    case LOGOUT_EXIT:
        SessionExit(EEXIT_EXIT, NULL);
        break;
    case LOGOUT_REBOOT:
        SessionExit(EEXIT_EXEC, Conf.session.cmd_reboot);
        break;
    case LOGOUT_HALT:
        SessionExit(EEXIT_EXEC, Conf.session.cmd_halt);
        break;
    case LOGOUT_LOCK:
        Espawn("%s", Conf.session.cmd_lock);
        break;
    case LOGOUT_SUSPEND:
        Espawn("%s", Conf.session.cmd_suspend);
        break;
    case LOGOUT_HIBERNATE:
        Espawn("%s", Conf.session.cmd_hibernate);
        break;
    }
}

#if ENABLE_DIALOGS

static void
_SessionLogoutCB(Dialog *d, int val, void *data __UNUSED__)
{
    DialogClose(d);

    _SessionLogout(val);
}

#define ISSET(s) ((s && *s != '\0') ? 1 : 0)

static void
_SessionLogoutConfirm(void)
{
    Dialog         *d;
    DItem          *table, *di;
    int             tcols, ncols;

    d = DialogFind("LOGOUT_DIALOG");
    if (!d)
    {
        SoundPlay(SOUND_LOGOUT);

        d = DialogCreate("LOGOUT_DIALOG");
        table = DialogInitItem(d);
        DialogSetTitle(d, _("Are you sure?"));

        di = DialogAddItem(table, DITEM_TEXT);
        DialogItemSetText(di, _("Are you sure you wish to log out ?"));
        DialogItemSetAlign(di, 512, 0);

        table = DialogAddItem(table, DITEM_TABLE);
        DialogItemSetAlign(table, 512, 0);
        DialogItemSetFill(table, 0, 0);

        tcols = ISSET(Conf.session.cmd_lock) +
            ISSET(Conf.session.cmd_suspend) + ISSET(Conf.session.cmd_hibernate);
        if (Conf.session.enable_reboot_halt || tcols >= 3)
            ncols = 3;
        else if (tcols >= 2)
            ncols = 2;
        else
            ncols = 1;
        DialogItemTableSetOptions(table, ncols, 0, 1, 0);

        if (tcols > 0)
        {
            tcols = 0;
            if (ISSET(Conf.session.cmd_hibernate))
            {
                tcols += 1;
                DialogItemAddButton(table, _("Hibernate"), _SessionLogoutCB,
                                    LOGOUT_HIBERNATE, 1, DLG_BUTTON_OK);
            }
            if (ISSET(Conf.session.cmd_suspend))
            {
                tcols += 1;
                DialogItemAddButton(table, _("Suspend"), _SessionLogoutCB,
                                    LOGOUT_SUSPEND, 1, DLG_BUTTON_OK);
            }
            if (ISSET(Conf.session.cmd_lock))
            {
                tcols += 1;
                DialogItemAddButton(table, _("Lock"), _SessionLogoutCB,
                                    LOGOUT_LOCK, 1, DLG_BUTTON_OK);
            }
            for (; tcols < ncols; tcols++)
                DialogAddItem(table, DITEM_NONE);
        }

        tcols = 0;
        if (Conf.session.enable_reboot_halt)
        {
            tcols += 2;
            DialogItemAddButton(table, _("Yes, Shut Down"), _SessionLogoutCB,
                                LOGOUT_HALT, 1, DLG_BUTTON_OK);
            DialogItemAddButton(table, _("Yes, Reboot"), _SessionLogoutCB,
                                LOGOUT_REBOOT, 1, DLG_BUTTON_OK);
        }
        tcols += 1;
        DialogItemAddButton(table, _("Yes, Log Out"), _SessionLogoutCB,
                            LOGOUT_EXIT, 1, DLG_BUTTON_OK);
        for (; tcols < ncols; tcols++)
            DialogAddItem(table, DITEM_NONE);

        di = DialogItemAddButton(table, _("No"), NULL, 0, 1, DLG_BUTTON_CANCEL);
        DialogItemSetColSpan(di, ncols);

        DialogBindKey(d, "Escape", DialogCallbackClose, 0, NULL);
        DialogBindKey(d, "Return", _SessionLogoutCB, LOGOUT_EXIT, NULL);
    }

    DialogShowCentered(d);
}
#endif                          /* ENABLE_DIALOGS */

void
SessionExit(int mode, const char *param)
{
    /* We do not want to be exited by children. */
    if (getpid() != Mode.wm.pid)
        return;

    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: mode=%d(%d) prm=%s\n", __func__, mode, Mode.wm.exit_mode,
                param ? param : "(none)");

    if (Mode.wm.exiting)
        return;

    if (Mode.wm.startup || Mode.wm.exit_now)
        goto done;

    switch (mode)
    {
    default:
        /* In event loop - Set exit mode */
        Mode.wm.exit_mode = mode;
        Mode.wm.exit_param = Estrdup(param);
        return;

    case EEXIT_QUIT:
        mode = Mode.wm.exit_mode;
        param = Mode.wm.exit_param;
        break;

    case EEXIT_ERROR:
        if (!Mode.wm.exiting)
            break;
        /* This may be possible during nested signal handling */
        Eprintf("%s: already in progress ... now exiting\n", __func__);
        exit(1);
        break;
    }

  done:
    Mode.wm.exiting++;
    doSMExit(mode, param);
}

void
SessionLogout(int mode)
{
    switch (mode)
    {
    default:                   /* We should not go here - ignore */
        break;

    case ESESSION_LOGOUT:
#if ENABLE_DIALOGS
        if (Conf.session.enable_logout_dialog)
            _SessionLogoutConfirm();
        else
#endif
            _SessionLogout(LOGOUT_EXIT);
        break;

    case ESESSION_SHUTDOWN:
#if ENABLE_DIALOGS
        if (Conf.session.enable_logout_dialog)
            _SessionLogoutConfirm();
        else
#endif
            _SessionLogout(LOGOUT_HALT);
        break;
    }
}

static void
_SessionRunProg(const char *prog, const char *params)
{
    if (EDebug(EDBUG_TYPE_SESSION))
        Eprintf("%s: %s %s\n", __func__, prog, params);
    Esystem("%s %s", prog, params);
}

void
SessionHelper(int when)
{
    switch (when)
    {
    case ESESSHLP_INIT:
        if (Conf.session.enable_script && Conf.session.script)
            _SessionRunProg(Conf.session.script, "init");
        break;
    case ESESSHLP_START:
        if (Conf.session.enable_script && Conf.session.script)
            _SessionRunProg(Conf.session.script, "start");
        break;
    case ESESSHLP_STOP:
        if (Conf.session.enable_script && Conf.session.script)
            _SessionRunProg(Conf.session.script, "stop");
        break;
    }
}

#if ENABLE_DIALOGS
/*
 * Session dialog
 */
static char     tmp_session_script;
static char     tmp_logout_dialog;
static char     tmp_reboot_halt;

static void
_DlgApplySession(Dialog *d __UNUSED__, int val __UNUSED__,
                 void *data __UNUSED__)
{
    Conf.session.enable_script = tmp_session_script;
    Conf.session.enable_logout_dialog = tmp_logout_dialog;
    Conf.session.enable_reboot_halt = tmp_reboot_halt;
    autosave();
}

static void
_DlgFillSession(Dialog *d __UNUSED__, DItem *table, void *data __UNUSED__)
{
    DItem          *di;

    tmp_session_script = Conf.session.enable_script;
    tmp_logout_dialog = Conf.session.enable_logout_dialog;
    tmp_reboot_halt = Conf.session.enable_reboot_halt;

    DialogItemTableSetOptions(table, 2, 0, 0, 0);

    di = DialogAddItem(table, DITEM_CHECKBUTTON);
    DialogItemSetColSpan(di, 2);
    DialogItemSetText(di, _("Enable session script"));
    DialogItemCheckButtonSetPtr(di, &tmp_session_script);

    di = DialogAddItem(table, DITEM_CHECKBUTTON);
    DialogItemSetColSpan(di, 2);
    DialogItemSetText(di, _("Enable logout dialog"));
    DialogItemCheckButtonSetPtr(di, &tmp_logout_dialog);

    di = DialogAddItem(table, DITEM_CHECKBUTTON);
    DialogItemSetColSpan(di, 2);
    DialogItemSetText(di, _("Enable reboot/halt on logout"));
    DialogItemCheckButtonSetPtr(di, &tmp_reboot_halt);
}

const DialogDef DlgSession = {
    "CONFIGURE_SESSION",
    N_("Session"), N_("Session Settings"),
    0,
    SOUND_SETTINGS_SESSION,
    "pix/miscellaneous.png",
    N_("Enlightenment Session\n" "Settings Dialog"),
    _DlgFillSession,
    DLG_OAC, _DlgApplySession, NULL
};
#endif                          /* ENABLE_DIALOGS */
