diff --git a/configure.ac b/configure.ac
index c5ecc74c2..815796cad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,7 +72,7 @@ CLUTTER_PACKAGE=clutter-1.0
MUTTER_PC_MODULES="
gtk+-3.0 >= 3.9.11
- gio-2.0 >= 2.25.10
+ gio-unix-2.0 >= 2.25.10
pango >= 1.2.0
cairo >= 1.10.0
gsettings-desktop-schemas >= 3.7.3
diff --git a/src/Makefile.am b/src/Makefile.am
index cca153392..6aafb9d2c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -167,6 +167,7 @@ libmutter_la_SOURCES = \
core/screen-private.h \
meta/screen.h \
meta/types.h \
+ core/restart.c \
core/stack.c \
core/stack.h \
core/stack-tracker.c \
@@ -316,6 +317,10 @@ bin_PROGRAMS=mutter
mutter_SOURCES = core/mutter.c
mutter_LDADD = $(MUTTER_LIBS) libmutter.la
+libexec_PROGRAMS = mutter-restart-helper
+mutter_restart_helper_SOURCES = core/restart-helper.c
+mutter_restart_helper_LDADD = $(MUTTER_LIBS)
+
if HAVE_INTROSPECTION
include $(INTROSPECTION_MAKEFILE)
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 6ebf78806..aed9e48f1 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -446,4 +446,11 @@ void meta_display_remove_pending_pings_for_window (MetaDisplay *display,
MetaGestureTracker * meta_display_get_gesture_tracker (MetaDisplay *display);
+gboolean meta_display_show_restart_message (MetaDisplay *display,
+ const char *message);
+gboolean meta_display_request_restart (MetaDisplay *display);
+
+void meta_restart_init (void);
+void meta_restart_finish (void);
+
#endif
diff --git a/src/core/display.c b/src/core/display.c
index 8b9c55f6b..603ba5160 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -117,6 +117,8 @@ enum
WINDOW_MARKED_URGENT,
GRAB_OP_BEGIN,
GRAB_OP_END,
+ SHOW_RESTART_MESSAGE,
+ RESTART,
LAST_SIGNAL
};
@@ -269,6 +271,59 @@ meta_display_class_init (MetaDisplayClass *klass)
META_TYPE_WINDOW,
META_TYPE_GRAB_OP);
+ /**
+ * MetaDisplay::show-restart-message:
+ * @display: the #MetaDisplay instance
+ * @message: (allow-none): The message to display, or %NULL
+ * to clear a previous restart message.
+ *
+ * The ::show-restart-message signal will be emitted to indicate
+ * that the compositor should show a message during restart. This is
+ * emitted when meta_restart() is called, either by Mutter
+ * internally or by the embedding compositor. The message should be
+ * immediately added to the Clutter stage in its final form -
+ * ::restart will be emitted to exit the application and leave the
+ * stage contents frozen as soon as the the stage is painted again.
+ *
+ * On case of failure to restart, this signal will be emitted again
+ * with %NULL for @message.
+ *
+ * Returns: %TRUE means the message was added to the stage; %FALSE
+ * indicates that the compositor did not show the message.
+ */
+ display_signals[SHOW_RESTART_MESSAGE] =
+ g_signal_new ("show-restart-message",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled,
+ NULL, NULL,
+ G_TYPE_BOOLEAN, 1,
+ G_TYPE_STRING);
+
+ /**
+ * MetaDisplay::restart:
+ * @display: the #MetaDisplay instance
+ *
+ * The ::restart signal is emitted to indicate that compositor
+ * should reexec the process. This is
+ * emitted when meta_restart() is called, either by Mutter
+ * internally or by the embedding compositor. See also
+ * ::show-restart-message.
+ *
+ * Returns: %FALSE to indicate that the compositor could not
+ * be restarted. When the compositor is restarted, the signal
+ * should not return.
+ */
+ display_signals[RESTART] =
+ g_signal_new ("restart",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled,
+ NULL, NULL,
+ G_TYPE_BOOLEAN, 0);
+
g_object_class_install_property (object_class,
PROP_FOCUS_WINDOW,
g_param_spec_object ("focus-window",
@@ -3138,3 +3193,28 @@ meta_display_get_gesture_tracker (MetaDisplay *display)
{
return display->gesture_tracker;
}
+
+gboolean
+meta_display_show_restart_message (MetaDisplay *display,
+ const char *message)
+{
+ gboolean result = FALSE;
+
+ g_signal_emit (display,
+ display_signals[SHOW_RESTART_MESSAGE], 0,
+ message, &result);
+
+ return result;
+}
+
+gboolean
+meta_display_request_restart (MetaDisplay *display)
+{
+ gboolean result = FALSE;
+
+ g_signal_emit (display,
+ display_signals[RESTART], 0,
+ &result);
+
+ return result;
+}
diff --git a/src/core/main.c b/src/core/main.c
index fb0605450..5ed5247a9 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -367,6 +367,8 @@ meta_init (void)
meta_clutter_init ();
}
+ meta_restart_init ();
+
/*
* XXX: We cannot handle high dpi scaling yet, so fix the scale to 1
* for now.
diff --git a/src/core/restart-helper.c b/src/core/restart-helper.c
new file mode 100644
index 000000000..57a19fb27
--- /dev/null
+++ b/src/core/restart-helper.c
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * SECTION:restart-helper
+ * @short_description: helper program during a restart
+ *
+ * To smoothly restart Mutter, we want to keep the composite
+ * overlay window enabled during the restart. This is done by
+ * spawning this program, which keeps a reference to the the composite
+ * overlay window until Mutter picks it back up.
+ */
+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+int
+main (int argc,
+ char **argv)
+{
+ Display *display = XOpenDisplay (NULL);
+ Window selection_window;
+ XSetWindowAttributes xwa;
+ unsigned long mask = 0;
+
+ xwa.override_redirect = True;
+ mask |= CWOverrideRedirect;
+
+
+ XCompositeGetOverlayWindow (display, DefaultRootWindow (display));
+
+ selection_window = XCreateWindow (display,
+ DefaultRootWindow (display),
+ -100, -100, 1, 1, 0,
+ 0,
+ InputOnly,
+ DefaultVisual (display, DefaultScreen (display)),
+ mask, &xwa);
+
+ XSetSelectionOwner (display,
+ XInternAtom (display, "_MUTTER_RESTART_HELPER", False),
+ selection_window,
+ CurrentTime);
+
+ /* Mutter looks for an (arbitrary) line printed to stdout to know that
+ * we have started and have a reference to the COW. XSync() so that
+ * everything is set on the X server before Mutter starts restarting.
+ */
+ XSync (display, False);
+
+ printf ("STARTED\n");
+ fflush (stdout);
+
+ while (True)
+ {
+ XEvent xev;
+
+ XNextEvent (display, &xev);
+ /* Mutter restarted and unset the selection to indicate that
+ * it has a reference on the COW again */
+ if (xev.xany.type == SelectionClear)
+ return 0;
+ }
+}
diff --git a/src/core/restart.c b/src/core/restart.c
new file mode 100644
index 000000000..903f8e899
--- /dev/null
+++ b/src/core/restart.c
@@ -0,0 +1,212 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+/*
+ * SECTION:restart
+ * @short_description: Smoothly restart the compositor
+ *
+ * There are some cases where we need to restart Mutter in order
+ * to deal with changes in state - the particular case inspiring
+ * this is enabling or disabling stereo output. To make this
+ * fairly smooth for the user, we need to do two things:
+ *
+ * - Display a message to the user and make sure that it is
+ * actually painted before we exit.
+ * - Use a helper program so that the Composite Overlay Window
+ * isn't unmapped and mapped.
+ *
+ * This handles both of these.
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include "ui.h"
+#include "util-private.h"
+#include "display-private.h"
+
+static gboolean restart_helper_started = FALSE;
+static gboolean restart_message_shown = FALSE;
+static gboolean is_restart = FALSE;
+
+void
+meta_restart_init (void)
+{
+ Display *xdisplay = meta_ui_get_display ();
+ Atom atom_restart_helper = XInternAtom (xdisplay, "_MUTTER_RESTART_HELPER", False);
+ Window restart_helper_window = NULL;
+
+ restart_helper_window = XGetSelectionOwner (xdisplay, atom_restart_helper);
+ if (restart_helper_window)
+ is_restart = TRUE;
+}
+
+static void
+restart_check_ready (void)
+{
+ if (restart_helper_started && restart_message_shown)
+ meta_display_request_restart (meta_get_display ());
+}
+
+static void
+restart_helper_read_line_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gsize length;
+ char *line = g_data_input_stream_read_line_finish_utf8 (G_DATA_INPUT_STREAM (source_object),
+ res,
+ &length, &error);
+ if (line == NULL)
+ {
+ meta_warning ("Failed to read output from restart helper%s%s\n",
+ error ? ": " : NULL,
+ error ? error->message : NULL);
+ }
+ else
+ g_free (line); /* We don't actually care what the restart helper outputs */
+
+ g_object_unref (source_object);
+
+ restart_helper_started = TRUE;
+ restart_check_ready ();
+}
+
+static gboolean
+restart_message_painted (gpointer data)
+{
+ restart_message_shown = TRUE;
+ restart_check_ready ();
+
+ return FALSE;
+}
+
+/**
+ * meta_restart:
+ * @message: message to display to the user.
+ *
+ * Starts the process of restarting the compositor. Note that Mutter's
+ * involvement here is to make the restart visually smooth for the
+ * user - it cannot itself safely reexec a program that embeds libmuttter.
+ * So in order for this to work, the compositor must handle two
+ * signals - MetaDisplay::show-restart-message, to display the
+ * message passed here on the Clutter stage, and ::restart to actually
+ * reexec the compositor.
+ */
+void
+meta_restart (const char *message)
+{
+ MetaDisplay *display = meta_get_display();
+ GInputStream *unix_stream;
+ GDataInputStream *data_stream;
+ GError *error = NULL;
+ int helper_out_fd;
+
+ static const char * const helper_argv[] = {
+ MUTTER_LIBEXECDIR "/mutter-restart-helper", NULL
+ };
+
+ if (meta_display_show_restart_message (display, message))
+ {
+ /* Wait until the stage was painted */
+ clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
+ restart_message_painted,
+ NULL, NULL);
+ }
+ else
+ {
+ /* Can't show the message, show the message as soon as the
+ * restart helper starts
+ */
+ restart_message_painted (NULL);
+ }
+
+ /* We also need to wait for the restart helper to get its
+ * reference to the Composite Overlay Window.
+ */
+ if (!g_spawn_async_with_pipes (NULL, /* working directory */
+ (char **)helper_argv,
+ NULL, /* envp */
+ G_SPAWN_DEFAULT,
+ NULL, NULL, /* child_setup */
+ NULL, /* child_pid */
+ NULL, /* standard_input */
+ &helper_out_fd,
+ NULL, /* standard_error */
+ &error))
+ {
+ meta_warning ("Failed to start restart helper: %s\n", error->message);
+ goto error;
+ }
+
+ unix_stream = g_unix_input_stream_new (helper_out_fd, TRUE);
+ data_stream = g_data_input_stream_new (unix_stream);
+ g_object_unref (unix_stream);
+
+ g_data_input_stream_read_line_async (data_stream, G_PRIORITY_DEFAULT,
+ NULL, restart_helper_read_line_callback,
+ &error);
+ if (error != NULL)
+ {
+ meta_warning ("Failed to read from restart helper: %s\n", error->message);
+ g_object_unref (data_stream);
+ goto error;
+ }
+
+ return;
+
+ error:
+ /* If starting the restart helper fails, then we just go ahead and restart
+ * immediately. We won't get a smooth transition, since the overlay window
+ * will be destroyed and recreated, but otherwise it will work fine.
+ */
+ restart_helper_started = TRUE;
+ restart_check_ready ();
+
+ return;
+}
+
+void
+meta_restart_finish (void)
+{
+ if (is_restart)
+ {
+ Display *xdisplay = meta_display_get_xdisplay (meta_get_display ());
+ Atom atom_restart_helper = XInternAtom (xdisplay, "_MUTTER_RESTART_HELPER", False);
+ XSetSelectionOwner (xdisplay, atom_restart_helper, None, CurrentTime);
+ }
+}
+
+/**
+ * meta_is_restart:
+ *
+ * Returns %TRUE if this instance of Mutter comes from Mutter
+ * restarting itself (for example to enable/disable stereo.)
+ * See meta_restart(). If this is the case, any startup visuals
+ * or animations should be suppressed.
+ */
+gboolean
+meta_is_restart (void)
+{
+ return is_restart;
+}
diff --git a/src/core/screen.c b/src/core/screen.c
index 3594d9f0a..959907f96 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -709,6 +709,10 @@ meta_screen_new (MetaDisplay *display,
screen->composite_overlay_window = XCompositeGetOverlayWindow (xdisplay, xroot);
+ /* Now that we've gotten taken a reference count on the COW, we
+ * can close the helper that is holding on to it */
+ meta_restart_finish ();
+
reload_monitor_infos (screen);
meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
diff --git a/src/meta/main.h b/src/meta/main.h
index 5c17bd763..d79ddcccc 100644
--- a/src/meta/main.h
+++ b/src/meta/main.h
@@ -34,6 +34,9 @@ gboolean meta_get_replace_current_wm (void); /* Actually defined in util
void meta_set_wm_name (const char *wm_name);
void meta_set_gnome_wm_keybindings (const char *wm_keybindings);
+void meta_restart (const char *message);
+gboolean meta_is_restart (void);
+
/**
* MetaExitCode:
* @META_EXIT_SUCCESS: Success