[mutter/wip/carlosg/x11less-preparations: 41/54] x11: Add MetaX11Stack object



commit 6e6ab5579d4fbf521b11102ed56b06d5cc984343
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue May 7 12:59:50 2019 +0200

    x11: Add MetaX11Stack object
    
    This object takes care of the X11 representation of the window stack,
    namely the _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING root window
    properties.
    
    This code has been lifted from src/core/stack.c into src/x11 as it's
    dependent on the X11 display availability. This also leaves MetaStack
    squeaky clean of x11 specifics.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/420

 src/core/stack.c                   | 257 +----------------------
 src/core/stack.h                   |  26 ---
 src/meson.build                    |   2 +
 src/x11/meta-x11-display-private.h |   2 +
 src/x11/meta-x11-display.c         |   3 +
 src/x11/meta-x11-stack-private.h   |  33 +++
 src/x11/meta-x11-stack.c           | 413 +++++++++++++++++++++++++++++++++++++
 7 files changed, 458 insertions(+), 278 deletions(-)
---
diff --git a/src/core/stack.c b/src/core/stack.c
index 55701d5bf..5a2f83301 100644
--- a/src/core/stack.c
+++ b/src/core/stack.c
@@ -29,18 +29,13 @@
 
 #include "core/stack.h"
 
-#include <X11/Xatom.h>
-
 #include "backends/meta-logical-monitor.h"
 #include "core/frame.h"
 #include "core/meta-workspace-manager-private.h"
 #include "core/window-private.h"
 #include "meta/group.h"
-#include "meta/meta-x11-errors.h"
 #include "meta/prefs.h"
 #include "meta/workspace.h"
-#include "x11/group-private.h"
-#include "x11/meta-x11-display-private.h"
 
 #define WINDOW_HAS_TRANSIENT_TYPE(w)                    \
           (w->type == META_WINDOW_DIALOG ||             \
@@ -52,11 +47,8 @@
 #define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w)                     \
   (WINDOW_HAS_TRANSIENT_TYPE (w) && w->transient_for == NULL)
 
-static void stack_sync_to_xserver (MetaStack *stack);
 static void meta_window_set_stack_position_no_sync (MetaWindow *window,
                                                     int         position);
-static void stack_do_window_deletions (MetaStack *stack);
-static void stack_do_window_additions (MetaStack *stack);
 static void stack_do_relayer          (MetaStack *stack);
 static void stack_do_constrain        (MetaStack *stack);
 static void stack_do_resort           (MetaStack *stack);
@@ -85,7 +77,6 @@ G_DEFINE_TYPE (MetaStack, meta_stack, G_TYPE_OBJECT)
 static void
 meta_stack_init (MetaStack *stack)
 {
-  stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window));
 }
 
 static void
@@ -93,11 +84,7 @@ meta_stack_finalize (GObject *object)
 {
   MetaStack *stack = META_STACK (object);
 
-  g_array_free (stack->xwindows, TRUE);
-
   g_list_free (stack->sorted);
-  g_list_free (stack->added);
-  g_list_free (stack->removed);
 
   G_OBJECT_CLASS (meta_stack_parent_class)->finalize (object);
 }
@@ -210,7 +197,11 @@ meta_stack_add (MetaStack  *stack,
   if (meta_window_is_in_stack (window))
     meta_bug ("Window %s had stack position already\n", window->desc);
 
-  stack->added = g_list_prepend (stack->added, window);
+  stack->sorted = g_list_prepend (stack->sorted, window);
+  stack->need_resort = TRUE; /* may not be needed as we add to top */
+  stack->need_constrain = TRUE;
+  stack->need_relayer = TRUE;
+
   g_signal_emit (stack, signals[WINDOW_ADDED], 0, window);
 
   window->stack_position = stack->n_positions;
@@ -219,7 +210,6 @@ meta_stack_add (MetaStack  *stack,
               "Window %s has stack_position initialized to %d\n",
               window->desc, window->stack_position);
 
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
 }
@@ -240,27 +230,10 @@ meta_stack_remove (MetaStack  *stack,
   window->stack_position = -1;
   stack->n_positions -= 1;
 
-  /* We don't know if it's been moved from "added" to "stack" yet */
-  stack->added = g_list_remove (stack->added, window);
   stack->sorted = g_list_remove (stack->sorted, window);
 
   g_signal_emit (stack, signals[WINDOW_REMOVED], 0, window);
 
-  /* stack->removed is only used to update stack->xwindows */
-  if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
-    {
-      /* Remember the window ID to remove it from the stack array.
-       * The macro is safe to use: Window is guaranteed to be 32 bits, and
-       * GUINT_TO_POINTER says it only works on 32 bits.
-       */
-      stack->removed = g_list_prepend (stack->removed,
-                                       GUINT_TO_POINTER (window->xwindow));
-      if (window->frame)
-        stack->removed = g_list_prepend (stack->removed,
-                                         GUINT_TO_POINTER (window->frame->xwindow));
-    }
-
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
 }
@@ -272,7 +245,6 @@ meta_stack_update_layer (MetaStack  *stack,
   MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
   stack->need_relayer = TRUE;
 
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
 }
@@ -284,7 +256,6 @@ meta_stack_update_transient (MetaStack  *stack,
   MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
   stack->need_constrain = TRUE;
 
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
 }
@@ -315,7 +286,6 @@ meta_stack_raise (MetaStack  *stack,
 
   meta_window_set_stack_position_no_sync (window, max_stack_position);
 
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
 }
@@ -345,7 +315,6 @@ meta_stack_lower (MetaStack  *stack,
 
   meta_window_set_stack_position_no_sync (window, min_stack_position);
 
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
 }
@@ -362,7 +331,6 @@ meta_stack_thaw (MetaStack *stack)
   g_return_if_fail (stack->freeze_count > 0);
 
   stack->freeze_count -= 1;
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, NULL);
 }
@@ -879,99 +847,6 @@ apply_constraints (Constraint **constraints,
   g_slist_free (heads);
 }
 
-/**
- * stack_do_window_deletions:
- *
- * Go through "deleted" and take the matching windows
- * out of "windows".
- */
-static void
-stack_do_window_deletions (MetaStack *stack)
-{
-  /* Do removals before adds, with paranoid idea that we might re-add
-   * the same window IDs.
-   */
-  GList *tmp;
-  int i;
-
-  tmp = stack->removed;
-  while (tmp != NULL)
-    {
-      Window xwindow;
-      xwindow = GPOINTER_TO_UINT (tmp->data);
-
-      /* We go from the end figuring removals are more
-       * likely to be recent.
-       */
-      i = stack->xwindows->len;
-      while (i > 0)
-        {
-          --i;
-
-          /* there's no guarantee we'll actually find windows to
-           * remove, e.g. the same xwindow could have been
-           * added/removed before we ever synced, and we put
-           * both the window->xwindow and window->frame->xwindow
-           * in the removal list.
-           */
-          if (xwindow == g_array_index (stack->xwindows, Window, i))
-            {
-              g_array_remove_index (stack->xwindows, i);
-              goto next;
-            }
-        }
-
-    next:
-      tmp = tmp->next;
-    }
-
-  g_list_free (stack->removed);
-  stack->removed = NULL;
-}
-
-static void
-stack_do_window_additions (MetaStack *stack)
-{
-  GList *tmp;
-  gint n_added;
-
-  n_added = g_list_length (stack->added);
-  if (n_added > 0)
-    {
-      meta_topic (META_DEBUG_STACK,
-                  "Adding %d windows to sorted list\n",
-                  n_added);
-
-      /* stack->added has the most recent additions at the
-       * front of the list, so we need to reverse it
-       */
-      stack->added = g_list_reverse (stack->added);
-
-      tmp = stack->added;
-      while (tmp != NULL)
-        {
-          MetaWindow *w;
-
-          w = tmp->data;
-
-          if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
-            g_array_append_val (stack->xwindows, w->xwindow);
-
-          /* add to the main list */
-          stack->sorted = g_list_prepend (stack->sorted, w);
-
-          tmp = tmp->next;
-        }
-
-      stack->need_resort = TRUE; /* may not be needed as we add to top */
-      stack->need_constrain = TRUE;
-      stack->need_relayer = TRUE;
-    }
-
-  g_list_free (stack->added);
-  stack->added = NULL;
-}
-
 /**
  * stack_do_relayer:
  *
@@ -1089,131 +964,11 @@ stack_do_resort (MetaStack *stack)
 static void
 stack_ensure_sorted (MetaStack *stack)
 {
-  stack_do_window_deletions (stack);
-  stack_do_window_additions (stack);
   stack_do_relayer (stack);
   stack_do_constrain (stack);
   stack_do_resort (stack);
 }
 
-/**
- * stack_sync_to_server:
- *
- * Order the windows on the X server to be the same as in our structure.
- * We do this using XRestackWindows if we don't know the previous order,
- * or XConfigureWindow on a few particular windows if we do and can figure
- * out the minimum set of changes.  After that, we set __NET_CLIENT_LIST
- * and __NET_CLIENT_LIST_STACKING.
- *
- * FIXME: Now that we have a good view of the stacking order on the server
- * with MetaStackTracker it should be possible to do a simpler and better
- * job of computing the minimal set of stacking requests needed.
- */
-static void
-stack_sync_to_xserver (MetaStack *stack)
-{
-  GArray *x11_stacked;
-  GArray *all_root_children_stacked; /* wayland OR x11 */
-  GList *tmp;
-  GArray *hidden_stack_ids;
-
-  /* Bail out if frozen */
-  if (stack->freeze_count > 0)
-    return;
-
-  meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n");
-
-  stack_ensure_sorted (stack);
-
-  /* Create stacked xwindow arrays, in bottom-to-top order
-   */
-  x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window));
-
-  all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (guint64));
-  hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (guint64));
-
-  meta_topic (META_DEBUG_STACK, "Bottom to top: ");
-  meta_push_no_msg_prefix ();
-
-  for (tmp = g_list_last(stack->sorted); tmp != NULL; tmp = tmp->prev)
-    {
-      MetaWindow *w = tmp->data;
-      guint64 top_level_window;
-      guint64 stack_id;
-
-      if (w->unmanaging)
-        continue;
-
-      meta_topic (META_DEBUG_STACK, "%u:%d - %s ",
-                 w->layer, w->stack_position, w->desc);
-
-      if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
-        g_array_append_val (x11_stacked, w->xwindow);
-
-      if (w->frame)
-       top_level_window = w->frame->xwindow;
-      else
-       top_level_window = w->xwindow;
-
-      if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
-        stack_id = top_level_window;
-      else
-        stack_id = w->stamp;
-
-      /* We don't restack hidden windows along with the rest, though they are
-       * reflected in the _NET hints. Hidden windows all get pushed below
-       * the screens fullscreen guard_window. */
-      if (w->hidden)
-       {
-          g_array_append_val (hidden_stack_ids, stack_id);
-         continue;
-       }
-
-      g_array_append_val (all_root_children_stacked, stack_id);
-    }
-
-  meta_topic (META_DEBUG_STACK, "\n");
-  meta_pop_no_msg_prefix ();
-
-  /* The screen guard window sits above all hidden windows and acts as
-   * a barrier to input reaching these windows. */
-  guint64 guard_window_id = stack->display->x11_display->guard_window;
-  g_array_append_val (hidden_stack_ids, guard_window_id);
-
-  /* Sync to server */
-
-  meta_topic (META_DEBUG_STACK, "Restacking %u windows\n",
-              all_root_children_stacked->len);
-
-  meta_stack_tracker_restack_managed (stack->display->stack_tracker,
-                                      (guint64 *)all_root_children_stacked->data,
-                                      all_root_children_stacked->len);
-  meta_stack_tracker_restack_at_bottom (stack->display->stack_tracker,
-                                        (guint64 *)hidden_stack_ids->data,
-                                        hidden_stack_ids->len);
-
-  /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */
-
-  XChangeProperty (stack->display->x11_display->xdisplay,
-                   stack->display->x11_display->xroot,
-                   stack->display->x11_display->atom__NET_CLIENT_LIST,
-                   XA_WINDOW,
-                   32, PropModeReplace,
-                   (unsigned char *)stack->xwindows->data,
-                   stack->xwindows->len);
-  XChangeProperty (stack->display->x11_display->xdisplay,
-                   stack->display->x11_display->xroot,
-                   stack->display->x11_display->atom__NET_CLIENT_LIST_STACKING,
-                   XA_WINDOW,
-                   32, PropModeReplace,
-                   (unsigned char *)x11_stacked->data,
-                   x11_stacked->len);
-
-  g_array_free (x11_stacked, TRUE);
-  g_array_free (hidden_stack_ids, TRUE);
-  g_array_free (all_root_children_stacked, TRUE);
-}
-
 MetaWindow*
 meta_stack_get_top (MetaStack *stack)
 {
@@ -1523,7 +1278,6 @@ meta_stack_set_positions (MetaStack *stack,
   meta_topic (META_DEBUG_STACK,
               "Reset the stack positions of (nearly) all windows\n");
 
-  stack_sync_to_xserver (stack);
   meta_stack_changed (stack);
   meta_stack_update_window_tile_matches (stack, NULL);
 }
@@ -1589,7 +1343,6 @@ meta_window_set_stack_position (MetaWindow *window,
   MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
 
   meta_window_set_stack_position_no_sync (window, position);
-  stack_sync_to_xserver (window->display->stack);
   meta_stack_changed (window->display->stack);
   meta_stack_update_window_tile_matches (window->display->stack,
                                          workspace_manager->active_workspace);
diff --git a/src/core/stack.h b/src/core/stack.h
index c407a2ba8..00d1cfe54 100644
--- a/src/core/stack.h
+++ b/src/core/stack.h
@@ -56,35 +56,9 @@ struct _MetaStack
   /** The MetaDisplay containing this stack. */
   MetaDisplay *display;
 
-  /**
-   * A sequence of all the Windows (X handles, not MetaWindows) of the windows
-   * we manage, sorted in order.  Suitable to be passed into _NET_CLIENT_LIST.
-   */
-  GArray *xwindows;
-
   /** The MetaWindows of the windows we manage, sorted in order. */
   GList *sorted;
 
-  /**
-   * MetaWindows waiting to be added to the "sorted" and "windows" list, after
-   * being added by meta_stack_add() and before being assimilated by
-   * stack_ensure_sorted().
-   *
-   * The order of the elements in this list is not important; what is important
-   * is the stack_position element of each window.
-   */
-  GList *added;
-
-  /**
-   * Windows (X handles, not MetaWindows) waiting to be removed from the
-   * "windows" list, after being removed by meta_stack_remove() and before
-   * being assimilated by stack_ensure_sorted().  (We already removed them
-   * from the "sorted" list.)
-   *
-   * The order of the elements in this list is not important.
-   */
-  GList *removed;
-
   /**
    * If this is zero, the local stack oughtn't to be brought up to date with
    * the X server's stack, because it is in the middle of being updated.
diff --git a/src/meson.build b/src/meson.build
index 0a1262fe9..5007b5cab 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -392,6 +392,8 @@ mutter_sources = [
   'x11/meta-x11-selection-input-stream-private.h',
   'x11/meta-x11-selection-output-stream.c',
   'x11/meta-x11-selection-output-stream-private.h',
+  'x11/meta-x11-stack.c',
+  'x11/meta-x11-stack-private.h',
   'x11/mutter-Xatomtype.h',
   'x11/session.c',
   'x11/session.h',
diff --git a/src/x11/meta-x11-display-private.h b/src/x11/meta-x11-display-private.h
index c7af182d9..d19cdd688 100644
--- a/src/x11/meta-x11-display-private.h
+++ b/src/x11/meta-x11-display-private.h
@@ -35,6 +35,7 @@
 #include "meta/types.h"
 #include "meta/meta-x11-display.h"
 #include "meta-startup-notification-x11.h"
+#include "meta-x11-stack-private.h"
 #include "ui/ui.h"
 
 typedef struct _MetaGroupPropHooks  MetaGroupPropHooks;
@@ -167,6 +168,7 @@ struct _MetaX11Display
 #define META_X11_DISPLAY_HAS_XINPUT_23(x11_display) ((x11_display)->have_xinput_23)
 
   MetaX11StartupNotification *startup_notification;
+  MetaX11Stack *x11_stack;
 };
 
 MetaX11Display *meta_x11_display_new (MetaDisplay *display, GError **error);
diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c
index 2aece19a8..98c4e3e43 100644
--- a/src/x11/meta-x11-display.c
+++ b/src/x11/meta-x11-display.c
@@ -108,6 +108,8 @@ meta_x11_display_dispose (GObject *object)
 
   meta_x11_selection_shutdown (x11_display);
 
+  g_clear_object (&x11_display->x11_stack);
+
   if (x11_display->ui)
     {
       meta_ui_free (x11_display->ui);
@@ -1270,6 +1272,7 @@ meta_x11_display_new (MetaDisplay *display, GError **error)
   set_desktop_geometry_hint (x11_display);
 
   x11_display->ui = meta_ui_new (x11_display);
+  x11_display->x11_stack = meta_x11_stack_new (x11_display);
 
   x11_display->keys_grabbed = FALSE;
   meta_x11_display_grab_keys (x11_display);
diff --git a/src/x11/meta-x11-stack-private.h b/src/x11/meta-x11-stack-private.h
new file mode 100644
index 000000000..a00b8e743
--- /dev/null
+++ b/src/x11/meta-x11-stack-private.h
@@ -0,0 +1,33 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef META_X11_STACK_H
+#define META_X11_STACK_H
+
+#include <glib-object.h>
+
+#include "meta/types.h"
+
+#define META_TYPE_X11_STACK (meta_x11_stack_get_type ())
+G_DECLARE_FINAL_TYPE (MetaX11Stack, meta_x11_stack, META, X11_STACK, GObject)
+
+typedef struct _MetaX11Stack MetaX11Stack;
+
+MetaX11Stack * meta_x11_stack_new (MetaX11Display *x11_display);
+
+#endif /* META_X11_STACK_H */
diff --git a/src/x11/meta-x11-stack.c b/src/x11/meta-x11-stack.c
new file mode 100644
index 000000000..fa08fc4c5
--- /dev/null
+++ b/src/x11/meta-x11-stack.c
@@ -0,0 +1,413 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "core/frame.h"
+#include "core/stack.h"
+#include "core/window-private.h"
+#include "x11/meta-x11-display-private.h"
+#include "x11/meta-x11-stack-private.h"
+
+struct _MetaX11Stack
+{
+  GObject parent;
+  MetaX11Display *x11_display;
+
+  /*
+   * A sequence of all the Windows (X handles, not MetaWindows) of the windows
+   * we manage, sorted in order.  Suitable to be passed into _NET_CLIENT_LIST.
+   */
+  GArray *xwindows;
+
+  /*
+   * MetaWindows waiting to be added to the xwindows list, after
+   * being added to the MetaStack.
+   *
+   * The order of the elements in this list is not important; what is important
+   * is the stack_position element of each window.
+   */
+  GList *added;
+
+  /*
+   * Windows (X handles, not MetaWindows) waiting to be removed from the
+   * xwindows list, after being removed from the MetaStack.
+   *
+   * The order of the elements in this list is not important.
+   */
+  GList *removed;
+};
+
+enum
+{
+  PROP_DISPLAY = 1,
+  N_PROPS
+};
+
+static GParamSpec *pspecs[N_PROPS] = { 0 };
+
+G_DEFINE_TYPE (MetaX11Stack, meta_x11_stack, G_TYPE_OBJECT)
+
+static void
+meta_x11_stack_init (MetaX11Stack *x11_stack)
+{
+  x11_stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window));
+}
+
+static void
+meta_x11_stack_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  MetaX11Stack *x11_stack = META_X11_STACK (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      x11_stack->x11_display = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_x11_stack_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  MetaX11Stack *x11_stack = META_X11_STACK (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      g_value_set_object (value, x11_stack->x11_display);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+stack_window_added_cb (MetaStack    *stack,
+                       MetaWindow   *window,
+                       MetaX11Stack *x11_stack)
+{
+  if (window->client_type != META_WINDOW_CLIENT_TYPE_X11)
+    return;
+
+  x11_stack->added = g_list_prepend (x11_stack->added, window);
+}
+
+static void
+stack_window_removed_cb (MetaStack    *stack,
+                         MetaWindow   *window,
+                         MetaX11Stack *x11_stack)
+{
+  if (window->client_type != META_WINDOW_CLIENT_TYPE_X11)
+    return;
+
+  x11_stack->added = g_list_remove (x11_stack->added, window);
+
+  x11_stack->removed = g_list_prepend (x11_stack->removed,
+                                   GUINT_TO_POINTER (window->xwindow));
+  if (window->frame)
+    {
+      x11_stack->removed = g_list_prepend (x11_stack->removed,
+                                           GUINT_TO_POINTER (window->frame->xwindow));
+    }
+}
+
+/**
+ * stack_do_window_deletions:
+ *
+ * Go through "deleted" and take the matching windows
+ * out of "windows".
+ */
+static void
+x11_stack_do_window_deletions (MetaX11Stack *x11_stack)
+{
+  GList *tmp;
+  int i;
+
+  tmp = x11_stack->removed;
+  while (tmp != NULL)
+    {
+      Window xwindow;
+      xwindow = GPOINTER_TO_UINT (tmp->data);
+
+      /* We go from the end figuring removals are more
+       * likely to be recent.
+       */
+      i = x11_stack->xwindows->len;
+      while (i > 0)
+        {
+          --i;
+
+          /* there's no guarantee we'll actually find windows to
+           * remove, e.g. the same xwindow could have been
+           * added/removed before we ever synced, and we put
+           * both the window->xwindow and window->frame->xwindow
+           * in the removal list.
+           */
+          if (xwindow == g_array_index (x11_stack->xwindows, Window, i))
+            {
+              g_array_remove_index (x11_stack->xwindows, i);
+              goto next;
+            }
+        }
+
+    next:
+      tmp = tmp->next;
+    }
+
+  g_clear_pointer (&x11_stack->removed, g_list_free);
+}
+
+static void
+x11_stack_do_window_additions (MetaX11Stack *x11_stack)
+{
+  GList *tmp;
+  gint n_added;
+
+  n_added = g_list_length (x11_stack->added);
+  if (n_added > 0)
+    {
+      meta_topic (META_DEBUG_STACK,
+                  "Adding %d windows to sorted list\n",
+                  n_added);
+
+      /* stack->added has the most recent additions at the
+       * front of the list, so we need to reverse it
+       */
+      x11_stack->added = g_list_reverse (x11_stack->added);
+
+      tmp = x11_stack->added;
+      while (tmp != NULL)
+        {
+          MetaWindow *w;
+
+          w = tmp->data;
+          g_array_append_val (x11_stack->xwindows, w->xwindow);
+          tmp = tmp->next;
+        }
+    }
+
+  g_clear_pointer (&x11_stack->added, g_list_free);
+}
+
+/**
+ * x11_stack_sync_to_server:
+ *
+ * Order the windows on the X server to be the same as in our structure.
+ * We do this using XRestackWindows if we don't know the previous order,
+ * or XConfigureWindow on a few particular windows if we do and can figure
+ * out the minimum set of changes.  After that, we set __NET_CLIENT_LIST
+ * and __NET_CLIENT_LIST_STACKING.
+ *
+ * FIXME: Now that we have a good view of the stacking order on the server
+ * with MetaStackTracker it should be possible to do a simpler and better
+ * job of computing the minimal set of stacking requests needed.
+ */
+static void
+x11_stack_sync_to_xserver (MetaX11Stack *x11_stack)
+{
+  MetaX11Display *x11_display = x11_stack->x11_display;
+  MetaStack *stack = x11_display->display->stack;
+  GArray *x11_stacked;
+  GArray *all_root_children_stacked; /* wayland OR x11 */
+  GList *tmp;
+  GArray *hidden_stack_ids;
+  uint64_t guard_window_id;
+  GList *sorted;
+
+  meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n");
+
+  /* Create stacked xwindow arrays, in bottom-to-top order
+   */
+  x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window));
+
+  all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (guint64));
+  hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (guint64));
+
+  meta_topic (META_DEBUG_STACK, "Bottom to top: ");
+  meta_push_no_msg_prefix ();
+
+  sorted = meta_stack_list_windows (stack, NULL);
+
+  for (tmp = sorted; tmp; tmp = tmp->next)
+    {
+      MetaWindow *w = tmp->data;
+      guint64 top_level_window;
+      guint64 stack_id;
+
+      if (w->unmanaging)
+        continue;
+
+      meta_topic (META_DEBUG_STACK, "%u:%d - %s ",
+                 w->layer, w->stack_position, w->desc);
+
+      if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
+        g_array_append_val (x11_stacked, w->xwindow);
+
+      if (w->frame)
+       top_level_window = w->frame->xwindow;
+      else
+       top_level_window = w->xwindow;
+
+      if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
+        stack_id = top_level_window;
+      else
+        stack_id = w->stamp;
+
+      /* We don't restack hidden windows along with the rest, though they are
+       * reflected in the _NET hints. Hidden windows all get pushed below
+       * the screens fullscreen guard_window. */
+      if (w->hidden)
+       {
+          g_array_append_val (hidden_stack_ids, stack_id);
+         continue;
+       }
+
+      g_array_append_val (all_root_children_stacked, stack_id);
+    }
+
+  meta_topic (META_DEBUG_STACK, "\n");
+  meta_pop_no_msg_prefix ();
+
+  /* The screen guard window sits above all hidden windows and acts as
+   * a barrier to input reaching these windows. */
+  guard_window_id = x11_stack->x11_display->guard_window;
+  g_array_append_val (hidden_stack_ids, guard_window_id);
+
+  /* Sync to server */
+
+  meta_topic (META_DEBUG_STACK, "Restacking %u windows\n",
+              all_root_children_stacked->len);
+
+  meta_stack_tracker_restack_managed (x11_display->display->stack_tracker,
+                                      (guint64 *)all_root_children_stacked->data,
+                                      all_root_children_stacked->len);
+  meta_stack_tracker_restack_at_bottom (x11_display->display->stack_tracker,
+                                        (guint64 *)hidden_stack_ids->data,
+                                        hidden_stack_ids->len);
+
+  /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */
+
+  XChangeProperty (x11_stack->x11_display->xdisplay,
+                   x11_stack->x11_display->xroot,
+                   x11_stack->x11_display->atom__NET_CLIENT_LIST,
+                   XA_WINDOW,
+                   32, PropModeReplace,
+                   (unsigned char *) x11_stack->xwindows->data,
+                   x11_stack->xwindows->len);
+  XChangeProperty (x11_stack->x11_display->xdisplay,
+                   x11_stack->x11_display->xroot,
+                   x11_stack->x11_display->atom__NET_CLIENT_LIST_STACKING,
+                   XA_WINDOW,
+                   32, PropModeReplace,
+                   (unsigned char *) x11_stacked->data,
+                   x11_stacked->len);
+
+  g_array_free (x11_stacked, TRUE);
+  g_array_free (hidden_stack_ids, TRUE);
+  g_array_free (all_root_children_stacked, TRUE);
+  g_list_free (sorted);
+}
+
+static void
+stack_changed_cb (MetaX11Stack *x11_stack)
+{
+  /* Do removals before adds, with paranoid idea that we might re-add
+   * the same window IDs.
+   */
+  x11_stack_do_window_deletions (x11_stack);
+  x11_stack_do_window_additions (x11_stack);
+  x11_stack_sync_to_xserver (x11_stack);
+}
+
+static void
+meta_x11_stack_constructed (GObject *object)
+{
+  MetaX11Stack *x11_stack = META_X11_STACK (object);
+  MetaX11Display *x11_display = x11_stack->x11_display;
+
+  G_OBJECT_CLASS (meta_x11_stack_parent_class)->constructed (object);
+
+  g_signal_connect (x11_display->display->stack,
+                    "window-added",
+                    G_CALLBACK (stack_window_added_cb),
+                    x11_stack);
+  g_signal_connect (x11_display->display->stack,
+                    "window-removed",
+                    G_CALLBACK (stack_window_removed_cb),
+                    x11_stack);
+  g_signal_connect_swapped (x11_display->display->stack,
+                            "changed",
+                            G_CALLBACK (stack_changed_cb),
+                            x11_stack);
+}
+
+static void
+meta_x11_stack_finalize (GObject *object)
+{
+  MetaX11Stack *x11_stack = META_X11_STACK (object);
+  MetaX11Display *x11_display = x11_stack->x11_display;
+
+  if (x11_display->display && x11_display->display->stack)
+    {
+      g_signal_handlers_disconnect_by_data (x11_display->display->stack,
+                                            x11_stack);
+    }
+
+  g_array_free (x11_stack->xwindows, TRUE);
+  g_list_free (x11_stack->added);
+  g_list_free (x11_stack->removed);
+
+  G_OBJECT_CLASS (meta_x11_stack_parent_class)->finalize (object);
+}
+
+static void
+meta_x11_stack_class_init (MetaX11StackClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = meta_x11_stack_set_property;
+  object_class->get_property = meta_x11_stack_get_property;
+  object_class->constructed = meta_x11_stack_constructed;
+  object_class->finalize = meta_x11_stack_finalize;
+
+  pspecs[PROP_DISPLAY] =
+    g_param_spec_object ("display",
+                         "Display",
+                         "Display",
+                         META_TYPE_X11_DISPLAY,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, N_PROPS, pspecs);
+}
+
+MetaX11Stack *
+meta_x11_stack_new (MetaX11Display *x11_display)
+{
+  return g_object_new (META_TYPE_X11_STACK,
+                       "display", x11_display,
+                       NULL);
+}


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