[gnome-shell] Move WindowPreviewLayout from JS to C



commit 04c781674c884935ff28805a690554f0c67605bd
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Sat Jun 20 17:22:30 2020 +0200

    Move WindowPreviewLayout from JS to C
    
    This layout manager is used quite often and the time we spend calling
    it's allocate and get_preferred_width/heigth functions increases with
    every open window.
    
    We can save a lot of precious time during the layout cycle by moving
    this layout manager from JS to C, thus avoiding the overhead of
    trampolining between C and JS land.
    
    In a measurement where the average time spent in vfunc_allocate() of the
    Workspace actor was measured while opening an overview with 20 windows,
    the average time spent went down from 3.1 ms to 2.3 ms.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1743>

 js/ui/windowPreview.js            | 182 +--------------
 src/meson.build                   |   2 +
 src/shell-window-preview-layout.c | 472 ++++++++++++++++++++++++++++++++++++++
 src/shell-window-preview-layout.h |  33 +++
 4 files changed, 511 insertions(+), 178 deletions(-)
---
diff --git a/js/ui/windowPreview.js b/js/ui/windowPreview.js
index bb97ebc635..3bd213959f 100644
--- a/js/ui/windowPreview.js
+++ b/js/ui/windowPreview.js
@@ -22,180 +22,6 @@ const ICON_OVERLAP = 0.7;
 
 const ICON_TITLE_SPACING = 6;
 
-var WindowPreviewLayout = GObject.registerClass({
-    Properties: {
-        'bounding-box': GObject.ParamSpec.boxed(
-            'bounding-box', 'Bounding box', 'Bounding box',
-            GObject.ParamFlags.READABLE,
-            Clutter.ActorBox.$gtype),
-    },
-}, class WindowPreviewLayout extends Clutter.LayoutManager {
-    _init() {
-        super._init();
-
-        this._container = null;
-        this._boundingBox = new Clutter.ActorBox();
-        this._windows = new Map();
-    }
-
-    _layoutChanged() {
-        let frameRect;
-
-        for (const windowInfo of this._windows.values()) {
-            const frame = windowInfo.metaWindow.get_frame_rect();
-            frameRect = frameRect?.union(frame) ?? frame;
-        }
-
-        if (!frameRect)
-            frameRect = new Meta.Rectangle();
-
-        const oldBox = this._boundingBox.copy();
-        this._boundingBox.set_origin(frameRect.x, frameRect.y);
-        this._boundingBox.set_size(frameRect.width, frameRect.height);
-
-        if (!this._boundingBox.equal(oldBox))
-            this.notify('bounding-box');
-
-        // Always call layout_changed(), a size or position change of an
-        // attached dialog might not affect the boundingBox
-        this.layout_changed();
-    }
-
-    vfunc_set_container(container) {
-        this._container = container;
-    }
-
-    vfunc_get_preferred_height(_container, _forWidth) {
-        return [0, this._boundingBox.get_height()];
-    }
-
-    vfunc_get_preferred_width(_container, _forHeight) {
-        return [0, this._boundingBox.get_width()];
-    }
-
-    vfunc_allocate(container, box) {
-        // If the scale isn't 1, we weren't allocated our preferred size
-        // and have to scale the children allocations accordingly.
-        const scaleX = this._boundingBox.get_width() > 0
-            ? box.get_width() / this._boundingBox.get_width()
-            : 1;
-        const scaleY = this._boundingBox.get_height() > 0
-            ? box.get_height() / this._boundingBox.get_height()
-            : 1;
-
-        const childBox = new Clutter.ActorBox();
-
-        for (const child of container) {
-            if (!child.visible)
-                continue;
-
-            const windowInfo = this._windows.get(child);
-            if (windowInfo) {
-                const bufferRect = windowInfo.metaWindow.get_buffer_rect();
-                childBox.set_origin(
-                    bufferRect.x - this._boundingBox.x1,
-                    bufferRect.y - this._boundingBox.y1);
-
-                const [, , natWidth, natHeight] = child.get_preferred_size();
-                childBox.set_size(natWidth, natHeight);
-
-                childBox.x1 *= scaleX;
-                childBox.x2 *= scaleX;
-                childBox.y1 *= scaleY;
-                childBox.y2 *= scaleY;
-
-                child.allocate(childBox);
-            } else {
-                child.allocate_preferred_size(0, 0);
-            }
-        }
-    }
-
-    /**
-     * addWindow:
-     * @param {Meta.Window} window: the MetaWindow instance
-     *
-     * Creates a ClutterActor drawing the texture of @window and adds it
-     * to the container. If @window is already part of the preview, this
-     * function will do nothing.
-     *
-     * @returns {Clutter.Actor} The newly created actor drawing @window
-     */
-    addWindow(window) {
-        const index = [...this._windows.values()].findIndex(info =>
-            info.metaWindow === window);
-
-        if (index !== -1)
-            return null;
-
-        const windowActor = window.get_compositor_private();
-        const actor = new Clutter.Clone({ source: windowActor });
-
-        this._windows.set(actor, {
-            metaWindow: window,
-            windowActor,
-            sizeChangedId: window.connect('size-changed', () =>
-                this._layoutChanged()),
-            positionChangedId: window.connect('position-changed', () =>
-                this._layoutChanged()),
-            windowActorDestroyId: windowActor.connect('destroy', () =>
-                actor.destroy()),
-            destroyId: actor.connect('destroy', () =>
-                this.removeWindow(window)),
-        });
-
-        this._container.add_child(actor);
-
-        this._layoutChanged();
-
-        return actor;
-    }
-
-    /**
-     * removeWindow:
-     * @param {Meta.Window} window: the window to remove from the preview
-     *
-     * Removes a MetaWindow @window from the preview which has been added
-     * previously using addWindow(). If @window is not part of preview,
-     * this function will do nothing.
-     */
-    removeWindow(window) {
-        const entry = [...this._windows].find(
-            ([, i]) => i.metaWindow === window);
-
-        if (!entry)
-            return;
-
-        const [actor, windowInfo] = entry;
-
-        windowInfo.metaWindow.disconnect(windowInfo.sizeChangedId);
-        windowInfo.metaWindow.disconnect(windowInfo.positionChangedId);
-        windowInfo.windowActor.disconnect(windowInfo.windowActorDestroyId);
-        actor.disconnect(windowInfo.destroyId);
-
-        this._windows.delete(actor);
-        this._container.remove_child(actor);
-
-        this._layoutChanged();
-    }
-
-    /**
-     * getWindows:
-     *
-     * Gets an array of all MetaWindows that were added to the layout
-     * using addWindow(), ordered by the insertion order.
-     *
-     * @returns {Array} An array including all windows
-     */
-    getWindows() {
-        return [...this._windows.values()].map(i => i.metaWindow);
-    }
-
-    get boundingBox() {
-        return this._boundingBox;
-    }
-});
-
 var WindowPreview = GObject.registerClass({
     Properties: {
         'overlay-enabled': GObject.ParamSpec.boolean(
@@ -235,7 +61,7 @@ var WindowPreview = GObject.registerClass({
         // the initialization of the actor if that layout manager keeps track
         // of its container, so set the layout manager after creating the
         // container
-        this._windowContainer.layout_manager = new WindowPreviewLayout();
+        this._windowContainer.layout_manager = new Shell.WindowPreviewLayout();
         this.add_child(this._windowContainer);
 
         this._addWindow(metaWindow);
@@ -601,7 +427,7 @@ var WindowPreview = GObject.registerClass({
     }
 
     _addWindow(metaWindow) {
-        const clone = this._windowContainer.layout_manager.addWindow(metaWindow);
+        const clone = this._windowContainer.layout_manager.add_window(metaWindow);
         if (!clone)
             return;
 
@@ -620,7 +446,7 @@ var WindowPreview = GObject.registerClass({
     }
 
     _deleteAll() {
-        const windows = this._windowContainer.layout_manager.getWindows();
+        const windows = this._windowContainer.layout_manager.get_windows();
 
         // Delete all windows, starting from the bottom-most (most-modal) one
         for (const window of windows.reverse())
@@ -645,7 +471,7 @@ var WindowPreview = GObject.registerClass({
     }
 
     _hasAttachedDialogs() {
-        return this._windowContainer.layout_manager.getWindows().length > 1;
+        return this._windowContainer.layout_manager.get_windows().length > 1;
     }
 
     _updateAttachedDialogs() {
diff --git a/src/meson.build b/src/meson.build
index a7c56bbcfe..d20134fd20 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -109,6 +109,7 @@ libshell_public_headers = [
   'shell-tray-icon.h',
   'shell-tray-manager.h',
   'shell-util.h',
+  'shell-window-preview-layout.h',
   'shell-window-tracker.h',
   'shell-wm.h'
 ]
@@ -151,6 +152,7 @@ libshell_sources = [
   'shell-tray-icon.c',
   'shell-tray-manager.c',
   'shell-util.c',
+  'shell-window-preview-layout.c',
   'shell-window-tracker.c',
   'shell-wm.c'
 ]
diff --git a/src/shell-window-preview-layout.c b/src/shell-window-preview-layout.c
new file mode 100644
index 0000000000..a759a74f17
--- /dev/null
+++ b/src/shell-window-preview-layout.c
@@ -0,0 +1,472 @@
+#include "config.h"
+
+#include <clutter/clutter.h>
+#include <meta/window.h>
+#include "shell-window-preview-layout.h"
+
+typedef struct _ShellWindowPreviewLayoutPrivate ShellWindowPreviewLayoutPrivate;
+struct _ShellWindowPreviewLayoutPrivate
+{
+  ClutterActor *container;
+  GHashTable *windows;
+
+  ClutterActorBox bounding_box;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_BOUNDING_BOX,
+
+  PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellWindowPreviewLayout, shell_window_preview_layout,
+                            CLUTTER_TYPE_LAYOUT_MANAGER);
+
+typedef struct _WindowInfo
+{
+  MetaWindow *window;
+  ClutterActor *window_actor;
+
+  gulong size_changed_id;
+  gulong position_changed_id;
+  gulong window_actor_destroy_id;
+  gulong destroy_id;
+} WindowInfo;
+
+static void
+shell_window_preview_layout_get_property (GObject      *object,
+                                          unsigned int  property_id,
+                                          GValue       *value,
+                                          GParamSpec   *pspec)
+{
+  ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (object);
+  ShellWindowPreviewLayoutPrivate *priv;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  switch (property_id)
+    {
+    case PROP_BOUNDING_BOX:
+      g_value_set_boxed (value, &priv->bounding_box);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+shell_window_preview_layout_set_container (ClutterLayoutManager *layout,
+                                           ClutterContainer     *container)
+{
+  ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+  ShellWindowPreviewLayoutPrivate *priv;
+  ClutterLayoutManagerClass *parent_class;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  priv->container = CLUTTER_ACTOR (container);
+
+  parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (shell_window_preview_layout_parent_class);
+  parent_class->set_container (layout, container);
+}
+
+static void
+shell_window_preview_layout_get_preferred_width (ClutterLayoutManager *layout,
+                                                 ClutterContainer     *container,
+                                                 float                 for_height,
+                                                 float                *min_width_p,
+                                                 float                *natural_width_p)
+{
+  ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+  ShellWindowPreviewLayoutPrivate *priv;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  if (min_width_p)
+    *min_width_p = 0;
+
+  if (natural_width_p)
+    *natural_width_p = clutter_actor_box_get_width (&priv->bounding_box);
+}
+
+static void
+shell_window_preview_layout_get_preferred_height (ClutterLayoutManager *layout,
+                                                  ClutterContainer     *container,
+                                                  float                 for_width,
+                                                  float                *min_height_p,
+                                                  float                *natural_height_p)
+{
+  ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+  ShellWindowPreviewLayoutPrivate *priv;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  if (min_height_p)
+    *min_height_p = 0;
+
+  if (natural_height_p)
+    *natural_height_p = clutter_actor_box_get_height (&priv->bounding_box);
+}
+
+
+static void
+shell_window_preview_layout_allocate (ClutterLayoutManager   *layout,
+                                      ClutterContainer       *container,
+                                      const ClutterActorBox  *box)
+{
+  ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (layout);
+  ShellWindowPreviewLayoutPrivate *priv;
+  float scale_x, scale_y;
+  float bounding_box_width, bounding_box_height;
+  ClutterActorIter iter;
+  ClutterActor *child;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  bounding_box_width = clutter_actor_box_get_width (&priv->bounding_box);
+  bounding_box_height = clutter_actor_box_get_height (&priv->bounding_box);
+
+  if (bounding_box_width == 0)
+    scale_x = 1.f;
+  else
+    scale_x = clutter_actor_box_get_width (box) / bounding_box_width;
+
+  if (bounding_box_height == 0)
+    scale_y = 1.f;
+  else
+    scale_y = clutter_actor_box_get_height (box) / bounding_box_height;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      ClutterActorBox child_box = { 0, };
+      WindowInfo *window_info;
+
+      if (!clutter_actor_is_visible (child))
+        continue;
+
+      window_info = g_hash_table_lookup (priv->windows, child);
+
+      if (window_info)
+        {
+          MetaRectangle buffer_rect;
+          float child_nat_width, child_nat_height;
+
+          meta_window_get_buffer_rect (window_info->window, &buffer_rect);
+
+          clutter_actor_box_set_origin (&child_box,
+                                        buffer_rect.x - priv->bounding_box.x1,
+                                        buffer_rect.y - priv->bounding_box.y1);
+
+          clutter_actor_get_preferred_size (child, NULL, NULL,
+                                            &child_nat_width, &child_nat_height);
+
+          clutter_actor_box_set_size (&child_box, child_nat_width, child_nat_height);
+
+          child_box.x1 *= scale_x;
+          child_box.x2 *= scale_x;
+          child_box.y1 *= scale_y;
+          child_box.y2 *= scale_y;
+
+          clutter_actor_allocate (child, &child_box);
+        }
+      else
+        {
+          float x, y;
+
+          clutter_actor_get_fixed_position (child, &x, &y);
+          clutter_actor_allocate_preferred_size (child, x, y);
+        }
+    }
+}
+
+static void
+on_layout_changed (ShellWindowPreviewLayout *self)
+{
+  ShellWindowPreviewLayoutPrivate *priv;
+  GHashTableIter iter;
+  gpointer value;
+  gboolean first_rect = TRUE;
+  MetaRectangle bounding_rect = { 0, };
+  ClutterActorBox old_bounding_box;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  old_bounding_box =
+    (ClutterActorBox) CLUTTER_ACTOR_BOX_INIT (priv->bounding_box.x1,
+                                              priv->bounding_box.y1,
+                                              priv->bounding_box.x2,
+                                              priv->bounding_box.y2);
+
+  g_hash_table_iter_init (&iter, priv->windows);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      WindowInfo *window_info = value;
+      MetaRectangle frame_rect;
+
+      meta_window_get_frame_rect (window_info->window, &frame_rect);
+
+      if (first_rect)
+        {
+          bounding_rect = frame_rect;
+          first_rect = FALSE;
+          continue;
+        }
+
+      meta_rectangle_union (&frame_rect, &bounding_rect, &bounding_rect);
+    }
+
+  clutter_actor_box_set_origin (&priv->bounding_box,
+                                (float) bounding_rect.x,
+                                (float) bounding_rect.y);
+  clutter_actor_box_set_size (&priv->bounding_box,
+                              (float) bounding_rect.width,
+                              (float) bounding_rect.height);
+
+  if (!clutter_actor_box_equal (&priv->bounding_box, &old_bounding_box))
+    g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_BOUNDING_BOX]);
+
+  clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
+}
+
+static void
+on_window_size_position_changed (MetaWindow               *window,
+                                 ShellWindowPreviewLayout *self)
+{
+  on_layout_changed (self);
+}
+
+static void
+on_window_destroyed (ClutterActor *actor)
+{
+  clutter_actor_destroy (actor);
+}
+
+static void
+on_actor_destroyed (ClutterActor *actor,
+                    ShellWindowPreviewLayout *self)
+{
+  ShellWindowPreviewLayoutPrivate *priv;
+  WindowInfo *window_info;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  window_info = g_hash_table_lookup (priv->windows, actor);
+  g_assert (window_info != NULL);
+
+  shell_window_preview_layout_remove_window (self, window_info->window);
+}
+
+static void
+shell_window_preview_layout_dispose (GObject *gobject)
+{
+  ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (gobject);
+  ShellWindowPreviewLayoutPrivate *priv;
+  GHashTableIter iter;
+  gpointer key, value;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  g_hash_table_iter_init (&iter, priv->windows);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      ClutterActor *actor = key;
+      WindowInfo *info = value;
+
+      g_clear_signal_handler (&info->size_changed_id, info->window);
+      g_clear_signal_handler (&info->position_changed_id, info->window);
+      g_clear_signal_handler (&info->window_actor_destroy_id, info->window_actor);
+      g_clear_signal_handler (&info->destroy_id, actor);
+
+      clutter_actor_remove_child (priv->container, actor);
+    }
+
+  g_hash_table_remove_all (priv->windows);
+
+  G_OBJECT_CLASS (shell_window_preview_layout_parent_class)->dispose (gobject);
+}
+
+static void
+shell_window_preview_layout_init (ShellWindowPreviewLayout *self)
+{
+  ShellWindowPreviewLayoutPrivate *priv;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  priv->windows = g_hash_table_new (NULL, NULL);
+}
+
+static void
+shell_window_preview_layout_class_init (ShellWindowPreviewLayoutClass *klass)
+{
+  ClutterLayoutManagerClass *layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  layout_class->get_preferred_width = shell_window_preview_layout_get_preferred_width;
+  layout_class->get_preferred_height = shell_window_preview_layout_get_preferred_height;
+  layout_class->allocate = shell_window_preview_layout_allocate;
+  layout_class->set_container = shell_window_preview_layout_set_container;
+
+  gobject_class->dispose = shell_window_preview_layout_dispose;
+  gobject_class->get_property = shell_window_preview_layout_get_property;
+
+  /**
+   * ShellWindowPreviewLayout:bounding-box:
+   */
+  obj_props[PROP_BOUNDING_BOX] =
+    g_param_spec_boxed ("bounding-box",
+                        "Bounding Box",
+                        "Bounding Box",
+                        CLUTTER_TYPE_ACTOR_BOX,
+                        G_PARAM_READABLE |
+                        G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
+}
+
+/**
+ * shell_window_preview_layout_add_window:
+ * @self: a #ShellWindowPreviewLayout
+ * @window: the #MetaWindow
+ *
+ * Creates a ClutterActor drawing the texture of @window and adds it
+ * to the container. If @window is already part of the preview, this
+ * function will do nothing.
+ *
+ * Returns: (transfer none): The newly created actor drawing @window
+ */
+ClutterActor *
+shell_window_preview_layout_add_window (ShellWindowPreviewLayout *self,
+                                        MetaWindow               *window)
+{
+  ShellWindowPreviewLayoutPrivate *priv;
+  ClutterActor *window_actor, *actor;
+  WindowInfo *window_info;
+  GHashTableIter iter;
+  gpointer value;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  g_hash_table_iter_init (&iter, priv->windows);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      WindowInfo *info = value;
+
+      if (info->window == window)
+        return NULL;
+    }
+
+  window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
+  actor = clutter_clone_new (window_actor);
+
+  window_info = g_new0 (WindowInfo, 1);
+
+  window_info->window = window;
+  window_info->window_actor = window_actor;
+  window_info->size_changed_id =
+    g_signal_connect (window, "size-changed",
+                      G_CALLBACK (on_window_size_position_changed), self);
+  window_info->position_changed_id =
+    g_signal_connect (window, "position-changed",
+                      G_CALLBACK (on_window_size_position_changed), self);
+  window_info->window_actor_destroy_id =
+    g_signal_connect (window_actor, "destroy",
+                      G_CALLBACK (on_window_destroyed), actor);
+  window_info->destroy_id =
+    g_signal_connect (actor, "destroy",
+                      G_CALLBACK (on_actor_destroyed), self);
+
+  g_hash_table_insert (priv->windows, actor, window_info);
+
+  clutter_actor_add_child (priv->container, actor);
+
+  on_layout_changed (self);
+
+  return actor;
+}
+
+/**
+ * shell_window_preview_layout_remove_window:
+ * @self: a #ShellWindowPreviewLayout
+ * @window: the #MetaWindow
+ *
+ * Removes a MetaWindow @window from the preview which has been added
+ * previously using shell_window_preview_layout_add_window().
+ * If @window is not part of preview, this function will do nothing.
+ */
+void
+shell_window_preview_layout_remove_window (ShellWindowPreviewLayout *self,
+                                           MetaWindow               *window)
+{
+  ShellWindowPreviewLayoutPrivate *priv;
+  ClutterActor *actor;
+  WindowInfo *window_info = NULL;
+  GHashTableIter iter;
+  gpointer key, value;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  g_hash_table_iter_init (&iter, priv->windows);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      WindowInfo *info = value;
+
+      if (info->window == window)
+        {
+          actor = CLUTTER_ACTOR (key);
+          window_info = info;
+          break;
+        }
+    }
+
+  if (window_info == NULL)
+    return;
+
+  g_clear_signal_handler (&window_info->size_changed_id, window);
+  g_clear_signal_handler (&window_info->position_changed_id, window);
+  g_clear_signal_handler (&window_info->window_actor_destroy_id, window_info->window_actor);
+  g_clear_signal_handler (&window_info->destroy_id, actor);
+
+  g_hash_table_remove (priv->windows, actor);
+
+  clutter_actor_remove_child (priv->container, actor);
+
+  on_layout_changed (self);
+}
+
+/**
+ * shell_window_preview_layout_get_windows:
+ * @self: a #ShellWindowPreviewLayout
+ *
+ * Gets an array of all MetaWindows that were added to the layout
+ * using shell_window_preview_layout_add_window(), ordered by the
+ * insertion order.
+ *
+ * Returns: (transfer container) (element-type Meta.Window): The list of windows
+ */
+GList *
+shell_window_preview_layout_get_windows (ShellWindowPreviewLayout *self)
+{
+  ShellWindowPreviewLayoutPrivate *priv;
+  GList *windows = NULL;
+  GHashTableIter iter;
+  gpointer value;
+
+  priv = shell_window_preview_layout_get_instance_private (self);
+
+  g_hash_table_iter_init (&iter, priv->windows);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      WindowInfo *window_info = value;
+
+      windows = g_list_prepend (windows, window_info->window);
+    }
+
+  return windows;
+}
diff --git a/src/shell-window-preview-layout.h b/src/shell-window-preview-layout.h
new file mode 100644
index 0000000000..9376b0da8f
--- /dev/null
+++ b/src/shell-window-preview-layout.h
@@ -0,0 +1,33 @@
+#ifndef __SHELL_WINDOW_PREVIEW_LAYOUT_H__
+#define __SHELL_WINDOW_PREVIEW_LAYOUT_H__
+
+G_BEGIN_DECLS
+
+#include <clutter/clutter.h>
+
+#define SHELL_TYPE_WINDOW_PREVIEW_LAYOUT (shell_window_preview_layout_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWindowPreviewLayout, shell_window_preview_layout,
+                      SHELL, WINDOW_PREVIEW_LAYOUT, ClutterLayoutManager)
+
+typedef struct _ShellWindowPreviewLayout ShellWindowPreviewLayout;
+typedef struct _ShellWindowPreviewLayoutPrivate ShellWindowPreviewLayoutPrivate;
+
+struct _ShellWindowPreviewLayout
+{
+  /*< private >*/
+  ClutterLayoutManager parent;
+
+  ShellWindowPreviewLayoutPrivate *priv;
+};
+
+ClutterActor * shell_window_preview_layout_add_window (ShellWindowPreviewLayout  *self,
+                                                       MetaWindow *window);
+
+void  shell_window_preview_layout_remove_window (ShellWindowPreviewLayout  *self,
+                                                 MetaWindow *window);
+
+GList * shell_window_preview_layout_get_windows (ShellWindowPreviewLayout  *self);
+
+G_END_DECLS
+
+#endif /* __SHELL_WINDOW_PREVIEW_LAYOUT_H__ */


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