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;
|
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({
|
var WindowPreview = GObject.registerClass({
|
||||||
Properties: {
|
Properties: {
|
||||||
'overlay-enabled': GObject.ParamSpec.boolean(
|
'overlay-enabled': GObject.ParamSpec.boolean(
|
||||||
@ -235,7 +61,7 @@ var WindowPreview = GObject.registerClass({
|
|||||||
// the initialization of the actor if that layout manager keeps track
|
// the initialization of the actor if that layout manager keeps track
|
||||||
// of its container, so set the layout manager after creating the
|
// of its container, so set the layout manager after creating the
|
||||||
// container
|
// container
|
||||||
this._windowContainer.layout_manager = new WindowPreviewLayout();
|
this._windowContainer.layout_manager = new Shell.WindowPreviewLayout();
|
||||||
this.add_child(this._windowContainer);
|
this.add_child(this._windowContainer);
|
||||||
|
|
||||||
this._addWindow(metaWindow);
|
this._addWindow(metaWindow);
|
||||||
@ -601,7 +427,7 @@ var WindowPreview = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
_addWindow(metaWindow) {
|
_addWindow(metaWindow) {
|
||||||
const clone = this._windowContainer.layout_manager.addWindow(metaWindow);
|
const clone = this._windowContainer.layout_manager.add_window(metaWindow);
|
||||||
if (!clone)
|
if (!clone)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -620,7 +446,7 @@ var WindowPreview = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
_deleteAll() {
|
_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
|
// Delete all windows, starting from the bottom-most (most-modal) one
|
||||||
for (const window of windows.reverse())
|
for (const window of windows.reverse())
|
||||||
@ -645,7 +471,7 @@ var WindowPreview = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
_hasAttachedDialogs() {
|
_hasAttachedDialogs() {
|
||||||
return this._windowContainer.layout_manager.getWindows().length > 1;
|
return this._windowContainer.layout_manager.get_windows().length > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateAttachedDialogs() {
|
_updateAttachedDialogs() {
|
||||||
|
@ -109,6 +109,7 @@ libshell_public_headers = [
|
|||||||
'shell-tray-icon.h',
|
'shell-tray-icon.h',
|
||||||
'shell-tray-manager.h',
|
'shell-tray-manager.h',
|
||||||
'shell-util.h',
|
'shell-util.h',
|
||||||
|
'shell-window-preview-layout.h',
|
||||||
'shell-window-tracker.h',
|
'shell-window-tracker.h',
|
||||||
'shell-wm.h'
|
'shell-wm.h'
|
||||||
]
|
]
|
||||||
@ -151,6 +152,7 @@ libshell_sources = [
|
|||||||
'shell-tray-icon.c',
|
'shell-tray-icon.c',
|
||||||
'shell-tray-manager.c',
|
'shell-tray-manager.c',
|
||||||
'shell-util.c',
|
'shell-util.c',
|
||||||
|
'shell-window-preview-layout.c',
|
||||||
'shell-window-tracker.c',
|
'shell-window-tracker.c',
|
||||||
'shell-wm.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