#include <config.h>
#ifdef HAVE_X_SM
#include <X11/SM/SMlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <sys/param.h>
#include <stdio.h>
#include "lisp.h"
#include "systime.h"
#include "sysselect.h"
#include "termhooks.h"
#include "termopts.h"
#include "xterm.h"
extern Lisp_Object Vuser_login_name;
static struct input_event emacs_event;
static int ice_fd = -1;
static int doing_interact = False;
static SmcConn smc_conn;
static char *client_id;
static char *emacs_program;
Lisp_Object Vx_session_id;
Lisp_Object Vx_session_previous_id;
#define SMID_OPT "--smid="
#define NOSPLASH_OPT "--no-splash"
int
x_session_check_input (bufp)
struct input_event *bufp;
{
SELECT_TYPE read_fds;
EMACS_TIME tmout;
if (ice_fd == -1) return 0;
FD_ZERO (&read_fds);
FD_SET (ice_fd, &read_fds);
tmout.tv_sec = 0;
tmout.tv_usec = 0;
emacs_event.kind = NO_EVENT;
if (select (ice_fd+1, &read_fds,
(SELECT_TYPE *)0, (SELECT_TYPE *)0, &tmout) < 0)
{
ice_fd = -1;
return 0;
}
if (FD_ISSET (ice_fd, &read_fds))
IceProcessMessages (SmcGetIceConnection (smc_conn),
(IceReplyWaitInfo *)0, (Bool *)0);
if (emacs_event.kind == NO_EVENT)
return 0;
bcopy (&emacs_event, bufp, sizeof (struct input_event));
return 1;
}
int
x_session_have_connection ()
{
return ice_fd != -1;
}
static void
smc_interact_CB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
doing_interact = True;
emacs_event.kind = SAVE_SESSION_EVENT;
}
static void
smc_save_yourself_CB (smcConn,
clientData,
saveType,
shutdown,
interactStyle,
fast)
SmcConn smcConn;
SmPointer clientData;
int saveType;
Bool shutdown;
int interactStyle;
Bool fast;
{
#define NR_PROPS 5
SmProp *props[NR_PROPS];
SmProp prop_ptr[NR_PROPS];
SmPropValue values[20];
int val_idx = 0;
int props_idx = 0;
char *cwd = NULL;
char *smid_opt;
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmCloneCommand;
props[props_idx]->type = SmLISTofARRAY8;
props[props_idx]->num_vals = 1;
props[props_idx]->vals = &values[val_idx++];
props[props_idx]->vals[0].length = strlen (emacs_program);
props[props_idx]->vals[0].value = emacs_program;
++props_idx;
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmProgram;
props[props_idx]->type = SmARRAY8;
props[props_idx]->num_vals = 1;
props[props_idx]->vals = &values[val_idx++];
props[props_idx]->vals[0].length = strlen (SDATA (Vinvocation_name));
props[props_idx]->vals[0].value = SDATA (Vinvocation_name);
++props_idx;
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmRestartCommand;
props[props_idx]->type = SmLISTofARRAY8;
props[props_idx]->num_vals = 3;
props[props_idx]->vals = &values[val_idx];
props[props_idx]->vals[0].length = strlen (emacs_program);
props[props_idx]->vals[0].value = emacs_program;
smid_opt = xmalloc (strlen (SMID_OPT) + strlen (client_id) + 1);
strcpy (smid_opt, SMID_OPT);
strcat (smid_opt, client_id);
props[props_idx]->vals[1].length = strlen (smid_opt);
props[props_idx]->vals[1].value = smid_opt;
props[props_idx]->vals[2].length = strlen (NOSPLASH_OPT);
props[props_idx]->vals[2].value = NOSPLASH_OPT;
val_idx += 3;
++props_idx;
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmUserID;
props[props_idx]->type = SmARRAY8;
props[props_idx]->num_vals = 1;
props[props_idx]->vals = &values[val_idx++];
props[props_idx]->vals[0].length = strlen (SDATA (Vuser_login_name));
props[props_idx]->vals[0].value = SDATA (Vuser_login_name);
++props_idx;
cwd = get_current_dir_name ();
if (cwd)
{
props[props_idx] = &prop_ptr[props_idx];
props[props_idx]->name = SmCurrentDirectory;
props[props_idx]->type = SmARRAY8;
props[props_idx]->num_vals = 1;
props[props_idx]->vals = &values[val_idx++];
props[props_idx]->vals[0].length = strlen (cwd);
props[props_idx]->vals[0].value = cwd;
++props_idx;
}
SmcSetProperties (smcConn, props_idx, props);
xfree (smid_opt);
if (cwd)
free (cwd);
if (interactStyle != SmInteractStyleAny
|| ! shutdown
|| saveType == SmSaveLocal
|| ! SmcInteractRequest (smcConn, SmDialogNormal, smc_interact_CB, 0))
{
SmcSaveYourselfDone (smcConn, True);
}
}
static void
smc_die_CB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
SmcCloseConnection (smcConn, 0, 0);
ice_fd = -1;
}
static void
smc_save_complete_CB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
}
static void
smc_shutdown_cancelled_CB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
}
static void
smc_error_handler (smcConn,
swap,
offendingMinorOpcode,
offendingSequence,
errorClass,
severity,
values)
SmcConn smcConn;
Bool swap;
int offendingMinorOpcode;
unsigned long offendingSequence;
int errorClass;
int severity;
SmPointer values;
{
}
static void
ice_error_handler (iceConn,
swap,
offendingMinorOpcode,
offendingSequence,
errorClass,
severity,
values)
IceConn iceConn;
Bool swap;
int offendingMinorOpcode;
unsigned long offendingSequence;
int errorClass;
int severity;
IcePointer values;
{
}
static void
ice_io_error_handler (iceConn)
IceConn iceConn;
{
ice_fd = -1;
}
static void
ice_conn_watch_CB (iceConn, clientData, opening, watchData)
IceConn iceConn;
IcePointer clientData;
Bool opening;
IcePointer *watchData;
{
if (! opening)
{
ice_fd = -1;
return;
}
ice_fd = IceConnectionNumber (iceConn);
#ifndef F_SETOWN_BUG
#ifdef F_SETOWN
#ifdef F_SETOWN_SOCK_NEG
fcntl (ice_fd, F_SETOWN, -getpid ());
#else
fcntl (ice_fd, F_SETOWN, getpid ());
#endif
#endif
#endif
#ifdef SIGIO
if (interrupt_input)
init_sigio (ice_fd);
#endif
}
static void
create_client_leader_window (dpyinfo, client_id)
struct x_display_info *dpyinfo;
char *client_id;
{
Window w;
XClassHint class_hints;
Atom sm_id;
w = XCreateSimpleWindow (dpyinfo->display,
dpyinfo->root_window,
-1, -1, 1, 1,
CopyFromParent, CopyFromParent, CopyFromParent);
class_hints.res_name = (char *) SDATA (Vx_resource_name);
class_hints.res_class = (char *) SDATA (Vx_resource_class);
XSetClassHint (dpyinfo->display, w, &class_hints);
XStoreName (dpyinfo->display, w, class_hints.res_name);
sm_id = XInternAtom (dpyinfo->display, "SM_CLIENT_ID", False);
XChangeProperty (dpyinfo->display, w, sm_id, XA_STRING, 8, PropModeReplace,
client_id, strlen (client_id));
dpyinfo->client_leader_window = w;
}
void
x_session_initialize (dpyinfo)
struct x_display_info *dpyinfo;
{
#define SM_ERRORSTRING_LEN 512
char errorstring[SM_ERRORSTRING_LEN];
char* previous_id = NULL;
SmcCallbacks callbacks;
int name_len = 0;
if (! EQ (Vx_session_previous_id, Qnil) && STRINGP (Vx_session_previous_id))
previous_id = SDATA (Vx_session_previous_id);
if (! EQ (Vinvocation_directory, Qnil))
name_len += strlen (SDATA (Vinvocation_directory));
name_len += strlen (SDATA (Vinvocation_name));
emacs_program = xmalloc (name_len + 1);
emacs_program[0] = '\0';
if (! EQ (Vinvocation_directory, Qnil))
strcpy (emacs_program, SDATA (Vinvocation_directory));
strcat (emacs_program, SDATA (Vinvocation_name));
callbacks.save_yourself.callback = smc_save_yourself_CB;
callbacks.save_yourself.client_data = 0;
callbacks.die.callback = smc_die_CB;
callbacks.die.client_data = 0;
callbacks.save_complete.callback = smc_save_complete_CB;
callbacks.save_complete.client_data = 0;
callbacks.shutdown_cancelled.callback = smc_shutdown_cancelled_CB;
callbacks.shutdown_cancelled.client_data = 0;
SmcSetErrorHandler (smc_error_handler);
IceSetErrorHandler (ice_error_handler);
IceSetIOErrorHandler (ice_io_error_handler);
IceAddConnectionWatch (ice_conn_watch_CB, 0);
smc_conn = SmcOpenConnection (NULL, NULL, 1, 0,
(SmcSaveYourselfProcMask|
SmcDieProcMask|
SmcSaveCompleteProcMask|
SmcShutdownCancelledProcMask),
&callbacks,
previous_id,
&client_id,
SM_ERRORSTRING_LEN,
errorstring);
if (smc_conn != 0)
{
Vx_session_id = make_string (client_id, strlen (client_id));
#ifdef USE_GTK
gdk_set_sm_client_id (client_id);
#else
create_client_leader_window (dpyinfo, client_id);
#endif
}
}
DEFUN ("handle-save-session", Fhandle_save_session,
Shandle_save_session, 1, 1, "e",
doc: )
(event)
Lisp_Object event;
{
if (doing_interact)
{
Bool cancel_shutdown = False;
cancel_shutdown = ! EQ (call0 (intern ("emacs-session-save")), Qnil);
SmcInteractDone (smc_conn, cancel_shutdown);
SmcSaveYourselfDone (smc_conn, True);
doing_interact = False;
}
return Qnil;
}
void
syms_of_xsmfns ()
{
DEFVAR_LISP ("x-session-id", &Vx_session_id,
doc: );
Vx_session_id = Qnil;
DEFVAR_LISP ("x-session-previous-id", &Vx_session_previous_id,
doc: );
Vx_session_previous_id = Qnil;
defsubr (&Shandle_save_session);
}
#endif