Blob Blame History Raw
From 83b7b17b64b3e42a87f524ac3504be81789ec0fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Fri, 24 Aug 2018 12:56:31 +0200
Subject: [PATCH 2/2] window: Force update monitor on hot plugs

Commit a3da4b8d5bd217c0262fd9361036877d155a300f changed updating of
window monitors to always use take affect when it was done from a
non-user operation. This could cause feed back loops when a non-user
driven operation would trigger the changing of a monitor, which itself
would trigger changing of the monitor again due to a window scale
change.

The reason for the change, was that when the window monitor changed due
to a hot plug, if it didn't actually change, eventually the window
monitor pointer would be pointing to freed memory.

Instead of force updating the monitor on all non-user operations, just
do it on hot plugs. This allows for the feedback loop preventing logic
to still do what its supposed to do, without risking dangling pointers
on hot plugs.

Related: https://gitlab.gnome.org/GNOME/mutter/issues/189
Closes: https://gitlab.gnome.org/GNOME/mutter/issues/192

(cherry picked from commit 8d3e053059cd202e740076caedfc8e3ac149066a)
---
 src/core/window-private.h | 1 +
 src/core/window.c         | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/core/window-private.h b/src/core/window-private.h
index bed2fc7f6..8552829b2 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -98,60 +98,61 @@ typedef enum
   META_PLACEMENT_GRAVITY_TOP    = 1 << 0,
   META_PLACEMENT_GRAVITY_BOTTOM = 1 << 1,
   META_PLACEMENT_GRAVITY_LEFT   = 1 << 2,
   META_PLACEMENT_GRAVITY_RIGHT  = 1 << 3,
 } MetaPlacementGravity;
 
 typedef enum
 {
   META_PLACEMENT_ANCHOR_NONE   = 0,
   META_PLACEMENT_ANCHOR_TOP    = 1 << 0,
   META_PLACEMENT_ANCHOR_BOTTOM = 1 << 1,
   META_PLACEMENT_ANCHOR_LEFT   = 1 << 2,
   META_PLACEMENT_ANCHOR_RIGHT  = 1 << 3,
 } MetaPlacementAnchor;
 
 typedef enum
 {
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_NONE     = 0,
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X  = 1 << 0,
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y  = 1 << 1,
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X   = 1 << 2,
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y   = 1 << 3,
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X = 1 << 4,
   META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 1 << 5,
 } MetaPlacementConstraintAdjustment;
 
 typedef enum _MetaWindowUpdateMonitorFlags
 {
   META_WINDOW_UPDATE_MONITOR_FLAGS_NONE = 0,
   META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP = 1 << 0,
+  META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE = 1 << 1,
 } MetaWindowUpdateMonitorFlags;
 
 typedef struct _MetaPlacementRule
 {
   MetaRectangle anchor_rect;
   MetaPlacementGravity gravity;
   MetaPlacementAnchor anchor;
   MetaPlacementConstraintAdjustment constraint_adjustment;
   int offset_x;
   int offset_y;
   int width;
   int height;
 } MetaPlacementRule;
 
 typedef enum
 {
   META_EDGE_CONSTRAINT_NONE    = 0,
   META_EDGE_CONSTRAINT_WINDOW  = 1,
   META_EDGE_CONSTRAINT_MONITOR = 2,
 } MetaEdgeConstraint;
 
 struct _MetaWindow
 {
   GObject parent_instance;
 
   MetaDisplay *display;
   MetaScreen *screen;
   guint64 stamp;
   MetaLogicalMonitor *monitor;
   MetaWorkspace *workspace;
diff --git a/src/core/window.c b/src/core/window.c
index ad4059704..07f826bb5 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -3776,97 +3776,97 @@ find_monitor_by_winsys_id (MetaWindow *window,
   logical_monitors =
     meta_monitor_manager_get_logical_monitors (monitor_manager);
 
   for (l = logical_monitors; l; l = l->next)
     {
       MetaLogicalMonitor *logical_monitor = l->data;
 
       if (logical_monitor->winsys_id == winsys_id)
         return logical_monitor;
     }
 
   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)
 {
   MetaBackend *backend = meta_get_backend ();
   MetaMonitorManager *monitor_manager =
     meta_backend_get_monitor_manager (backend);
   const MetaLogicalMonitor *old, *new;
 
   if (meta_window_has_fullscreen_monitors (window))
     meta_window_clear_fullscreen_monitors (window);
 
   if (window->override_redirect || window->type == META_WINDOW_DESKTOP)
     {
       meta_window_update_monitor (window,
-                                  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
+                                  META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE);
       return;
     }
 
   old = window->monitor;
 
   /* Try the preferred output first */
   new = find_monitor_by_winsys_id (window, window->preferred_output_winsys_id);
 
   /* Otherwise, try to find the old output on a new monitor */
   if (old && !new)
     new = find_monitor_by_winsys_id (window, old->winsys_id);
 
   /* Fall back to primary if everything else failed */
   if (!new)
     new = meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
 
   if (new && old)
     {
       if (window->tile_mode != META_TILE_NONE)
         window->tile_monitor_number = new->number;
 
       /* This will eventually reach meta_window_update_monitor that
        * will send leave/enter-monitor events. The old != new monitor
        * check will always fail (due to the new logical_monitors set) so
        * we will always send the events, even if the new and old monitor
        * index is the same. That is right, since the enumeration of the
        * monitors changed and the same index could be refereing
        * to a different monitor. */
       meta_window_move_between_rects (window,
                                       &old->rect,
                                       &new->rect);
     }
   else
     {
       meta_window_update_monitor (window,
-                                  META_WINDOW_UPDATE_MONITOR_FLAGS_NONE);
+                                  META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE);
     }
 }
 
 void
 meta_window_update_monitor (MetaWindow                   *window,
                             MetaWindowUpdateMonitorFlags  flags)
 {
   const MetaLogicalMonitor *old;
 
   old = window->monitor;
   META_WINDOW_GET_CLASS (window)->update_main_monitor (window, flags);
   if (old != window->monitor)
     {
       meta_window_on_all_workspaces_changed (window);
 
       /* 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.
        */
       if (meta_prefs_get_workspaces_only_on_primary () &&
           flags & META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP &&
           meta_window_is_on_primary_monitor (window)  &&
           window->screen->active_workspace != window->workspace)
         meta_window_change_workspace (window, window->screen->active_workspace);
 
-- 
2.18.1