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>
This commit is contained in:
parent
3eb40df06b
commit
04c781674c
@ -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() {
|
||||
|
@ -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'
|
||||
]
|
||||
|
472
src/shell-window-preview-layout.c
Normal file
472
src/shell-window-preview-layout.c
Normal file
@ -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;
|
||||
}
|
33
src/shell-window-preview-layout.h
Normal file
33
src/shell-window-preview-layout.h
Normal file
@ -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__ */
|
Loading…
Reference in New Issue
Block a user