diff --git a/SOURCES/0001-Add-a-framework-for-restarting-the-compositor-with-n.patch b/SOURCES/0001-Add-a-framework-for-restarting-the-compositor-with-n.patch new file mode 100644 index 0000000..7291174 --- /dev/null +++ b/SOURCES/0001-Add-a-framework-for-restarting-the-compositor-with-n.patch @@ -0,0 +1,559 @@ +From 6710b03d1ebafc15ee4e826a5281204c6441c083 Mon Sep 17 00:00:00 2001 +From: "Owen W. Taylor" +Date: Thu, 8 May 2014 18:35:49 -0400 +Subject: [PATCH 1/4] Add a framework for restarting the compositor with nice + visuals + +The current GNOME Shell Alt-F2 restart looks very messy and also +provides no indication to the user what is going on. We need to +restart the compositor to switch in and out of stereo mode, so +add a framework for doing this more cleanly: + +Additions: + + meta_restart(): restarts the compositor with a message + MetaDisplay::show-restart-message: signal the embedding + shell to show a message + MetaDisplay::restart: signal the embedding shell to restart + itself. + meta_is_restart(): indicates whether the current instance is a + restart so we can suppress login animations. + +A helper program meta-restart-helper holds the composite overlay +window up during the restart to avoid visual artifacts. + +https://bugzilla.gnome.org/show_bug.cgi?id=733026 +--- + configure.ac | 1 + + src/Makefile.am | 5 ++ + src/compositor/compositor.c | 5 ++ + src/core/display-private.h | 7 ++ + src/core/display.c | 80 +++++++++++++++++ + src/core/main.c | 3 + + src/core/restart-helper.c | 82 +++++++++++++++++ + src/core/restart.c | 212 ++++++++++++++++++++++++++++++++++++++++++++ + src/meta/main.h | 3 + + 9 files changed, 398 insertions(+) + create mode 100644 src/core/restart-helper.c + create mode 100644 src/core/restart.c + +diff --git a/configure.ac b/configure.ac +index 6e9ca8c..dcd8a54 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -69,6 +69,7 @@ CLUTTER_PACKAGE=clutter-1.0 + MUTTER_PC_MODULES=" + gtk+-3.0 >= 3.3.7 + 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 e2cec91..d664b54 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -119,6 +119,7 @@ libmutter_la_SOURCES = \ + core/screen-private.h \ + meta/screen.h \ + meta/types.h \ ++ core/restart.c \ + core/session.c \ + core/session.h \ + core/stack.c \ +@@ -213,6 +214,10 @@ bin_PROGRAMS=mutter mutter-theme-viewer + 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/compositor/compositor.c b/src/compositor/compositor.c +index 91b477f..539a7a6 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -86,6 +86,7 @@ + #include "meta-window-group.h" + #include "window-private.h" /* to check window->hidden */ + #include "display-private.h" /* for meta_display_lookup_x_window() */ ++#include "util-private.h" + #include + #include + +@@ -216,6 +217,10 @@ get_output_window (MetaScreen *screen) + xroot = meta_screen_get_xroot (screen); + output = 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 (); ++ + meta_core_add_old_event_mask (xdisplay, output, &mask); + + XISetMask (mask.mask, XI_KeyPress); +diff --git a/src/core/display-private.h b/src/core/display-private.h +index 3423fb1..d227492 100644 +--- a/src/core/display-private.h ++++ b/src/core/display-private.h +@@ -470,4 +470,11 @@ gboolean meta_display_process_barrier_event (MetaDisplay *display, + XIBarrierEvent *event); + #endif /* HAVE_XI23 */ + ++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 d611314..9ed0f6e 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -146,6 +146,8 @@ enum + WINDOW_MARKED_URGENT, + GRAB_OP_BEGIN, + GRAB_OP_END, ++ SHOW_RESTART_MESSAGE, ++ RESTART, + LAST_SIGNAL + }; + +@@ -322,6 +324,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", +@@ -5856,3 +5911,28 @@ meta_display_clear_mouse_mode (MetaDisplay *display) + { + display->mouse_mode = FALSE; + } ++ ++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 4bec3d2..d5bde44 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -53,6 +53,7 @@ + #include + #include "ui.h" + #include "session.h" ++#include "stereo.h" + #include + #include + +@@ -441,6 +442,8 @@ meta_init (void) + + meta_ui_init (); + ++ meta_restart_init (); ++ + /* + * Clutter can only be initialized after the UI. + */ +diff --git a/src/core/restart-helper.c b/src/core/restart-helper.c +new file mode 100644 +index 0000000..57a19fb +--- /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 0000000..04ef7d5 +--- /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 ++#include "ui.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 */ ++ 0, /* 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/meta/main.h b/src/meta/main.h +index dd4e7f1..af7a2c9 100644 +--- a/src/meta/main.h ++++ b/src/meta/main.h +@@ -35,6 +35,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 +-- +1.9.3 + diff --git a/SOURCES/0001-MetaTextureTower-actually-mark-revalidated-levels-as.patch b/SOURCES/0001-MetaTextureTower-actually-mark-revalidated-levels-as.patch new file mode 100644 index 0000000..27f6e35 --- /dev/null +++ b/SOURCES/0001-MetaTextureTower-actually-mark-revalidated-levels-as.patch @@ -0,0 +1,13 @@ +diff -up mutter-3.8.4/src/compositor/meta-texture-tower.c.texture-tower mutter-3.8.4/src/compositor/meta-texture-tower.c +--- mutter-3.8.4/src/compositor/meta-texture-tower.c.texture-tower 2014-09-29 15:07:00.955634201 -0400 ++++ mutter-3.8.4/src/compositor/meta-texture-tower.c 2014-09-29 15:08:01.656005996 -0400 +@@ -562,6 +562,9 @@ texture_tower_revalidate (MetaTextureTow + { + if (!texture_tower_revalidate_fbo (tower, level)) + texture_tower_revalidate_client (tower, level); ++ ++ tower->invalid[level].x1 = tower->invalid[level].x2 = 0; ++ tower->invalid[level].y1 = tower->invalid[level].y2 = 0; + } + + /** diff --git a/SOURCES/0001-window-Fix-delayed-mouse-mode-on-X.patch b/SOURCES/0001-window-Fix-delayed-mouse-mode-on-X.patch new file mode 100644 index 0000000..1b101f2 --- /dev/null +++ b/SOURCES/0001-window-Fix-delayed-mouse-mode-on-X.patch @@ -0,0 +1,41 @@ +From 8c4610721a117d5d50fc72fb75e41a6727d93b2d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 7 May 2014 23:22:41 +0200 +Subject: [PATCH] window: Fix delayed mouse mode on X + +Using meta_stack_get_default_focus_window_at_point() is unreliable +when used with the overlay windows or popups. Instead, we can just +look up the MetaWindow for the client window that XIQueryPointer +returns. + +https://bugzilla.gnome.org/show_bug.cgi?id=730541 +--- + src/core/display.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/src/core/display.c b/src/core/display.c +index 435d9d9..b7688e6 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -1703,17 +1703,7 @@ window_focus_on_pointer_rest_callback (gpointer data) { + return TRUE; + } + +- /* Explicitly check for the overlay window, as get_focus_window_at_point() +- * may return windows that extend underneath the chrome (like +- * override-redirect or DESKTOP windows) +- */ +- if (child == meta_get_overlay_window (screen)) +- goto out; +- +- window = +- meta_stack_get_default_focus_window_at_point (screen->stack, +- screen->active_workspace, +- None, root_x, root_y); ++ window = meta_display_lookup_x_window (display, child); + + if (window == NULL) + goto out; +-- +2.1.0 + diff --git a/SOURCES/0002-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0002-Add-support-for-quad-buffer-stereo.patch new file mode 100644 index 0000000..c3d0c82 --- /dev/null +++ b/SOURCES/0002-Add-support-for-quad-buffer-stereo.patch @@ -0,0 +1,909 @@ +From 9ceccaf38295d73b729bc3a6777fa6b948fa4007 Mon Sep 17 00:00:00 2001 +From: "Owen W. Taylor" +Date: Thu, 8 May 2014 18:44:15 -0400 +Subject: [PATCH] Add support for quad-buffer stereo + +Track the stereo status of windows using the new EXT_stereo_tree +GLX extension. + +When stereo is enabled or disabled, a restart is triggered via +meta_restart() after a timeout, setting a _META_ENABLE_STEREO +property on the root window to indicate whether we should +turn on a stereo stage for clutter. The property avoids a loop, +since we need to enable stereo *before* initializing Clutter and GL, +but we need GL to figure out whether we have stereo windows. + +Stereo windows are drawn to the stage using new functionality +in Cogl to setup a stereo context, select which buffer to draw +to, and draw either the left or right buffer of a stereo +texture_from_pixmap. +--- + src/Makefile.am | 2 + + src/compositor/compositor-private.h | 10 ++ + src/compositor/compositor.c | 127 +++++++++++++++++++- + src/compositor/meta-shaped-texture-private.h | 37 ++++++ + src/compositor/meta-shaped-texture.c | 166 ++++++++++++++++++++------- + src/compositor/meta-window-actor-private.h | 7 ++ + src/compositor/meta-window-actor.c | 27 ++++- + src/core/main.c | 2 + + src/core/stereo.c | 144 +++++++++++++++++++++++ + src/core/stereo.h | 28 +++++ + src/meta/meta-shaped-texture.h | 3 +- + 11 files changed, 508 insertions(+), 45 deletions(-) + create mode 100644 src/compositor/meta-shaped-texture-private.h + create mode 100644 src/core/stereo.c + create mode 100644 src/core/stereo.h + +diff --git a/src/Makefile.am b/src/Makefile.am +index d664b54..1d2422e 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -122,6 +122,8 @@ libmutter_la_SOURCES = \ + core/restart.c \ + core/session.c \ + core/session.h \ ++ core/stereo.c \ ++ core/stereo.h \ + core/stack.c \ + core/stack.h \ + core/stack-tracker.c \ +diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h +index acb8d3c..b4c5c16 100644 +--- a/src/compositor/compositor-private.h ++++ b/src/compositor/compositor-private.h +@@ -28,6 +28,8 @@ struct _MetaCompositor + gint64 server_time_query_time; + gint64 server_time_offset; + ++ int glx_opcode; ++ + guint server_time_is_monotonic_time : 1; + guint show_redraw : 1; + guint debug : 1; +@@ -56,6 +58,9 @@ struct _MetaCompScreen + + gint switch_workspace_in_progress; + ++ guint stereo_tree_ext : 1; ++ guint have_stereo_windows : 1; ++ + MetaPluginManager *plugin_mgr; + }; + +@@ -79,4 +84,9 @@ gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, + + void meta_check_end_modal (MetaScreen *screen); + ++gboolean meta_compositor_window_is_stereo (MetaScreen *screen, ++ Window xwindow); ++void meta_compositor_select_stereo_notify (MetaScreen *screen, ++ Window xwindow); ++ + #endif /* META_COMPOSITOR_PRIVATE_H */ +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index 539a7a6..fd29e48 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -86,7 +86,8 @@ + #include "meta-window-group.h" + #include "window-private.h" /* to check window->hidden */ + #include "display-private.h" /* for meta_display_lookup_x_window() */ +-#include "util-private.h" ++#include "stack-tracker.h" ++#include "stereo.h" + #include + #include + +@@ -579,6 +580,101 @@ redirect_windows (MetaCompositor *compositor, + } + } + ++#define GLX_STEREO_TREE_EXT 0x20F5 ++#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001 ++#define GLX_STEREO_NOTIFY_EXT 0x00000000 ++ ++typedef struct { ++ int type; ++ unsigned long serial; ++ Bool send_event; ++ Display *display; ++ int extension; ++ int evtype; ++ Drawable window; ++ Bool stereo_tree; ++} StereoNotifyEvent; ++ ++static gboolean ++screen_has_stereo_tree_ext (MetaScreen *screen) ++{ ++#if 0 ++ MetaDisplay *display = meta_screen_get_display (screen); ++ Display *xdisplay = meta_display_get_xdisplay (display); ++ const char *extensions_string; ++ ++ static const char * (*query_extensions_string) (Display *display, ++ int screen); ++ ++ if (query_extensions_string == NULL) ++ query_extensions_string = ++ (const char * (*) (Display *, int)) ++ cogl_get_proc_address ("glXQueryExtensionsString"); ++ ++ extensions_string = query_extensions_string (xdisplay, ++ meta_screen_get_screen_number (screen)); ++ ++ return strstr (extensions_string, "EXT_stereo_tree") != 0; ++#else ++ return TRUE; ++#endif ++} ++ ++#include ++ ++gboolean ++meta_compositor_window_is_stereo (MetaScreen *screen, ++ Window xwindow) ++{ ++ MetaCompScreen *info = meta_screen_get_compositor_data (screen); ++ MetaDisplay *display = meta_screen_get_display (screen); ++ Display *xdisplay = meta_display_get_xdisplay (display); ++ ++ static int (*query_drawable) (Display *dpy, ++ Drawable draw, ++ int attribute, ++ unsigned int *value); ++ ++ if (info->stereo_tree_ext) ++ { ++ unsigned int stereo_tree = 0; ++ ++ if (query_drawable == NULL) ++ query_drawable = ++ (int (*) (Display *, Drawable, int, unsigned int *)) ++ cogl_get_proc_address ("glXQueryDrawable"); ++ ++ query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree); ++ ++ return stereo_tree != 0; ++ } ++ else ++ return FALSE; ++} ++ ++void ++meta_compositor_select_stereo_notify (MetaScreen *screen, ++ Window xwindow) ++{ ++ MetaCompScreen *info = meta_screen_get_compositor_data (screen); ++ MetaDisplay *display = meta_screen_get_display (screen); ++ Display *xdisplay = meta_display_get_xdisplay (display); ++ ++ static void (*select_event) (Display *dpy, ++ Drawable draw, ++ unsigned long event_mask); ++ ++ if (info->stereo_tree_ext) ++ { ++ if (select_event == NULL) ++ select_event = ++ (void (*) (Display *, Drawable, unsigned long)) ++ cogl_get_proc_address ("glXSelectEvent"); ++ ++ select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT); ++ } ++} ++ + void + meta_compositor_manage_screen (MetaCompositor *compositor, + MetaScreen *screen) +@@ -609,6 +705,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor, + info->output = None; + info->windows = NULL; + ++ info->stereo_tree_ext = screen_has_stereo_tree_ext (screen); ++ + meta_screen_set_cm_selection (screen); + + info->stage = clutter_stage_new (); +@@ -995,6 +1093,23 @@ meta_compositor_process_event (MetaCompositor *compositor, + DEBUG_TRACE ("meta_compositor_process_event (process_damage)\n"); + process_damage (compositor, (XDamageNotifyEvent *) event, window); + } ++ else if (event->type == GenericEvent && ++ event->xcookie.extension == compositor->glx_opcode) ++ { ++ if (event->xcookie.evtype == GLX_STEREO_NOTIFY_EXT) ++ { ++ StereoNotifyEvent *stereo_event = (StereoNotifyEvent *)(event->xcookie.data); ++ window = meta_display_lookup_x_window (compositor->display, stereo_event->window); ++ ++ if (window != NULL) ++ { ++ MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); ++ meta_window_actor_stereo_notify (window_actor, stereo_event->stereo_tree); ++ meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker); ++ } ++ } ++ } ++ + break; + } + +@@ -1209,6 +1324,7 @@ meta_compositor_sync_stack (MetaCompositor *compositor, + { + GList *old_stack; + MetaCompScreen *info = meta_screen_get_compositor_data (screen); ++ int stereo_window_count = 0; + + DEBUG_TRACE ("meta_compositor_sync_stack\n"); + +@@ -1286,12 +1402,16 @@ meta_compositor_sync_stack (MetaCompositor *compositor, + * near the front of the other.) + */ + info->windows = g_list_prepend (info->windows, actor); ++ if (meta_window_actor_is_stereo (actor)) ++ stereo_window_count++; + + stack = g_list_remove (stack, window); + old_stack = g_list_remove (old_stack, actor); + } + + sync_actor_stacking (info); ++ ++ meta_stereo_set_have_stereo_windows (stereo_window_count > 0); + } + + void +@@ -1505,6 +1625,7 @@ meta_compositor_new (MetaDisplay *display) + Atom atoms[G_N_ELEMENTS(atom_names)]; + MetaCompositor *compositor; + Display *xdisplay = meta_display_get_xdisplay (display); ++ int glx_major_opcode, glx_first_event, glx_first_error; + + if (!composite_at_least_version (display, 0, 3)) + return NULL; +@@ -1531,6 +1652,10 @@ meta_compositor_new (MetaDisplay *display) + compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func, + compositor, + NULL); ++ if (XQueryExtension (xdisplay, ++ "GLX", ++ &glx_major_opcode, &glx_first_event, &glx_first_error)) ++ compositor->glx_opcode = glx_major_opcode; + + return compositor; + } +diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h +new file mode 100644 +index 0000000..c70f6a8 +--- /dev/null ++++ b/src/compositor/meta-shaped-texture-private.h +@@ -0,0 +1,37 @@ ++/* ++ * shaped texture ++ * ++ * An actor to draw a texture clipped to a list of rectangles ++ * ++ * Authored By Neil Roberts ++ * ++ * Copyright (C) 2008 Intel Corporation ++ * 2013 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, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ */ ++ ++#ifndef __META_SHAPED_TEXTURE_PRIVATE_H__ ++#define __META_SHAPED_TEXTURE_PRIVATE_H__ ++ ++#include ++ ++ClutterActor *meta_shaped_texture_new (void); ++gboolean meta_shaped_texture_get_unobscured_bounds (MetaShapedTexture *stex, ++ cairo_rectangle_int_t *unobscured_bounds); ++gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self); ++ ++#endif +diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c +index c6239c9..132cb09 100644 +--- a/src/compositor/meta-shaped-texture.c ++++ b/src/compositor/meta-shaped-texture.c +@@ -65,8 +65,10 @@ G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture, + struct _MetaShapedTexturePrivate + { + MetaTextureTower *paint_tower; ++ MetaTextureTower *paint_tower_right; + Pixmap pixmap; + CoglTexturePixmapX11 *texture; ++ CoglTexturePixmapX11 *texture_right; + CoglTexture *mask_texture; + CoglPipeline *pipeline; + CoglPipeline *pipeline_unshaped; +@@ -75,6 +77,7 @@ struct _MetaShapedTexturePrivate + + guint tex_width, tex_height; + ++ guint stereo : 1; + guint create_mipmaps : 1; + }; + +@@ -103,7 +106,9 @@ meta_shaped_texture_init (MetaShapedTexture *self) + priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self); + + priv->paint_tower = meta_texture_tower_new (); ++ priv->paint_tower_right = NULL; /* demand create */ + priv->texture = NULL; ++ priv->texture_right = NULL; + priv->mask_texture = NULL; + priv->create_mipmaps = TRUE; + } +@@ -114,13 +119,13 @@ meta_shaped_texture_dispose (GObject *object) + MetaShapedTexture *self = (MetaShapedTexture *) object; + MetaShapedTexturePrivate *priv = self->priv; + +- if (priv->paint_tower) +- meta_texture_tower_free (priv->paint_tower); +- priv->paint_tower = NULL; ++ g_clear_pointer (&priv->paint_tower, meta_texture_tower_free); ++ g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free); + + g_clear_pointer (&priv->pipeline, cogl_object_unref); + g_clear_pointer (&priv->pipeline_unshaped, cogl_object_unref); + g_clear_pointer (&priv->texture, cogl_object_unref); ++ g_clear_pointer (&priv->texture_right, cogl_object_unref); + + meta_shaped_texture_set_mask_texture (self, NULL); + meta_shaped_texture_set_clip_region (self, NULL); +@@ -129,11 +134,11 @@ meta_shaped_texture_dispose (GObject *object) + } + + static void +-meta_shaped_texture_paint (ClutterActor *actor) ++paint_texture (MetaShapedTexture *stex, ++ CoglTexture *paint_tex) + { +- MetaShapedTexture *stex = (MetaShapedTexture *) actor; + MetaShapedTexturePrivate *priv = stex->priv; +- CoglTexture *paint_tex; ++ ClutterActor *actor = CLUTTER_ACTOR (stex); + guint tex_width, tex_height; + ClutterActorBox alloc; + +@@ -145,32 +150,6 @@ meta_shaped_texture_paint (ClutterActor *actor) + if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) + return; + +- if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex))) +- clutter_actor_realize (CLUTTER_ACTOR (stex)); +- +- /* The GL EXT_texture_from_pixmap extension does allow for it to be +- * used together with SGIS_generate_mipmap, however this is very +- * rarely supported. Also, even when it is supported there +- * are distinct performance implications from: +- * +- * - Updating mipmaps that we don't need +- * - Having to reallocate pixmaps on the server into larger buffers +- * +- * So, we just unconditionally use our mipmap emulation code. If we +- * wanted to use SGIS_generate_mipmap, we'd have to query COGL to +- * see if it was supported (no API currently), and then if and only +- * if that was the case, set the clutter texture quality to HIGH. +- * Setting the texture quality to high without SGIS_generate_mipmap +- * support for TFP textures will result in fallbacks to XGetImage. +- */ +- if (priv->create_mipmaps) +- paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); +- else +- paint_tex = COGL_TEXTURE (priv->texture); +- +- if (paint_tex == NULL) +- return; +- + tex_width = priv->tex_width; + tex_height = priv->tex_height; + +@@ -279,6 +258,76 @@ meta_shaped_texture_paint (ClutterActor *actor) + } + + static void ++meta_shaped_texture_paint (ClutterActor *actor) ++{ ++ MetaShapedTexture *stex = (MetaShapedTexture *) actor; ++ MetaShapedTexturePrivate *priv = stex->priv; ++ CoglFramebuffer *fb; ++ gboolean stereo; ++ CoglTexture *paint_tex; ++ CoglTexture *paint_tex_right; ++ ++ if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) ++ return; ++ ++ if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex))) ++ clutter_actor_realize (CLUTTER_ACTOR (stex)); ++ ++ /* The GL EXT_texture_from_pixmap extension does allow for it to be ++ * used together with SGIS_generate_mipmap, however this is very ++ * rarely supported. Also, even when it is supported there ++ * are distinct performance implications from: ++ * ++ * - Updating mipmaps that we don't need ++ * - Having to reallocate pixmaps on the server into larger buffers ++ * ++ * So, we just unconditionally use our mipmap emulation code. If we ++ * wanted to use SGIS_generate_mipmap, we'd have to query COGL to ++ * see if it was supported (no API currently), and then if and only ++ * if that was the case, set the clutter texture quality to HIGH. ++ * Setting the texture quality to high without SGIS_generate_mipmap ++ * support for TFP textures will result in fallbacks to XGetImage. ++ */ ++ if (priv->create_mipmaps) ++ paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); ++ else ++ paint_tex = COGL_TEXTURE (priv->texture); ++ ++ if (paint_tex == NULL) ++ return; ++ ++ fb = cogl_get_draw_framebuffer (); ++ ++ stereo = priv->stereo && cogl_framebuffer_get_is_stereo (fb); ++ ++ if (stereo) ++ { ++ if (priv->create_mipmaps) ++ paint_tex_right = meta_texture_tower_get_paint_texture (priv->paint_tower_right); ++ else ++ paint_tex_right = COGL_TEXTURE (priv->texture_right); ++ } ++ else ++ paint_tex_right = NULL; ++ ++ if (paint_tex != NULL) ++ { ++ if (stereo) ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_LEFT); ++ paint_texture (stex, paint_tex); ++ if (stereo) ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH); ++ } ++ ++ if (paint_tex_right != NULL) ++ { ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_RIGHT); ++ paint_texture (stex, paint_tex_right); ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH); ++ } ++} ++ ++static void + meta_shaped_texture_pick (ClutterActor *actor, + const ClutterColor *color) + { +@@ -392,6 +441,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, + base_texture = create_mipmaps ? + COGL_TEXTURE (priv->texture) : NULL; + meta_texture_tower_set_base_texture (priv->paint_tower, base_texture); ++ ++ if (priv->stereo) ++ { ++ base_texture = create_mipmaps ? COGL_TEXTURE (priv->texture_right) : NULL; ++ meta_texture_tower_set_base_texture (priv->paint_tower_right, base_texture); ++ } + } + } + +@@ -435,13 +490,16 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, + x, y, width, height); + + meta_texture_tower_update_area (priv->paint_tower, x, y, width, height); ++ if (priv->stereo) ++ meta_texture_tower_update_area (priv->paint_tower_right, x, y, width, height); + + clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip); + } + + static void + set_cogl_texture (MetaShapedTexture *stex, +- CoglTexturePixmapX11 *cogl_tex) ++ CoglTexturePixmapX11 *cogl_tex, ++ gboolean stereo) + { + MetaShapedTexturePrivate *priv; + guint width, height; +@@ -452,9 +510,17 @@ set_cogl_texture (MetaShapedTexture *stex, + + if (priv->texture != NULL) + cogl_object_unref (priv->texture); ++ if (priv->texture_right != NULL) ++ cogl_object_unref (priv->texture_right); + ++ priv->stereo = stereo; + priv->texture = cogl_tex; + ++ if (priv->stereo) ++ priv->texture_right = cogl_texture_pixmap_x11_new_right ((CoglTexturePixmapX11 *)cogl_tex); ++ else ++ priv->texture_right = NULL; ++ + if (priv->pipeline != NULL) + cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex)); + +@@ -493,7 +559,8 @@ set_cogl_texture (MetaShapedTexture *stex, + */ + void + meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, +- Pixmap pixmap) ++ Pixmap pixmap, ++ gboolean stereo) + { + MetaShapedTexturePrivate *priv; + +@@ -501,23 +568,44 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, + + priv = stex->priv; + +- if (priv->pixmap == pixmap) ++ if (priv->pixmap == pixmap && priv->stereo == stereo) + return; + + priv->pixmap = pixmap; ++ priv->stereo = stereo; + + if (pixmap != None) + { + CoglContext *ctx = + clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- set_cogl_texture (stex, cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL)); ++ CoglTexturePixmapX11 *texture; ++ if (priv->stereo) ++ texture = cogl_texture_pixmap_x11_new_left (ctx, pixmap, FALSE, NULL); ++ else ++ texture = cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL); ++ set_cogl_texture (stex, texture, stereo); + } + else +- set_cogl_texture (stex, NULL); ++ set_cogl_texture (stex, NULL, stereo); ++ ++ if (priv->stereo) ++ { ++ if (priv->paint_tower_right == NULL) ++ priv->paint_tower_right = meta_texture_tower_new (); ++ } ++ else ++ { ++ g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free); ++ } + + if (priv->create_mipmaps) +- meta_texture_tower_set_base_texture (priv->paint_tower, +- COGL_TEXTURE (priv->texture)); ++ { ++ meta_texture_tower_set_base_texture (priv->paint_tower, ++ COGL_TEXTURE (priv->texture)); ++ if (priv->stereo) ++ meta_texture_tower_set_base_texture (priv->paint_tower_right, ++ COGL_TEXTURE (priv->texture_right)); ++ } + } + + /** +diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h +index 90a9e35..914a8db 100644 +--- a/src/compositor/meta-window-actor-private.h ++++ b/src/compositor/meta-window-actor-private.h +@@ -66,4 +66,11 @@ void meta_window_actor_reset_visible_regions (MetaWindowActor *self); + void meta_window_actor_effect_completed (MetaWindowActor *actor, + gulong event); + ++void meta_window_actor_stereo_notify (MetaWindowActor *actor, ++ gboolean stereo_tree); ++ ++gboolean meta_window_actor_is_stereo (MetaWindowActor *actor); ++ ++void meta_window_actor_detach (MetaWindowActor *self); ++ + #endif /* META_WINDOW_ACTOR_PRIVATE_H */ +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 42d1257..1a11d0e 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -108,6 +108,7 @@ struct _MetaWindowActorPrivate + guint visible : 1; + guint mapped : 1; + guint argb32 : 1; ++ guint stereo : 1; + guint disposed : 1; + guint redecorating : 1; + +@@ -175,7 +176,6 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume); + + +-static void meta_window_actor_detach (MetaWindowActor *self); + static gboolean meta_window_actor_has_shadow (MetaWindowActor *self); + + static void meta_window_actor_handle_updates (MetaWindowActor *self); +@@ -362,6 +362,9 @@ meta_window_actor_constructed (GObject *object) + if (format && format->type == PictTypeDirect && format->direct.alphaMask) + priv->argb32 = TRUE; + ++ priv->stereo = meta_compositor_window_is_stereo (screen, xwindow); ++ meta_compositor_select_stereo_notify (screen, xwindow); ++ + if (!priv->actor) + { + priv->actor = meta_shaped_texture_new (); +@@ -1218,7 +1221,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self, + * when the window is unmapped or when we want to update to a new + * pixmap for a new size. + */ +-static void ++void + meta_window_actor_detach (MetaWindowActor *self) + { + MetaWindowActorPrivate *priv = self->priv; +@@ -1234,7 +1237,7 @@ meta_window_actor_detach (MetaWindowActor *self) + * pixmap, but it certainly doesn't work with current DRI/Mesa + */ + meta_shaped_texture_set_pixmap (META_SHAPED_TEXTURE (priv->actor), +- None); ++ None, FALSE); + cogl_flush(); + + XFreePixmap (xdisplay, priv->back_pixmap); +@@ -1823,7 +1826,7 @@ check_needs_pixmap (MetaWindowActor *self) + FALSE); + + meta_shaped_texture_set_pixmap (META_SHAPED_TEXTURE (priv->actor), +- priv->back_pixmap); ++ priv->back_pixmap, priv->stereo); + + texture = meta_shaped_texture_get_texture (META_SHAPED_TEXTURE (priv->actor)); + +@@ -2575,3 +2578,19 @@ meta_window_actor_set_updates_frozen (MetaWindowActor *self, + meta_window_actor_thaw (self); + } + } ++ ++void ++meta_window_actor_stereo_notify (MetaWindowActor *self, ++ gboolean stereo_tree) ++{ ++ MetaWindowActorPrivate *priv = self->priv; ++ ++ priv->stereo = stereo_tree; ++ meta_window_actor_detach (self); ++} ++ ++gboolean ++meta_window_actor_is_stereo (MetaWindowActor *self) ++{ ++ return self->priv->stereo; ++} +diff --git a/src/core/main.c b/src/core/main.c +index d5bde44..8092afc 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -444,6 +444,8 @@ meta_init (void) + + meta_restart_init (); + ++ meta_stereo_init (); ++ + /* + * Clutter can only be initialized after the UI. + */ +diff --git a/src/core/stereo.c b/src/core/stereo.c +new file mode 100644 +index 0000000..d16a210 +--- /dev/null ++++ b/src/core/stereo.c +@@ -0,0 +1,144 @@ ++/* -*- 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:stereo ++ * @short_description: Keep track of whether we are a stereo compositor ++ * ++ * With GLX, we need to use a different GL context for stereo and ++ * non-stereo support. Support for multiple GL contexts is unfinished ++ * in Cogl and entirely lacking in Clutter, so it's by far easier ++ * to just restart Mutter when we detect a stereo window. ++ * ++ * A property _MUTTER_ENABLE_STEREO is maintained on the root window ++ * to know whether we should initialize clutter for stereo or not. ++ * When the presence or absence of stereo windows mismatches the ++ * stereo-enabled state for a sufficiently long period of time, ++ * we restart Mutter. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include "ui.h" ++#include ++#include "display-private.h" ++#include "stereo.h" ++ ++static guint stereo_switch_id = 0; ++static gboolean stereo_enabled = FALSE; ++/* -1 so the first time meta_stereo_set_have_stereo_windows() is called ++ * we avoid the short-circuit and set up a timeout to restart ++ * if necessary */ ++static gboolean stereo_have_windows = (gboolean)-1; ++static gboolean stereo_restart = FALSE; ++ ++#define STEREO_ENABLE_WAIT 1000 ++#define STEREO_DISABLE_WAIT 5000 ++ ++void ++meta_stereo_init (void) ++{ ++ Display *xdisplay = meta_ui_get_display (); ++ Window root = DefaultRootWindow (xdisplay); ++ Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False); ++ Atom type; ++ int format; ++ unsigned long n_items, bytes_after; ++ guchar *data; ++ ++ XGetWindowProperty (xdisplay, root, atom_enable_stereo, ++ 0, 1, False, XA_INTEGER, ++ &type, &format, &n_items, &bytes_after, &data); ++ if (type == XA_INTEGER) ++ { ++ if (format == 32 && n_items == 1 && bytes_after == 0) ++ { ++ stereo_enabled = *(long *)data; ++ } ++ else ++ { ++ meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n"); ++ } ++ ++ XFree (data); ++ } ++ else if (type != None) ++ { ++ meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n"); ++ } ++ ++ meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s", ++ stereo_enabled ? "yes" : "no"); ++ clutter_x11_set_use_stereo_stage (stereo_enabled); ++} ++ ++static gboolean ++meta_stereo_switch (gpointer data) ++{ ++ stereo_switch_id = 0; ++ stereo_restart = TRUE; ++ ++ meta_restart (stereo_have_windows ? ++ _("Enabling stereo...") : ++ _("Disabling stereo...")); ++ ++ return FALSE; ++} ++ ++void ++meta_stereo_set_have_stereo_windows (gboolean have_windows) ++{ ++ have_windows = have_windows != FALSE; ++ ++ if (!stereo_restart && have_windows != stereo_have_windows) ++ { ++ MetaDisplay *display = meta_get_display (); ++ Display *xdisplay = meta_display_get_xdisplay (display); ++ Window root = DefaultRootWindow (xdisplay); ++ Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False); ++ long value; ++ ++ stereo_have_windows = have_windows; ++ ++ if (stereo_have_windows) ++ meta_verbose ("Detected stereo windows\n"); ++ else ++ meta_verbose ("No stereo windows detected\n"); ++ ++ value = stereo_have_windows; ++ XChangeProperty (xdisplay, root, ++ atom_enable_stereo, XA_INTEGER, 32, ++ PropModeReplace, (guchar *)&value, 1); ++ ++ if (stereo_switch_id != 0) ++ { ++ g_source_remove (stereo_switch_id); ++ stereo_switch_id = 0; ++ } ++ ++ if (stereo_have_windows != stereo_enabled) ++ stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT, ++ meta_stereo_switch, NULL); ++ } ++} +diff --git a/src/core/stereo.h b/src/core/stereo.h +new file mode 100644 +index 0000000..ccd1d70 +--- /dev/null ++++ b/src/core/stereo.h +@@ -0,0 +1,28 @@ ++/* -*- 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 . ++ */ ++ ++#ifndef META_STEREO_H ++#define META_STEREO_H ++ ++void meta_stereo_init (void); ++void meta_stereo_set_have_stereo_windows (gboolean have_windows); ++gboolean meta_stereo_is_restart (void); ++void meta_stereo_finish_restart (void); ++ ++#endif +diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h +index 28fb5f6..fcf33c3 100644 +--- a/src/meta/meta-shaped-texture.h ++++ b/src/meta/meta-shaped-texture.h +@@ -76,7 +76,8 @@ void meta_shaped_texture_update_area (MetaShapedTexture *stex, + int height); + + void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, +- Pixmap pixmap); ++ Pixmap pixmap, ++ gboolean stereo); + + CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex); + +-- +1.8.3.1 + diff --git a/SOURCES/0003-Fix-windows-walking-up-the-screen-on-restart.patch b/SOURCES/0003-Fix-windows-walking-up-the-screen-on-restart.patch new file mode 100644 index 0000000..ba6aa84 --- /dev/null +++ b/SOURCES/0003-Fix-windows-walking-up-the-screen-on-restart.patch @@ -0,0 +1,59 @@ +From e44f0f15e945a2132912d9763cfa3e9e326e0aea Mon Sep 17 00:00:00 2001 +From: "Owen W. Taylor" +Date: Tue, 8 Jul 2014 21:08:31 -0400 +Subject: [PATCH 3/4] Fix windows walking up the screen on restart + +When a window is initially created, we need to save it's user rect +after any adjustments for gravity. Otherwise, the next time the window is +queued for a resize, it will jump back to it's initial position. + +We did that for newly created windows, but on restart, when windows +were already placed, the logic skipped saving the position. Use an +explicit flag so that we always save the position for newly created +MetaWindows. +--- + src/core/constraints.h | 4 +++- + src/core/window.c | 5 +++-- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/core/constraints.h b/src/core/constraints.h +index 5fa1e4e..1a59724 100644 +--- a/src/core/constraints.h ++++ b/src/core/constraints.h +@@ -35,7 +35,9 @@ typedef enum + META_DO_GRAVITY_ADJUST = 1 << 1, + META_IS_USER_ACTION = 1 << 2, + META_IS_MOVE_ACTION = 1 << 3, +- META_IS_RESIZE_ACTION = 1 << 4 ++ META_IS_RESIZE_ACTION = 1 << 4, ++ META_FORCE_STATIC_GRAVITY = 1 << 5, ++ META_IS_INITIAL_RESIZE = 1 << 6 + } MetaMoveResizeFlags; + + void meta_window_constrain (MetaWindow *window, +diff --git a/src/core/window.c b/src/core/window.c +index 60347ef..8d221e3 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -1430,7 +1430,7 @@ meta_window_new_with_attrs (MetaDisplay *display, + * initial map is handled same as configure request + */ + flags = +- META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION; ++ META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION | META_IS_INITIAL_RESIZE; + if (!window->override_redirect) + meta_window_move_resize_internal (window, + flags, +@@ -5316,7 +5316,8 @@ meta_window_move_resize_internal (MetaWindow *window, + if (need_configure_notify) + send_configure_notify (window); + +- if (!window->placed && window->force_save_user_rect && !window->fullscreen) ++ if (((flags & META_IS_INITIAL_RESIZE) != 0 || !window->placed) && ++ window->force_save_user_rect && !window->fullscreen) + force_save_user_window_placement (window); + else if (is_user_action) + save_user_window_placement (window); +-- +1.9.3 + diff --git a/SOURCES/0004-Leave-windows-in-place-on-a-crash-respawn.patch b/SOURCES/0004-Leave-windows-in-place-on-a-crash-respawn.patch new file mode 100644 index 0000000..def0321 --- /dev/null +++ b/SOURCES/0004-Leave-windows-in-place-on-a-crash-respawn.patch @@ -0,0 +1,162 @@ +From 443608282901a16565ef01e70d3fb1f66beb14f5 Mon Sep 17 00:00:00 2001 +From: "Owen W. Taylor" +Date: Tue, 8 Jul 2014 21:35:47 -0400 +Subject: [PATCH 4/4] Leave windows in place on a crash respawn + +When Mutter is restarted and cleans up the window positions +cleanly, we need to add gravity adjustments. We can detect +this easily. + + - Windows started before the window manager (should add + add gravity adjustments) + - Replacing a different window manager (should add gravity + adjustments) + - A crash (no adjustments needed) + +In GNOME the first two cases shouldn't happen in normal +usage, so assume the third case (though that *shouldn't* +happen in normal usage either.) +--- + src/core/display.c | 4 ++-- + src/core/screen.c | 2 +- + src/core/window-private.h | 3 ++- + src/core/window.c | 33 ++++++++++++++++++++++++++------- + 4 files changed, 31 insertions(+), 11 deletions(-) + +diff --git a/src/core/display.c b/src/core/display.c +index 9ed0f6e..90888c5 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -2613,14 +2613,14 @@ event_callback (XEvent *event, + && meta_display_screen_for_root (display, event->xmap.event)) + { + window = meta_window_new (display, event->xmap.window, +- FALSE); ++ FALSE, FALSE); + } + break; + case MapRequest: + if (window == NULL) + { + window = meta_window_new (display, event->xmaprequest.window, +- FALSE); ++ FALSE, FALSE); + } + /* if frame was receiver it's some malicious send event or something */ + else if (!frame_was_receiver && window) +diff --git a/src/core/screen.c b/src/core/screen.c +index 6db3ea3..6d8d018 100644 +--- a/src/core/screen.c ++++ b/src/core/screen.c +@@ -1093,7 +1093,7 @@ meta_screen_manage_all_windows (MetaScreen *screen) + WindowInfo *info = list->data; + + meta_window_new_with_attrs (screen->display, info->xwindow, TRUE, +- META_COMP_EFFECT_NONE, ++ TRUE, + &info->attrs); + } + meta_stack_thaw (screen->stack); +diff --git a/src/core/window-private.h b/src/core/window-private.h +index e9f935f..cc77861 100644 +--- a/src/core/window-private.h ++++ b/src/core/window-private.h +@@ -481,11 +481,12 @@ struct _MetaWindowClass + + MetaWindow* meta_window_new (MetaDisplay *display, + Window xwindow, ++ gboolean managing_screen, + gboolean must_be_viewable); + MetaWindow* meta_window_new_with_attrs (MetaDisplay *display, + Window xwindow, ++ gboolean managing_screen, + gboolean must_be_viewable, +- MetaCompEffect effect, + XWindowAttributes *attrs); + void meta_window_unmanage (MetaWindow *window, + guint32 timestamp); +diff --git a/src/core/window.c b/src/core/window.c +index 8d221e3..8a290f3 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -40,6 +40,7 @@ + #include "keybindings-private.h" + #include "ui.h" + #include "place.h" ++#include + #include "session.h" + #include + #include "resizepopup.h" +@@ -659,6 +660,7 @@ maybe_leave_show_desktop_mode (MetaWindow *window) + MetaWindow* + meta_window_new (MetaDisplay *display, + Window xwindow, ++ gboolean managing_screen, + gboolean must_be_viewable) + { + XWindowAttributes attrs; +@@ -682,8 +684,8 @@ meta_window_new (MetaDisplay *display, + return NULL; + } + window = meta_window_new_with_attrs (display, xwindow, ++ managing_screen, + must_be_viewable, +- META_COMP_EFFECT_CREATE, + &attrs); + } + else +@@ -815,8 +817,8 @@ meta_window_should_attach_to_parent (MetaWindow *window) + MetaWindow* + meta_window_new_with_attrs (MetaDisplay *display, + Window xwindow, ++ gboolean managing_screen, + gboolean must_be_viewable, +- MetaCompEffect effect, + XWindowAttributes *attrs) + { + MetaWindow *window; +@@ -827,6 +829,8 @@ meta_window_new_with_attrs (MetaDisplay *display, + MetaMoveResizeFlags flags; + gboolean has_shape; + MetaScreen *screen; ++ MetaCompEffect effect = ++ managing_screen ? META_COMP_EFFECT_NONE : META_COMP_EFFECT_CREATE; + + g_assert (attrs != NULL); + +@@ -1425,12 +1429,27 @@ meta_window_new_with_attrs (MetaDisplay *display, + else + window->layer = META_LAYER_OVERRIDE_REDIRECT; /* otherwise set by MetaStack */ + +- /* Put our state back where it should be, +- * passing TRUE for is_configure_request, ICCCM says +- * initial map is handled same as configure request +- */ + flags = +- META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION | META_IS_INITIAL_RESIZE; ++ META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION | META_IS_INITIAL_RESIZE; ++ ++ /* ICCCM says initial map is handled same as configure request. When ++ * we are initially managing the screen, we distinguish two cases: ++ * ++ * Restart: in this case, we put the windows back to the unframed ++ * position before exiting, so we need to give them gravity ++ * adjustments. ++ * Something else: (perhaps a crash) if we didn't exit cleanly, then ++ * windows will be reparented by the X server so that the client ++ * origin stays the same, and no frame adjustment is needed. ++ * ++ * We don't have any way to distinguish replacing a different window ++ * manager from respawning on crash, so when we replace a different ++ * window manager, windows will shift onscreen, but this is expected ++ * to only happen in development. ++ */ ++ if (!managing_screen || meta_is_restart()) ++ flags |= META_IS_CONFIGURE_REQUEST; ++ + if (!window->override_redirect) + meta_window_move_resize_internal (window, + flags, +-- +1.9.3 + diff --git a/SOURCES/preserve-monitor.patch b/SOURCES/preserve-monitor.patch new file mode 100644 index 0000000..d75accc --- /dev/null +++ b/SOURCES/preserve-monitor.patch @@ -0,0 +1,255 @@ +From abedce08727b9993e0365c9b925bbcab9110feb1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 13 Jun 2014 22:28:26 +0200 +Subject: [PATCH 1/2] window: Keep track of preferred output + +Remember the last monitor a window was moved to by user action and +try to move it back on monitor changes; this should match user +expectations much better when a monitor is unplugged temporarily. + +https://bugzilla.gnome.org/show_bug.cgi?id=731760 +--- + src/core/window-private.h | 1 + + src/core/window.c | 47 ++++++++++++++++++++++++++++++++++------------- + 2 files changed, 35 insertions(+), 13 deletions(-) + +diff --git a/src/core/window-private.h b/src/core/window-private.h +index e9f935f..9a0c760 100644 +--- a/src/core/window-private.h ++++ b/src/core/window-private.h +@@ -148,6 +148,7 @@ struct _MetaWindow + * that to toggle between normal/tiled or maximized/tiled states. */ + guint saved_maximize : 1; + int tile_monitor_number; ++ XID preferred_output; + + /* Whether we're shaded */ + guint shaded : 1; +diff --git a/src/core/window.c b/src/core/window.c +index 60347ef..8b8fbe7 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -1198,6 +1198,7 @@ meta_window_new_with_attrs (MetaDisplay *display, + window->compositor_private = NULL; + + window->monitor = meta_screen_get_monitor_for_window (window->screen, window); ++ window->preferred_output = window->monitor->output; + + window->tile_match = NULL; + +@@ -4779,13 +4780,29 @@ meta_window_get_monitor (MetaWindow *window) + return window->monitor->number; + } + ++static MetaMonitorInfo * ++find_monitor_by_id (MetaWindow *window, ++ guint id) ++{ ++ int i; ++ ++ for (i = 0; i < window->screen->n_monitor_infos; i++) ++ { ++ MetaMonitorInfo *info = &window->screen->monitor_infos[i]; ++ ++ if (info->output != 0 && info->output == id) ++ return info; ++ } ++ ++ return NULL; ++} ++ + /* This is called when the monitor setup has changed. The window->monitor + * reference is still "valid", but refer to the previous monitor setup */ + void + meta_window_update_for_monitors_changed (MetaWindow *window) + { + const MetaMonitorInfo *old, *new; +- int i; + + if (window->type == META_WINDOW_DESKTOP) + return; +@@ -4798,20 +4815,16 @@ meta_window_update_for_monitors_changed (MetaWindow *window) + + old = window->monitor; + +- /* Start on primary */ +- new = &window->screen->monitor_infos[window->screen->primary_monitor_index]; ++ /* Try the preferred output first */ ++ new = find_monitor_by_id (window, window->preferred_output); + +- /* But, if we can find the old output on a new monitor, use that */ +- for (i = 0; i < window->screen->n_monitor_infos; i++) +- { +- MetaMonitorInfo *info = &window->screen->monitor_infos[i]; ++ /* Otherwise, try to find the old output on a new monitor */ ++ if (!new) ++ new = find_monitor_by_id (window, old->output); + +- if (info->output == old->output) +- { +- new = info; +- break; +- } +- } ++ /* Fall back to primary if everything else failed */ ++ if (!new) ++ new = &window->screen->monitor_infos[window->screen->primary_monitor_index]; + + if (window->tile_mode != META_TILE_NONE) + window->tile_monitor_number = new->number; +@@ -4934,6 +4947,7 @@ meta_window_move_resize_internal (MetaWindow *window, + int client_move_y; + MetaRectangle new_rect; + MetaRectangle old_rect; ++ guint old_output_id; + + g_return_if_fail (!window->override_redirect); + +@@ -5332,8 +5346,14 @@ meta_window_move_resize_internal (MetaWindow *window, + + meta_window_refresh_resize_popup (window); + ++ old_output_id = window->monitor->output; ++ + meta_window_update_monitor (window); + ++ if (old_output_id != window->monitor->output && ++ flags & META_IS_MOVE_ACTION && flags & META_IS_USER_ACTION) ++ window->preferred_output = window->monitor->output; ++ + /* Invariants leaving this function are: + * a) window->rect and frame->rect reflect the actual + * server-side size/pos of window->xwindow and frame->xwindow +@@ -5536,6 +5556,7 @@ meta_window_move_to_monitor (MetaWindow *window, + window->tile_monitor_number = monitor; + + meta_window_move_between_rects (window, &old_area, &new_area); ++ window->preferred_output = window->monitor->output; + } + + void +-- +2.1.0 + + +From 6d39e1a72f8914d6a021d801940ab8a6c13d16f7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 20 Jun 2014 18:06:40 +0200 +Subject: [PATCH 2/2] window: Add user_op parameter to update_monitor() + +When workspaces-only-on-primary is set and a window is moved back to the +primary, we also move it to the active workspace to avoid the confusion +of a visible window suddenly disappearing when crossing the monitor border. +However when the window is not actually moved by the user, preserving the +workspace makes more sense - we already do this in some cases (e.g. when +moving between primary monitors), but miss others (unplugging the previous +monitor); just add an explicit user_op parameter as used elsewhere to cover +all exceptions. + +https://bugzilla.gnome.org/show_bug.cgi?id=731760 +--- + src/core/window-private.h | 1 - + src/core/window.c | 30 ++++++++++++++---------------- + 2 files changed, 14 insertions(+), 17 deletions(-) + +diff --git a/src/core/window-private.h b/src/core/window-private.h +index 9a0c760..1a39a4b 100644 +--- a/src/core/window-private.h ++++ b/src/core/window-private.h +@@ -677,5 +677,4 @@ gboolean meta_window_can_tile_side_by_side (MetaWindow *window); + void meta_window_compute_tile_match (MetaWindow *window); + + gboolean meta_window_updates_are_frozen (MetaWindow *window); +- + #endif +diff --git a/src/core/window.c b/src/core/window.c +index 8b8fbe7..f376ea0 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -145,7 +145,8 @@ static void meta_window_move_between_rects (MetaWindow *window, + static void unmaximize_window_before_freeing (MetaWindow *window); + static void unminimize_window_and_all_transient_parents (MetaWindow *window); + +-static void meta_window_update_monitor (MetaWindow *window); ++static void meta_window_update_monitor (MetaWindow *window, ++ gboolean user_op); + + /* Idle handlers for the three queues (run with meta_later_add()). The + * "data" parameter in each case will be a GINT_TO_POINTER of the +@@ -4809,7 +4810,7 @@ meta_window_update_for_monitors_changed (MetaWindow *window) + + if (window->override_redirect) + { +- meta_window_update_monitor (window); ++ meta_window_update_monitor (window, FALSE); + return; + } + +@@ -4842,7 +4843,8 @@ meta_window_update_for_monitors_changed (MetaWindow *window) + } + + static void +-meta_window_update_monitor (MetaWindow *window) ++meta_window_update_monitor (MetaWindow *window, ++ gboolean user_op) + { + const MetaMonitorInfo *old; + +@@ -4852,22 +4854,17 @@ meta_window_update_monitor (MetaWindow *window) + { + meta_window_update_on_all_workspaces (window); + +- /* If workspaces only on primary and we moved back to primary, ensure that the +- * window is now in that workspace. We do this because while the window is on a +- * non-primary monitor it is always visible, so it would be very jarring if it +- * disappeared when it crossed the monitor border. ++ /* If workspaces only on primary and we moved back to primary due to a user action, ++ * ensure that the window is now in that workspace. We do this because while ++ * the window is on a non-primary monitor it is always visible, so it would be ++ * very jarring if it disappeared when it crossed the monitor border. + * The one time we want it to both change to the primary monitor and a non-active + * workspace is when dropping the window on some other workspace thumbnail directly. + * That should be handled by explicitly moving the window before changing the +- * workspace +- * Don't do this if old == NULL, because thats what happens when starting up, and +- * we don't want to move all windows around from a previous WM instance. Nor do +- * we want it when moving from one primary monitor to another (can happen during +- * screen reconfiguration. ++ * workspace. + */ +- if (meta_prefs_get_workspaces_only_on_primary () && ++ if (meta_prefs_get_workspaces_only_on_primary () && user_op && + meta_window_is_on_primary_monitor (window) && +- old != NULL && !old->is_primary && + window->screen->active_workspace != window->workspace) + meta_window_change_workspace (window, window->screen->active_workspace); + +@@ -4878,6 +4875,7 @@ meta_window_update_monitor (MetaWindow *window) + /* If we're changing monitors, we need to update the has_maximize_func flag, + * as the working area has changed. */ + recalc_window_features (window); ++ meta_window_queue (window, META_QUEUE_CALC_SHOWING); + } + } + +@@ -5348,7 +5346,7 @@ meta_window_move_resize_internal (MetaWindow *window, + + old_output_id = window->monitor->output; + +- meta_window_update_monitor (window); ++ meta_window_update_monitor (window, flags & META_IS_USER_ACTION); + + if (old_output_id != window->monitor->output && + flags & META_IS_MOVE_ACTION && flags & META_IS_USER_ACTION) +@@ -5672,7 +5670,7 @@ meta_window_configure_notify (MetaWindow *window, + window->rect.y = event->y; + window->rect.width = event->width; + window->rect.height = event->height; +- meta_window_update_monitor (window); ++ meta_window_update_monitor (window, FALSE); + + /* Whether an override-redirect window is considered fullscreen depends + * on its geometry. +-- +2.1.0 + diff --git a/SOURCES/vertical-monitor-layouts.patch b/SOURCES/vertical-monitor-layouts.patch new file mode 100644 index 0000000..ff9595f --- /dev/null +++ b/SOURCES/vertical-monitor-layouts.patch @@ -0,0 +1,244 @@ +From 13603e46a07f4ece309f5b2ab3196ad306b6008c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 6 Jun 2014 07:58:20 +0200 +Subject: [PATCH 1/3] boxes: Ignore struts that don't attach to the side they + claim + +Like the _NET_WM_STRUT/_NET_WM_STRUT_PARTIAL client properties, +_NET_WORKAREA is defined in terms of screen geometry rather than +taking individual monitors into account. However we do want to +allow system chrome to be attached to a monitor edge rather than +a screen edges under some circumstances. As not all clients can +be assumed to deal gracefully with the resulting workarea, use +those "struts" only internally for constraining windows, but +ignore them when exporting _NET_WORKAREA. + +https://bugzilla.gnome.org/show_bug.cgi?id=730527 +--- + src/core/boxes.c | 29 ++++++++++++++++++++++++++--- + 1 file changed, 26 insertions(+), 3 deletions(-) + +diff --git a/src/core/boxes.c b/src/core/boxes.c +index 3c4f444..1057528 100644 +--- a/src/core/boxes.c ++++ b/src/core/boxes.c +@@ -548,6 +548,26 @@ compare_rect_areas (gconstpointer a, gconstpointer b) + return b_area - a_area; /* positive ret value denotes b > a, ... */ + } + ++/* ... and another helper for get_minimal_spanning_set_for_region()... */ ++static gboolean ++check_strut_align (MetaStrut *strut, const MetaRectangle *rect) ++{ ++ /* Check whether @strut actually aligns to the side of @rect it claims */ ++ switch (strut->side) ++ { ++ case META_SIDE_TOP: ++ return BOX_TOP (strut->rect) <= BOX_TOP (*rect); ++ case META_SIDE_BOTTOM: ++ return BOX_BOTTOM (strut->rect) >= BOX_BOTTOM (*rect); ++ case META_SIDE_LEFT: ++ return BOX_LEFT (strut->rect) <= BOX_LEFT (*rect); ++ case META_SIDE_RIGHT: ++ return BOX_RIGHT (strut->rect) >= BOX_RIGHT (*rect); ++ default: ++ return FALSE; ++ } ++} ++ + /** + * meta_rectangle_get_minimal_spanning_set_for_region: + * @basic_rect: Input rectangle +@@ -631,8 +651,9 @@ meta_rectangle_get_minimal_spanning_set_for_region ( + + for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next) + { +- GList *rect_iter; +- MetaRectangle *strut_rect = &((MetaStrut*)strut_iter->data)->rect; ++ GList *rect_iter; ++ MetaStrut *strut = (MetaStrut*)strut_iter->data; ++ MetaRectangle *strut_rect = &strut->rect; + + tmp_list = ret; + ret = NULL; +@@ -640,7 +661,9 @@ meta_rectangle_get_minimal_spanning_set_for_region ( + while (rect_iter) + { + MetaRectangle *rect = (MetaRectangle*) rect_iter->data; +- if (!meta_rectangle_overlap (rect, strut_rect)) ++ ++ if (!meta_rectangle_overlap (strut_rect, rect) || ++ !check_strut_align (strut, basic_rect)) + ret = g_list_prepend (ret, rect); + else + { +-- +2.1.0 + + +From 147decb30f1539b18eb8aeb1e37914d9cccac016 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 12 Jun 2014 01:06:25 +0200 +Subject: [PATCH 2/3] testboxes: Update test cases + +Who cares? We do now ... + +https://bugzilla.gnome.org/show_bug.cgi?id=730527 +--- + src/core/testboxes.c | 42 ++++++++++++++++++------------------------ + 1 file changed, 18 insertions(+), 24 deletions(-) + +diff --git a/src/core/testboxes.c b/src/core/testboxes.c +index 128efac..44f70b1 100644 +--- a/src/core/testboxes.c ++++ b/src/core/testboxes.c +@@ -240,7 +240,7 @@ static GSList* + get_strut_list (int which) + { + GSList *ans; +- MetaDirection wc = 0; /* wc == who cares? ;-) */ ++ MetaSide wc = 0; /* wc == who cares? ;-) */ + + ans = NULL; + +@@ -250,32 +250,32 @@ get_strut_list (int which) + case 0: + break; + case 1: +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 400, 1160, 1600, 40, wc)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 400, 1160, 1600, 40, META_SIDE_BOTTOM)); + break; + case 2: +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 800, 1100, 400, 100, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 300, 1150, 150, 50, wc)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 800, 1100, 400, 100, META_SIDE_BOTTOM)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 300, 1150, 150, 50, META_SIDE_BOTTOM)); + break; + case 3: +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 800, 1100, 400, 100, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 300, 1150, 80, 50, wc)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 800, 1100, 400, 100, META_SIDE_LEFT)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 300, 1150, 80, 50, META_SIDE_BOTTOM)); + ans = g_slist_prepend (ans, new_meta_strut ( 700, 525, 200, 150, wc)); + break; + case 4: +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 800, 1200, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 800, 0, 1600, 20, wc)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 800, 1200, META_SIDE_LEFT)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 800, 0, 1600, 20, META_SIDE_TOP)); + break; + case 5: +- ans = g_slist_prepend (ans, new_meta_strut ( 800, 0, 1600, 20, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 800, 1200, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 800, 10, 800, 1200, wc)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 800, 0, 1600, 20, META_SIDE_TOP)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 800, 1200, META_SIDE_LEFT)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 800, 10, 800, 1200, META_SIDE_RIGHT)); + break; + case 6: +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 40, wc)); +- ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, wc)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 40, META_SIDE_TOP)); ++ ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); + break; + } + +@@ -625,15 +625,9 @@ test_regions_okay () + /*************************************************************/ + region = get_screen_region (3); + tmp = NULL; +- tmp = g_list_prepend (tmp, new_meta_rect ( 380, 675, 420, 525)); /* 220500 */ + tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 300, 1180)); /* 354000 */ +- tmp = g_list_prepend (tmp, new_meta_rect ( 380, 20, 320, 1180)); /* 377600 */ +- tmp = g_list_prepend (tmp, new_meta_rect ( 0, 675, 800, 475)); /* 380000 */ +- tmp = g_list_prepend (tmp, new_meta_rect (1200, 20, 400, 1180)); /* 472000 */ +- tmp = g_list_prepend (tmp, new_meta_rect ( 0, 675, 1600, 425)); /* 680000 */ +- tmp = g_list_prepend (tmp, new_meta_rect ( 900, 20, 700, 1080)); /* 756000 */ +- tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 700, 1130)); /* 791000 */ +- tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 1600, 505)); /* 808000 */ ++ tmp = g_list_prepend (tmp, new_meta_rect ( 380, 20, 1220, 1180)); /* 377600 */ ++ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 1600, 1130)); /* 791000 */ + #if 0 + printf ("Got to here...\n"); + char region_list[(RECT_LENGTH+2) * g_list_length (region)]; +-- +2.1.0 + + +From 0e1668ea9d4fb070480351021af44932ab04bcc2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 11 Jun 2014 02:16:13 +0200 +Subject: [PATCH 3/3] workspace: Extend builtin struts to screen edge when + possible + +Struts are defined in terms of screen edges, so expand the rectangles +we get via set_builtin_struts() accordingly. However we do want to +allow chrome on edges between monitors, in which case the expansion +would render an entire monitor unusable - don't expand the rectangles +in that case, which means we will only use them for constraining +windows but ignore them for the client-visible _NET_WORKAREA property. + +https://bugzilla.gnome.org/show_bug.cgi?id=730527 +--- + src/core/workspace.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/src/core/workspace.c b/src/core/workspace.c +index e5da6d2..9683baf 100644 +--- a/src/core/workspace.c ++++ b/src/core/workspace.c +@@ -1042,6 +1042,45 @@ void + meta_workspace_set_builtin_struts (MetaWorkspace *workspace, + GSList *struts) + { ++ MetaScreen *screen = workspace->screen; ++ GSList *l; ++ ++ for (l = struts; l; l = l->next) ++ { ++ MetaStrut *strut = l->data; ++ int idx = meta_screen_get_monitor_index_for_rect (screen, &strut->rect); ++ ++ switch (strut->side) ++ { ++ case META_SIDE_TOP: ++ if (meta_screen_get_monitor_neighbor (screen, idx, META_SCREEN_UP)) ++ continue; ++ ++ strut->rect.height += strut->rect.y; ++ strut->rect.y = 0; ++ break; ++ case META_SIDE_BOTTOM: ++ if (meta_screen_get_monitor_neighbor (screen, idx, META_SCREEN_DOWN)) ++ continue; ++ ++ strut->rect.height = screen->rect.height - strut->rect.y; ++ break; ++ case META_SIDE_LEFT: ++ if (meta_screen_get_monitor_neighbor (screen, idx, META_SCREEN_LEFT)) ++ continue; ++ ++ strut->rect.width += strut->rect.x; ++ strut->rect.x = 0; ++ break; ++ case META_SIDE_RIGHT: ++ if (meta_screen_get_monitor_neighbor (screen, idx, META_SCREEN_RIGHT)) ++ continue; ++ ++ strut->rect.width = screen->rect.width - strut->rect.x; ++ break; ++ } ++ } ++ + /* Reordering doesn't actually matter, so we don't catch all + * no-impact changes, but this is just a (possibly unnecessary + * anyways) optimization */ +-- +2.1.0 + diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec index 1338c8a..dbd0edb 100644 --- a/SPECS/mutter.spec +++ b/SPECS/mutter.spec @@ -1,6 +1,6 @@ Name: mutter Version: 3.8.4 -Release: 10%{?dist} +Release: 16%{?dist} Summary: Window and compositing manager based on Clutter Group: User Interface/Desktops @@ -11,6 +11,7 @@ Source0: http://download.gnome.org/sources/%{name}/3.8/%{name}-%{version}. Patch0: 0001-shaped-texture-Use-nearest-pixel-interpolation-if-th.patch # https://bugzilla.gnome.org/show_bug.cgi?id=719669 Patch1: MetaWindowGroup-fix-paint-volume.patch +Patch2: 0001-window-Fix-delayed-mouse-mode-on-X.patch Patch10: 0001-background-Allow-using-sliced-textures-for-file-base.patch @@ -23,8 +24,22 @@ Patch40: 0001-background-don-t-save-pixbuf-in-user-data.patch Patch41: 0001-workspace-Add-missing-chain-up-for-finalize.patch Patch50: deal-more-gracefully-with-oversized-windows.patch +Patch51: vertical-monitor-layouts.patch +Patch52: preserve-monitor.patch -BuildRequires: clutter-devel >= 1.13.5 +Patch61: 0001-Add-a-framework-for-restarting-the-compositor-with-n.patch +Patch62: 0002-Add-support-for-quad-buffer-stereo.patch +Patch63: 0003-Fix-windows-walking-up-the-screen-on-restart.patch +Patch64: 0004-Leave-windows-in-place-on-a-crash-respawn.patch + +# Backport: https://bugzilla.gnome.org/show_bug.cgi?id=734400 +Patch65: 0001-MetaTextureTower-actually-mark-revalidated-levels-as.patch + +%define clutter_version 1.14.4-10 +%define cogl_version 1.14.0-6 + +BuildRequires: clutter-devel >= %{clutter_version} +BuildRequires: cogl-devel >= %{cogl_version} BuildRequires: pango-devel BuildRequires: startup-notification-devel BuildRequires: gtk3-devel >= 3.3.3 @@ -50,6 +65,8 @@ BuildRequires: gsettings-desktop-schemas-devel # an ABI change. Conflicts: gnome-shell < 3.7.2 +Requires: cogl >= %{cogl_version} +Requires: clutter >= %{clutter_version} Requires: control-center-filesystem Requires: startup-notification Requires: dbus-x11 @@ -81,12 +98,22 @@ utilities for testing Metacity/Mutter themes. %setup -q %patch0 -p1 -b .software-rendering-performance %patch1 -p1 -b .window-group-paint-volume +%patch2 -p1 -b .fix-delayed-mouse-mode %patch10 -p1 -b .sliced-backgrounds %patch20 -p2 -b .translation-updates %patch30 -p1 -b .minimal-touch-support %patch40 -p1 -b .dont-save-pixbuf-in-userdata %patch41 -p1 -b .plug-minor-memory-leak %patch50 -p1 -b .deal-with-oversized-windows +%patch51 -p1 -b .improve-vertical-monitor-layouts +%patch52 -p1 -b .preserve-monitor + +%patch61 -p1 -b .stereo1 +%patch62 -p1 -b .stereo2 +%patch63 -p1 -b .stereo3 +%patch64 -p1 -b .stereo4 + +%patch65 -p1 -b .texture-tower %build (if ! test -x configure; then NOCONFIGURE=1 ./autogen.sh; fi; @@ -135,6 +162,7 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : %doc %{_mandir}/man1/mutter-message.1.gz %{_bindir}/mutter %{_bindir}/mutter-message +%{_libexecdir}/mutter-restart-helper %{_datadir}/applications/*.desktop %{_datadir}/gnome/wm-properties/mutter-wm.desktop %{_datadir}/mutter @@ -157,6 +185,27 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : %exclude %{_datadir}/gtk-doc %changelog +* Wed Jan 14 2015 Florian Müllner - 3.8.4.16 +- Fix window placement regression + Resolves: rhbz#1153641 + +* Thu Nov 13 2014 Florian Müllner - 3.8.4-15 +- Fix delayed mouse mode + Resolves: rhbz#1149585 + +* Thu Oct 09 2014 Florian Müllner - 3.8.4-14 +- Preserve window placement on monitor changes + Resolves: rhbz#1126754 + +* Thu Oct 09 2014 Florian Müllner - 3.8.4-13 +- Improve handling of vertical monitor layouts + Resolves: rhbz#1108322 + +* Thu Jul 17 2014 Owen Taylor 3.8.4-13 +- Add patches for quadbuffer stereo suppport + Fix a bad performance problem drawing window thumbnails + Resolves: rhbz#861507 + * Tue Mar 11 2014 Florian Müllner - 3.8.4-10 - Fix crash when encountering over-sized windows Resolves: #1027832