summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrhp <rhp>2001-06-22 06:21:44 +0000
committerrhp <rhp>2001-06-22 06:21:44 +0000
commit043b048c5583e6db1e10a92f1b7c9d0f9c4c6149 (patch)
tree3d30b3e6dd7d62d7eef8c312632f886e430280e1
parent67d9ca1d9f4cfa1a97b6815a47e86acb38737c9f (diff)
downloadmetacity-043b048c5583e6db1e10a92f1b7c9d0f9c4c6149.tar.gz
...
-rw-r--r--acconfig.h1
-rw-r--r--configure.in19
-rw-r--r--src/main.c122
-rw-r--r--src/session.c456
4 files changed, 586 insertions, 12 deletions
diff --git a/acconfig.h b/acconfig.h
index 7e0cc165..5623e574 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -9,3 +9,4 @@
#undef GETTEXT_PACKAGE
#undef HAVE_SHAPE_EXT
#undef HAVE_XFT
+#undef HAVE_SM
diff --git a/configure.in b/configure.in
index b4631cc9..7150d923 100644
--- a/configure.in
+++ b/configure.in
@@ -43,6 +43,25 @@ AM_GNU_GETTEXT
## here we get the flags we'll actually use
PKG_CHECK_MODULES(METACITY, gtk+-2.0 >= 1.3.6)
+CFLAGS="$METACITY_CFLAGS $CFLAGS"
+
+found_sm=false
+case "$METACITY_LIBS" in
+ *-lSM*)
+ found_sm=true
+ ;;
+ *)
+ AC_CHECK_LIB(SM, SmcSaveYourselfDone,
+ AC_CHECK_HEADERS(X11/SM/SMlib.h,
+ METACITY_LIBS="-lSM -lICE $METACITY_LIBS" found_sm=true),
+ , $METACITY_LIBS)
+ ;;
+esac
+
+if test "$found_sm" = "true"; then
+ AC_DEFINE(HAVE_SM)
+fi
+
# Check for shaped window extension
AC_CHECK_LIB(Xext, XShapeCombineMask, AC_DEFINE(HAVE_SHAPE_EXT),,$METACITY_LIBS)
diff --git a/src/main.c b/src/main.c
index 6a5577af..2793e7e6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,6 +24,7 @@
#include "display.h"
#include "errors.h"
#include "ui.h"
+#include "session.h"
#include <glib-object.h>
@@ -37,13 +38,24 @@
static MetaExitCode meta_exit_code = META_EXIT_SUCCESS;
static GMainLoop *meta_main_loop = NULL;
+static void
+usage (void)
+{
+ g_print ("metacity [--disable-sm] [--sm-client-id=ID] [--display=DISPLAY]\n");
+ exit (0);
+}
+
int
main (int argc, char **argv)
{
struct sigaction act;
sigset_t empty_mask;
char *display_name;
-
+ int i;
+ const char *client_id;
+ gboolean disable_sm;
+ const char *prev_arg;
+
sigemptyset (&empty_mask);
act.sa_handler = SIG_IGN;
act.sa_mask = empty_mask;
@@ -52,34 +64,125 @@ main (int argc, char **argv)
g_set_prgname (PACKAGE);
- meta_main_loop = g_main_loop_new (NULL, FALSE);
-
meta_set_verbose (TRUE);
meta_set_debugging (TRUE);
meta_set_syncing (g_getenv ("METACITY_SYNC") != NULL);
- if (g_getenv ("METACITY_DISPLAY"))
+ /* Parse options lamely */
+
+ display_name = NULL;
+ client_id = NULL;
+ disable_sm = FALSE;
+ prev_arg = NULL;
+ i = 1;
+ while (i < argc)
+ {
+ const char *arg = argv[i];
+
+ if (strcmp (arg, "--help") == 0 ||
+ strcmp (arg, "-h") == 0 ||
+ strcmp (arg, "-?") == 0)
+ usage ();
+ else if (strcmp (arg, "--sm-disable") == 0)
+ disable_sm = TRUE;
+ else if (strstr (arg, "--display=") == arg)
+ {
+ const char *disp;
+
+ if (display_name != NULL)
+ meta_fatal ("Can't specify display twice\n");
+
+ disp = strchr (arg, '=');
+ ++disp;
+
+ display_name =
+ g_strconcat ("DISPLAY=", disp, NULL);
+ }
+ else if (prev_arg &&
+ strcmp (prev_arg, "--display") == 0)
+ {
+ if (display_name != NULL)
+ meta_fatal ("Can't specify display twice\n");
+
+ display_name = g_strconcat ("DISPLAY=", arg, NULL);
+ }
+ else if (strcmp (arg, "--display") == 0)
+ ; /* wait for next arg */
+ else if (strstr (arg, "--sm-client-id=") == arg)
+ {
+ const char *id;
+
+ if (client_id)
+ meta_fatal ("Can't specify client ID twice\n");
+
+ id = strchr (arg, '=');
+ ++id;
+
+ client_id = g_strdup (id);
+ }
+ else if (prev_arg &&
+ strcmp (prev_arg, "--sm-client-id") == 0)
+ {
+ if (client_id)
+ meta_fatal ("Can't specify client ID twice\n");
+
+ client_id = g_strdup (arg);
+ }
+ else if (strcmp (arg, "--sm-client-id") == 0)
+ ; /* wait for next arg */
+ else
+ usage ();
+
+ prev_arg = arg;
+
+ ++i;
+ }
+
+ meta_main_loop = g_main_loop_new (NULL, FALSE);
+
+ if (display_name == NULL &&
+ g_getenv ("METACITY_DISPLAY"))
{
meta_verbose ("Using METACITY_DISPLAY %s\n",
g_getenv ("METACITY_DISPLAY"));
display_name =
g_strconcat ("DISPLAY=", g_getenv ("METACITY_DISPLAY"), NULL);
+ }
+
+ if (display_name)
+ {
putenv (display_name);
/* DO NOT FREE display_name, putenv() sucks */
}
-
-
+
/* gtk_init() below overrides this, so it can be removed */
meta_errors_init ();
g_type_init (0); /* grumble */
- meta_ui_init (&argc, &argv);
+
+ if (!disable_sm)
+ meta_session_init (client_id); /* client_id == NULL is fine */
+
+ meta_ui_init (&argc, &argv);
if (!meta_display_open (NULL))
meta_exit (META_EXIT_ERROR);
g_main_run (meta_main_loop);
+ {
+ GSList *displays;
+ GSList *tmp;
+
+ displays = meta_displays_list ();
+ tmp = displays;
+ while (tmp != NULL)
+ {
+ meta_display_close (tmp->data);
+ tmp = tmp->next;
+ }
+ }
+
return meta_exit_code;
}
@@ -93,8 +196,9 @@ void
meta_quit (MetaExitCode code)
{
meta_exit_code = code;
-
- g_main_quit (meta_main_loop);
+
+ if (g_main_is_running (meta_main_loop))
+ g_main_quit (meta_main_loop);
}
void
diff --git a/src/session.c b/src/session.c
index 6986d5f2..414b5988 100644
--- a/src/session.c
+++ b/src/session.c
@@ -1,7 +1,8 @@
/* Metacity Session Management */
/*
- * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2001 Havoc Pennington (some code in here from
+ * libgnomeui, (C) Tom Tromey, Carsten Schaar)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -16,12 +17,461 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
+ * 02111-1307, USA. */
#include "session.h"
+#ifndef HAVE_SM
+void
+meta_session_init (const char *previous_id)
+{
+ meta_verbose ("Compiled without session management support\n");
+}
+#else /* HAVE_SM */
+#include <X11/ICE/ICElib.h>
+#include <X11/SM/SMlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "main.h"
+#include "util.h"
+static void ice_io_error_handler (IceConn connection);
+static void new_ice_connection (IceConn connection, IcePointer client_data,
+ Bool opening, IcePointer *watch_data);
+/* This is called when data is available on an ICE connection. */
+static gboolean
+process_ice_messages (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer client_data)
+{
+ IceConn connection = (IceConn) client_data;
+ IceProcessMessagesStatus status;
+
+ status = IceProcessMessages (connection, NULL, NULL);
+
+ if (status == IceProcessMessagesIOError)
+ {
+#if 0
+ IcePointer context = IceGetConnectionContext (connection);
+#endif
+
+ /* We were disconnected */
+ IceSetShutdownNegotiation (connection, False);
+ IceCloseConnection (connection);
+ }
+
+ return TRUE;
+}
+
+/* This is called when a new ICE connection is made. It arranges for
+ the ICE connection to be handled via the event loop. */
+static void
+new_ice_connection (IceConn connection, IcePointer client_data, Bool opening,
+ IcePointer *watch_data)
+{
+ guint input_id;
+
+ if (opening)
+ {
+ /* Make sure we don't pass on these file descriptors to any
+ * exec'ed children
+ */
+ GIOChannel *channel;
+
+ fcntl (IceConnectionNumber(connection),F_SETFD,
+ fcntl(IceConnectionNumber(connection),F_GETFD,0) | FD_CLOEXEC);
+
+ channel = g_io_channel_unix_new (IceConnectionNumber (connection));
+
+ input_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_ERR,
+ process_ice_messages,
+ connection);
+
+ g_io_channel_unref (channel);
+
+ *watch_data = (IcePointer) GUINT_TO_POINTER (input_id);
+ }
+ else
+ {
+ input_id = GPOINTER_TO_UINT ((gpointer) *watch_data);
+
+ g_source_remove (input_id);
+ }
+}
+
+static IceIOErrorHandler gnome_ice_installed_handler;
+
+/* We call any handler installed before (or after) gnome_ice_init but
+ avoid calling the default libICE handler which does an exit() */
+static void
+ice_io_error_handler (IceConn connection)
+{
+ if (gnome_ice_installed_handler)
+ (*gnome_ice_installed_handler) (connection);
+}
+
+static void
+ice_init (void)
+{
+ static gboolean ice_initted = FALSE;
+
+ if (! ice_initted)
+ {
+ IceIOErrorHandler default_handler;
+
+ gnome_ice_installed_handler = IceSetIOErrorHandler (NULL);
+ default_handler = IceSetIOErrorHandler (ice_io_error_handler);
+
+ if (gnome_ice_installed_handler == default_handler)
+ gnome_ice_installed_handler = NULL;
+
+ IceAddConnectionWatch (new_ice_connection, NULL);
+
+ ice_initted = TRUE;
+ }
+}
+
+typedef enum
+{
+ STATE_DISCONNECTED,
+ STATE_IDLE,
+ STATE_SAVING_PHASE_1,
+ STATE_WAITING_FOR_PHASE_2,
+ STATE_SAVING_PHASE_2,
+ STATE_FROZEN,
+ STATE_REGISTERING
+} ClientState;
+
+static void save_phase_2_callback (SmcConn smc_conn,
+ SmPointer client_data);
+static void interact_callback (SmcConn smc_conn,
+ SmPointer client_data);
+static void shutdown_cancelled_callback (SmcConn smc_conn,
+ SmPointer client_data);
+static void save_complete_callback (SmcConn smc_conn,
+ SmPointer client_data);
+static void die_callback (SmcConn smc_conn,
+ SmPointer client_data);
+static void save_yourself_callback (SmcConn smc_conn,
+ SmPointer client_data,
+ int save_style,
+ Bool shutdown,
+ int interact_style,
+ Bool fast);
+static void set_clone_restart_commands (void);
+
+static gchar *client_id = NULL;
+static gpointer session_connection = NULL;
+static ClientState current_state = STATE_DISCONNECTED;
+
+void
+meta_session_init (const char *previous_id)
+{
+ /* Some code here from twm */
+ char buf[256];
+ unsigned long mask;
+ SmcCallbacks callbacks;
+
+ meta_verbose ("Initializing session with session ID '%s'\n",
+ previous_id ? previous_id : "(none)");
+
+ ice_init ();
+
+ mask = SmcSaveYourselfProcMask | SmcDieProcMask |
+ SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
+
+ callbacks.save_yourself.callback = save_yourself_callback;
+ callbacks.save_yourself.client_data = NULL;
+
+ callbacks.die.callback = die_callback;
+ callbacks.die.client_data = NULL;
+
+ callbacks.save_complete.callback = save_complete_callback;
+ callbacks.save_complete.client_data = NULL;
+
+ callbacks.shutdown_cancelled.callback = shutdown_cancelled_callback;
+ callbacks.shutdown_cancelled.client_data = NULL;
+
+ session_connection =
+ SmcOpenConnection (NULL, /* use SESSION_MANAGER env */
+ NULL, /* means use existing ICE connection */
+ SmProtoMajor,
+ SmProtoMinor,
+ mask,
+ &callbacks,
+ previous_id,
+ &client_id,
+ 255, buf);
+
+ if (session_connection == NULL)
+ {
+ meta_warning ("Failed to open connection to session manager: %s\n", buf);
+ return;
+ }
+ else
+ meta_verbose ("Obtained session ID '%s'\n", client_id);
+
+ if (previous_id && strcmp (previous_id, client_id) == 0)
+ current_state = STATE_IDLE;
+ else
+ current_state = STATE_REGISTERING;
+
+ {
+ SmProp prop1, prop2, prop3, prop4, prop5, *props[5];
+ SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val;
+ char pid[32];
+ char hint = SmRestartIfRunning;
+
+ prop1.name = SmProgram;
+ prop1.type = SmARRAY8;
+ prop1.num_vals = 1;
+ prop1.vals = &prop1val;
+ prop1val.value = "metacity";
+ prop1val.length = strlen ("metacity");
+
+ /* twm sets getuid() for this, but the SM spec plainly
+ * says pw_name, twm is on crack
+ */
+ prop2.name = SmUserID;
+ prop2.type = SmARRAY8;
+ prop2.num_vals = 1;
+ prop2.vals = &prop2val;
+ prop2val.value = g_get_user_name ();
+ prop2val.length = strlen (prop2val.value);
+
+ prop3.name = SmRestartStyleHint;
+ prop3.type = SmCARD8;
+ prop3.num_vals = 1;
+ prop3.vals = &prop3val;
+ prop3val.value = &hint;
+ prop3val.length = 1;
+
+ sprintf (pid, "%d", getpid ());
+ prop4.name = SmProcessID;
+ prop4.type = SmARRAY8;
+ prop4.num_vals = 1;
+ prop4.vals = &prop4val;
+ prop4val.value = pid;
+ prop4val.length = strlen (prop4val.value);
+
+ /* Always start in home directory */
+ prop5.name = SmCurrentDirectory;
+ prop5.type = SmARRAY8;
+ prop5.num_vals = 1;
+ prop5.vals = &prop5val;
+ prop5val.value = g_get_home_dir ();
+ prop5val.length = strlen (prop5val.value);
+
+ props[0] = &prop1;
+ props[1] = &prop2;
+ props[2] = &prop3;
+ props[3] = &prop4;
+ props[4] = &prop5;
+
+ SmcSetProperties (session_connection, 5, props);
+ }
+
+ set_clone_restart_commands ();
+}
+
+static void
+disconnect (void)
+{
+ SmcCloseConnection (session_connection, 0, NULL);
+ session_connection = NULL;
+ current_state = STATE_DISCONNECTED;
+}
+
+static void
+save_yourself_possibly_done (gboolean shutdown,
+ gboolean successful)
+{
+ if (current_state == STATE_SAVING_PHASE_1)
+ {
+ Status status;
+
+ status = SmcRequestSaveYourselfPhase2 (session_connection,
+ save_phase_2_callback,
+ GINT_TO_POINTER (shutdown));
+
+ if (status)
+ current_state = STATE_WAITING_FOR_PHASE_2;
+ }
+
+ if (current_state == STATE_SAVING_PHASE_1 ||
+ current_state == STATE_SAVING_PHASE_2)
+ {
+ SmcSaveYourselfDone (session_connection,
+ successful);
+
+ if (shutdown)
+ current_state = STATE_FROZEN;
+ else
+ current_state = STATE_IDLE;
+ }
+}
+
+
+static void
+save_phase_2_callback (SmcConn smc_conn, SmPointer client_data)
+{
+ gboolean shutdown;
+
+ shutdown = GPOINTER_TO_INT (client_data);
+
+ current_state = STATE_SAVING_PHASE_2;
+
+ save_yourself_possibly_done (shutdown, TRUE);
+}
+
+static void
+save_yourself_callback (SmcConn smc_conn,
+ SmPointer client_data,
+ int save_style,
+ Bool shutdown,
+ int interact_style,
+ Bool fast)
+{
+ gboolean successful;
+
+ successful = TRUE;
+
+ /* The first SaveYourself after registering for the first time
+ * is a special case (SM specs 7.2).
+ *
+ * This SaveYourself seems to be included in the protocol to
+ * ask the client to specify its initial SmProperties since
+ * there is little point saving a copy of the initial state.
+ *
+ * A bug in xsm means that it does not send us a SaveComplete
+ * in response to this initial SaveYourself. Therefore, we
+ * must not set a grab because it would never be released.
+ * Indeed, even telling the app that this SaveYourself has
+ * arrived is hazardous as the app may take its own steps
+ * to freeze its WM state while waiting for the SaveComplete.
+ *
+ * Fortunately, we have already set the SmProperties during
+ * gnome_client_connect so there is little lost in simply
+ * returning immediately.
+ *
+ * Apps which really want to save their initial states can
+ * do so safely using gnome_client_save_yourself_request.
+ */
+
+ if (current_state == STATE_REGISTERING)
+ {
+ current_state = STATE_IDLE;
+
+ /* Double check that this is a section 7.2 SaveYourself: */
+
+ if (save_style == SmSaveLocal &&
+ interact_style == SmInteractStyleNone &&
+ !shutdown && !fast)
+ {
+ /* The protocol requires this even if xsm ignores it. */
+ SmcSaveYourselfDone (session_connection, successful);
+ return;
+ }
+ }
+
+ current_state = STATE_SAVING_PHASE_1;
+
+ set_clone_restart_commands ();
+
+ save_yourself_possibly_done (shutdown, successful);
+}
+
+
+static void
+die_callback (SmcConn smc_conn, SmPointer client_data)
+{
+ meta_verbose ("Exiting at request of session manager\n");
+ disconnect ();
+ meta_quit (META_EXIT_SUCCESS);
+}
+
+static void
+save_complete_callback (SmcConn smc_conn, SmPointer client_data)
+{
+ /* nothing */
+}
+
+static void
+shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data)
+{
+ /* nothing */
+}
+
+static void
+interact_callback (SmcConn smc_conn, SmPointer client_data)
+{
+ /* nothing */
+}
+
+static void
+set_clone_restart_commands (void)
+{
+ char *restartv[10];
+ char *clonev[10];
+ int i;
+ SmProp prop1, prop2, *props[2];
+
+ /* Restart (use same client ID) */
+
+ prop1.name = SmRestartCommand;
+ prop1.type = SmLISTofARRAY8;
+
+ i = 0;
+ restartv[i] = "metacity";
+ ++i;
+ restartv[i] = "--sm-client-id";
+ ++i;
+ restartv[i] = client_id;
+ ++i;
+ restartv[i] = NULL;
+
+ prop1.vals = g_new (SmPropValue, i);
+ i = 0;
+ while (restartv[i])
+ {
+ prop1.vals[i].value = restartv[i];
+ prop1.vals[i].length = strlen (restartv[i]);
+ ++i;
+ }
+ prop1.num_vals = i;
+
+ /* Clone (no client ID) */
+
+ i = 0;
+ clonev[i] = "metacity";
+ ++i;
+ clonev[i] = NULL;
+
+ prop2.name = SmCloneCommand;
+ prop2.type = SmLISTofARRAY8;
+
+ prop2.vals = g_new (SmPropValue, i);
+ i = 0;
+ while (clonev[i])
+ {
+ prop2.vals[i].value = clonev[i];
+ prop2.vals[i].length = strlen (clonev[i]);
+ ++i;
+ }
+ prop2.num_vals = i;
+
+ props[0] = &prop1;
+ props[1] = &prop2;
+
+ SmcSetProperties (session_connection, 2, props);
+}
+
+#endif /* HAVE_SM */