8c40b48a09
The WindowInfo allocated when adding a window was not getting free'd when the window was getting removed again or the layout was getting disposed. Also the hash table in which the WindowInfos are stored was not getting free'd on destruction either. Both could result in small leaks after closing the overview. Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5238 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2256>
488 lines
15 KiB
C
488 lines
15 KiB
C
#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_finalize (GObject *gobject)
|
|
{
|
|
ShellWindowPreviewLayout *self = SHELL_WINDOW_PREVIEW_LAYOUT (gobject);
|
|
ShellWindowPreviewLayoutPrivate *priv;
|
|
|
|
priv = shell_window_preview_layout_get_instance_private (self);
|
|
|
|
g_hash_table_destroy (priv->windows);
|
|
|
|
G_OBJECT_CLASS (shell_window_preview_layout_parent_class)->finalize (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_full (NULL, NULL, NULL,
|
|
(GDestroyNotify) g_free);
|
|
}
|
|
|
|
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->finalize = shell_window_preview_layout_finalize;
|
|
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;
|
|
}
|