[mutter] window: Unmanage rule placed window if ending up outside of parent



commit b4f15696407f06438265af03c4e88e4f7739d5af
Author: Jonas Ådahl <jadahl gmail com>
Date:   Thu Oct 18 16:24:18 2018 +0200

    window: Unmanage rule placed window if ending up outside of parent
    
    If a client maps a persistent popup with a placement rule, then resizes
    the parent window so that the popup ends up outside of the parent,
    unmanage the popup and log a warning about the client being buggy.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/496

 src/core/constraints.c    | 30 ++++++++++++++++++++++++++++++
 src/core/window-private.h |  3 +++
 src/core/window.c         | 29 +++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+)
---
diff --git a/src/core/constraints.c b/src/core/constraints.c
index f743bae5b..67fb92215 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -145,6 +145,8 @@ typedef struct
    */
   GList  *usable_screen_region;
   GList  *usable_monitor_region;
+
+  gboolean should_unmanage;
 } ConstraintInfo;
 
 static gboolean do_screen_and_monitor_relative_constraints (MetaWindow     *window,
@@ -253,6 +255,14 @@ do_all_constraints (MetaWindow         *window,
       satisfied = satisfied &&
                   (*constraint->func) (window, info, priority, check_only);
 
+      if (info->should_unmanage)
+        {
+          meta_topic (META_DEBUG_GEOMETRY,
+                      "constraint %s wants to unmanage window.\n",
+                      constraint->name);
+          return TRUE;
+        }
+
       if (!check_only)
         {
           /* Log how the constraint modified the position */
@@ -312,6 +322,12 @@ meta_window_constrain (MetaWindow          *window,
      */
     satisfied = do_all_constraints (window, &info, priority, check_only);
 
+    if (info.should_unmanage)
+      {
+        meta_window_unmanage_on_idle (window);
+        return;
+      }
+
     /* Drop the least important constraints if we can't satisfy them all */
     priority++;
   }
@@ -421,6 +437,8 @@ setup_constraint_info (ConstraintInfo      *info,
   info->usable_monitor_region =
     meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor);
 
+  info->should_unmanage = FALSE;
+
   /* Log all this information for debugging */
   meta_topic (META_DEBUG_GEOMETRY,
               "Setting up constraint info:\n"
@@ -802,6 +820,8 @@ constrain_custom_rule (MetaWindow         *window,
 
   if (window->placement_rule_constrained)
     {
+      MetaRectangle parent_buffer_rect;
+
       parent = meta_window_get_transient_for (window);
       meta_window_get_frame_rect (parent, &parent_rect);
       info->current.x =
@@ -809,6 +829,16 @@ constrain_custom_rule (MetaWindow         *window,
       info->current.y =
         parent_rect.y + window->constrained_placement_rule_offset_y;
 
+      meta_window_get_buffer_rect (parent, &parent_buffer_rect);
+      if (!meta_rectangle_overlap (&info->current, &parent_buffer_rect) &&
+          !meta_rectangle_is_adjacent_to (&info->current, &parent_buffer_rect))
+        {
+          g_warning ("Buggy client caused popup to be placed outside of parent "
+                     "window");
+          info->should_unmanage = TRUE;
+          return TRUE;
+        }
+
       return TRUE;
     }
 
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 9fe5bc526..a6cebb4fb 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -527,6 +527,8 @@ struct _MetaWindow
   gboolean placement_rule_constrained;
   int constrained_placement_rule_offset_x;
   int constrained_placement_rule_offset_y;
+
+  guint unmanage_idle_id;
 };
 
 struct _MetaWindowClass
@@ -610,6 +612,7 @@ MetaWindow * _meta_window_shared_new       (MetaDisplay         *display,
 
 void        meta_window_unmanage           (MetaWindow  *window,
                                             guint32      timestamp);
+void        meta_window_unmanage_on_idle   (MetaWindow *window);
 void        meta_window_queue              (MetaWindow  *window,
                                             guint queuebits);
 void        meta_window_tile               (MetaWindow        *window,
diff --git a/src/core/window.c b/src/core/window.c
index b85db6ee3..d2c24506b 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -1424,6 +1424,9 @@ meta_window_unmanage (MetaWindow  *window,
   meta_verbose ("Unmanaging %s\n", window->desc);
   window->unmanaging = TRUE;
 
+  if (window->unmanage_idle_id)
+    g_source_remove (window->unmanage_idle_id);
+
 #ifdef HAVE_WAYLAND
   /* This needs to happen for both Wayland and XWayland clients,
    * so it can't be in MetaWindowWayland. */
@@ -1595,6 +1598,32 @@ meta_window_unmanage (MetaWindow  *window,
   g_object_unref (window);
 }
 
+static gboolean
+unmanage_window_idle_callback (gpointer user_data)
+{
+  MetaWindow *window = META_WINDOW (user_data);
+  uint32_t timestamp;
+
+  window->unmanage_idle_id = 0;
+
+  timestamp = meta_display_get_current_time_roundtrip (window->display);
+  meta_window_unmanage (window, timestamp);
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+meta_window_unmanage_on_idle (MetaWindow *window)
+{
+  if (window->unmanage_idle_id)
+    return;
+
+  window->unmanage_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+                                              unmanage_window_idle_callback,
+                                              window,
+                                              NULL);
+}
+
 static void
 set_wm_state (MetaWindow *window)
 {


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