[metacity] window: make determination of attached dialog windows more consistent



commit ca4b237b5b075f345720c2608d6185134ccfe30c
Author: Dan Winship <danw gnome org>
Date:   Tue Apr 5 10:14:52 2011 -0400

    window: make determination of attached dialog windows more consistent
    
    Different bits of code were using slightly different checks to test
    whether a window was an attached dialog. Add a new
    meta_window_is_attached_dialog(), and use that everywhere.
    
    Also, freeze the is-attached status when the window is first shown,
    rather than recomputing it each time the caller asks, since this could
    cause problems if a window changes its type after it has already been
    attached, etc. However, if an attached window's parent is destroyed,
    or an attached window changes its transient-for, then fix things up by
    destroying the old MetaWindow and creating a new one (causing
    compositor unmap and map events to be fired off, allowing the display
    of the window to be fixed up).
    
    Remove some code in display.c that tried to fix existing windows if
    the gconf setting changed, but which didn't actually do anything (at
    least under gnome-shell). However, if 654643 was fixed then the new
    behavior with this patch would be that changing the gconf setting
    would affect new dialogs, but not existing ones.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=646761

 src/core/constraints.c    |    4 +-
 src/core/display.c        |   30 +------------
 src/core/window-private.h |    5 ++
 src/core/window-props.c   |   73 +++++++++++++++++++++-----------
 src/core/window.c         |  104 ++++++++++++++++++++++++++++++++++++---------
 src/include/window.h      |    1 +
 6 files changed, 140 insertions(+), 77 deletions(-)
---
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 0fabaf1..ec80988 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -824,9 +824,7 @@ constrain_modal_dialog (MetaWindow         *window,
   MetaWindow *parent = meta_window_get_transient_for (window);
   gboolean constraint_already_satisfied;
 
-  if (!meta_prefs_get_attach_modal_dialogs ())
-    return TRUE;
-  if (window->type != META_WINDOW_MODAL_DIALOG || !parent)
+  if (!meta_window_is_attached_dialog (window))
     return TRUE;
 
   x = parent->rect.x + (parent->rect.width / 2 - info->current.width / 2);
diff --git a/src/core/display.c b/src/core/display.c
index 72d55a8..e82d38e 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -3311,8 +3311,7 @@ meta_display_begin_grab_op (MetaDisplay *display,
   /* If window is a modal dialog attached to its parent,
    * grab the parent instead for moving.
    */
-  if (meta_prefs_get_attach_modal_dialogs () &&
-      window->type == META_WINDOW_MODAL_DIALOG &&
+  if (window && meta_window_is_attached_dialog (window) &&
       meta_grab_op_is_moving (op))
     grab_window = meta_window_get_transient_for (window);
 
@@ -5033,33 +5032,6 @@ prefs_changed_callback (MetaPreference pref,
     {
       update_compositor (display, TRUE);
     }
-  else if (pref == META_PREF_ATTACH_MODAL_DIALOGS)
-    {
-      GSList *windows;
-      GSList *tmp;
-
-      windows = meta_display_list_windows (display, META_LIST_DEFAULT);
-
-      for (tmp = windows; tmp != NULL; tmp = tmp->next)
-        {
-          MetaWindow *w = tmp->data;
-          MetaWindow *parent = meta_window_get_transient_for (w);
-          meta_window_recalc_features (w);
-
-          if (w->type == META_WINDOW_MODAL_DIALOG && parent)
-            {
-              int x, y;
-              /* Forcing a call to move_resize() does two things: first, it handles
-               * resizing the dialog frame window to the correct size when we remove
-               * or add the decorations. Second, it will take care of positioning the
-               * dialog as "attached" to the parent when we turn the preference on
-               * via the constrain_modal_dialog() constraint.
-               **/
-              meta_window_get_position (w, &x, &y);
-              meta_window_move (w, FALSE, x, y);
-            }
-        }
-    }
 }
 
 void
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 4510535..36ba47b 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -323,6 +323,9 @@ struct _MetaWindow
   /* if TRUE, application is buggy and SYNC resizing is turned off */
   guint disable_sync : 1;
 
+  /* if TRUE, window is attached to its parent */
+  guint attached : 1;
+
   /* if non-NULL, the bounds of the window frame */
   cairo_region_t *frame_bounds;
 
@@ -691,4 +694,6 @@ void meta_window_configure_notify (MetaWindow      *window,
 void meta_window_propagate_focus_appearance (MetaWindow *window,
                                              gboolean    focused);
 
+gboolean meta_window_should_attach_to_parent (MetaWindow *window);
+
 #endif
diff --git a/src/core/window-props.c b/src/core/window-props.c
index 75058f6..38f8c97 100644
--- a/src/core/window-props.c
+++ b/src/core/window-props.c
@@ -1533,42 +1533,46 @@ reload_transient_for (MetaWindow    *window,
                       gboolean       initial)
 {
   MetaWindow *parent = NULL;
-
-  if (meta_window_appears_focused (window) && window->xtransient_for != None)
-    meta_window_propagate_focus_appearance (window, FALSE);
-
-  window->xtransient_for = None;
+  Window transient_for, old_transient_for;
 
   if (value->type != META_PROP_VALUE_INVALID)
-    window->xtransient_for = value->v.xwindow;
-
-  /* Make sure transient_for is valid */
-  if (window->xtransient_for != None)
     {
-      parent = meta_display_lookup_x_window (window->display,
-                                             window->xtransient_for);
+      transient_for = value->v.xwindow;
+
+      parent = meta_display_lookup_x_window (window->display, transient_for);
       if (!parent)
         {
           meta_warning ("Invalid WM_TRANSIENT_FOR window 0x%lx specified for %s.\n",
-                        window->xtransient_for, window->desc);
-          window->xtransient_for = None;
+                        transient_for, window->desc);
+          transient_for = None;
         }
-    }
 
-  /* Make sure there is not a loop */
-  while (parent)
-    {
-      if (parent == window)
+      /* Make sure there is not a loop */
+      while (parent)
         {
-          meta_warning ("WM_TRANSIENT_FOR window 0x%lx for %s would create loop.\n",
-                        window->xtransient_for, window->desc);
-          window->xtransient_for = None;
-          break;
-        }
+          if (parent == window)
+            {
+              meta_warning ("WM_TRANSIENT_FOR window 0x%lx for %s would create loop.\n",
+                            transient_for, window->desc);
+              transient_for = None;
+              break;
+            }
 
-      parent = meta_display_lookup_x_window (parent->display,
-                                             parent->xtransient_for);
+          parent = meta_display_lookup_x_window (parent->display,
+                                                 parent->xtransient_for);
+        }
     }
+  else
+    transient_for = None;
+
+  if (transient_for == window->xtransient_for)
+    return;
+
+  if (meta_window_appears_focused (window) && window->xtransient_for != None)
+    meta_window_propagate_focus_appearance (window, FALSE);
+
+  old_transient_for = window->xtransient_for;
+  window->xtransient_for = transient_for;
 
   window->transient_parent_is_root_window =
     window->xtransient_for == window->screen->xroot;
@@ -1582,6 +1586,25 @@ reload_transient_for (MetaWindow    *window,
   /* may now be a dialog */
   meta_window_recalc_window_type (window);
 
+  if (!window->constructing)
+    {
+      /* If the window attaches, detaches, or changes attached
+       * parents, we need to destroy the MetaWindow and let a new one
+       * be created (which happens as a side effect of
+       * meta_window_unmanage()). The condition below is correct
+       * because we know window->xtransient_for has changed.
+       */
+      if (window->attached || meta_window_should_attach_to_parent (window))
+        {
+          guint32 timestamp;
+
+          window->xtransient_for = old_transient_for;
+          timestamp = meta_display_get_current_time_roundtrip (window->display);
+          meta_window_free (window, timestamp);
+          return;
+        }
+    }
+
   /* update stacking constraints */
   if (!window->override_redirect)
     meta_stack_update_transient (window->screen->stack, window);
diff --git a/src/core/window.c b/src/core/window.c
index 65305cd..83caa46 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -226,6 +226,22 @@ is_our_xwindow (MetaDisplay       *display,
   return FALSE;
 }
 
+gboolean
+meta_window_should_attach_to_parent (MetaWindow *window)
+{
+  MetaWindow *parent;
+
+  if (!meta_prefs_get_attach_modal_dialogs () ||
+      window->type != META_WINDOW_MODAL_DIALOG)
+    return FALSE;
+
+  parent = meta_window_get_transient_for (window);
+  if (!parent)
+    return FALSE;
+
+  return TRUE;
+}
+
 MetaWindow*
 meta_window_new (MetaDisplay *display,
                  Window       xwindow,
@@ -496,6 +512,7 @@ meta_window_new (MetaDisplay *display,
   window->shaken_loose = FALSE;
   window->have_focus_click_grab = FALSE;
   window->disable_sync = FALSE;
+  window->attached = FALSE;
   window->frame_bounds = NULL;
 
   window->unmaps_pending = 0;
@@ -640,6 +657,10 @@ meta_window_new (MetaDisplay *display,
         meta_display_get_current_time_roundtrip (window->display);
   }
 
+  window->attached = meta_window_should_attach_to_parent (window);
+  if (window->attached)
+    recalc_window_features (window);
+
   if (window->decorated)
     meta_window_ensure_frame (window);
 
@@ -975,6 +996,24 @@ meta_window_apply_session_info (MetaWindow *window,
     }
 }
 
+static gboolean
+detach_foreach_func (MetaWindow *window,
+                     void       *data)
+{
+  GList **children = data;
+  MetaWindow *parent;
+
+  if (window->attached)
+    {
+      /* Only return the immediate children of the window being unmanaged */
+      parent = meta_window_get_transient_for (window);
+      if (parent->unmanaging)
+        *children = g_list_prepend (*children, window);
+    }
+
+  return TRUE;
+}
+
 void
 meta_window_free (MetaWindow  *window,
                   guint32      timestamp)
@@ -998,6 +1037,21 @@ meta_window_free (MetaWindow  *window,
 
   window->unmanaging = TRUE;
 
+  if (meta_prefs_get_attach_modal_dialogs ())
+    {
+      GList *attached_children = NULL, *iter;
+
+      /* Detach any attached dialogs by unmapping and letting them
+       * be remapped after @window is destroyed.
+       */
+      meta_window_foreach_transient (window,
+                                     detach_foreach_func,
+                                     &attached_children);
+      for (iter = attached_children; iter; iter = iter->next)
+        meta_window_free (iter->data, timestamp);
+      g_list_free (attached_children);
+    }
+
   if (window->fullscreen)
     {
       MetaGroup *group;
@@ -1151,8 +1205,12 @@ meta_window_free (MetaWindow  *window,
           meta_error_trap_pop (window->display);
         }
 
-      /* And we need to be sure the window is mapped so other WMs
-       * know that it isn't Withdrawn
+      /* If we're unmanaging a window that is not withdrawn, then
+       * either (a) mutter is exiting, in which case we need to map
+       * the window so the next WM will know that it's not Withdrawn,
+       * or (b) we want to create a new MetaWindow to replace the
+       * current one, which will happen automatically if we re-map
+       * the X Window.
        */
       meta_error_trap_push (window->display);
       XMapWindow (window->display->xdisplay,
@@ -3347,12 +3405,10 @@ send_sync_request (MetaWindow *window)
 }
 
 static gboolean
-move_attached_dialog (MetaWindow *window,
-                      void       *data)
+maybe_move_attached_dialog (MetaWindow *window,
+                            void       *data)
 {
-  MetaWindow *parent = meta_window_get_transient_for (window);
-
-  if (window->type == META_WINDOW_MODAL_DIALOG && parent)
+  if (meta_window_is_attached_dialog (window))
     /* It ignores x,y for such a dialog */
     meta_window_move (window, FALSE, 0, 0);
 
@@ -3820,8 +3876,7 @@ meta_window_move_resize_internal (MetaWindow          *window,
       window->frame_bounds = NULL;
     }
 
-  if (meta_prefs_get_attach_modal_dialogs ())
-    meta_window_foreach_transient (window, move_attached_dialog, NULL);
+  meta_window_foreach_transient (window, maybe_move_attached_dialog, NULL);
 }
 
 void
@@ -5644,14 +5699,11 @@ meta_window_propagate_focus_appearance (MetaWindow *window,
 {
   MetaWindow *child, *parent, *focus_window;
 
-  if (!meta_prefs_get_attach_modal_dialogs ())
-    return;
-
   focus_window = window->display->focus_window;
 
   child = window;
   parent = meta_window_get_transient_for (child);
-  while (parent && (!focused || child->type == META_WINDOW_MODAL_DIALOG))
+  while (parent && (!focused || meta_window_is_attached_dialog (child)))
     {
       gboolean child_focus_state_changed;
 
@@ -6690,10 +6742,8 @@ recalc_window_features (MetaWindow *window)
   if (window->type == META_WINDOW_TOOLBAR)
     window->decorated = FALSE;
 
-  if (window->type == META_WINDOW_MODAL_DIALOG && meta_prefs_get_attach_modal_dialogs ())
-    {
-      window->border_only = TRUE;
-    }
+  if (meta_window_is_attached_dialog (window))
+    window->border_only = TRUE;
 
   if (window->type == META_WINDOW_DESKTOP ||
       window->type == META_WINDOW_DOCK ||
@@ -7594,9 +7644,7 @@ update_resize (MetaWindow *window,
    * size changes apply to both sides, so that the dialog
    * remains centered to the parent.
    */
-  if (window->type == META_WINDOW_MODAL_DIALOG &&
-      meta_prefs_get_attach_modal_dialogs () &&
-      meta_window_get_transient_for (window) != NULL)
+  if (meta_window_is_attached_dialog (window))
     dx *= 2;
 
   new_w = window->display->grab_anchor_window_pos.width;
@@ -8916,3 +8964,19 @@ meta_window_get_frame_bounds (MetaWindow *window)
 
   return window->frame_bounds;
 }
+
+/**
+ * meta_window_is_attached_dialog:
+ * @window: a #MetaWindow
+ *
+ * Tests if @window is should be attached to its parent window.
+ * (If the "attach_modal_dialogs" option is not enabled, this will
+ * always return %FALSE.)
+ *
+ * Return value: whether @window should be attached to its parent
+ */
+gboolean
+meta_window_is_attached_dialog (MetaWindow *window)
+{
+  return window->attached;
+}
diff --git a/src/include/window.h b/src/include/window.h
index f82c444..be3fbdc 100644
--- a/src/include/window.h
+++ b/src/include/window.h
@@ -36,6 +36,7 @@ Window meta_window_get_xwindow (MetaWindow *window);
 MetaWindow *meta_window_get_transient_for (MetaWindow *window);
 gboolean meta_window_is_fullscreen (MetaWindow *window);
 gboolean meta_window_is_maximized (MetaWindow *window);
+gboolean meta_window_is_attached_dialog (MetaWindow *window);
 
 cairo_region_t *meta_window_get_frame_bounds (MetaWindow *window);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]