Move MonitorGroup from JS to C

Mostly, anyway.

This commit does a fairly 1:1 port of the objects methods, leaving the
constructor in JS land, with the goal of cutting down on the time spent
in `_interpolateProgress` (which has been sufficient to drop frames)
This commit is contained in:
Zander Brown 2023-10-28 03:14:23 +01:00
parent 4bcad31904
commit fbce396f4a
No known key found for this signature in database
GPG key ID: 25EE3C36E31E5F84
4 changed files with 531 additions and 117 deletions

View file

@ -4,7 +4,6 @@ import Clutter from 'gi://Clutter';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
import St from 'gi://St';
import * as Background from './background.js';
import * as Layout from './layout.js';
@ -14,7 +13,6 @@ import * as Util from '../misc/util.js';
import * as Main from './main.js';
const WINDOW_ANIMATION_TIME = 250;
export const WORKSPACE_SPACING = 100;
export const WorkspaceGroup = GObject.registerClass(
class WorkspaceGroup extends Shell.WorkspaceGroup {
@ -141,33 +139,23 @@ class WorkspaceGroup extends Shell.WorkspaceGroup {
}
});
export const MonitorGroup = GObject.registerClass({
Properties: {
'progress': GObject.ParamSpec.double(
'progress', 'progress', 'progress',
GObject.ParamFlags.READWRITE,
-Infinity, Infinity, 0),
},
}, class MonitorGroup extends St.Widget {
export const MonitorGroup = GObject.registerClass(
class MonitorGroup extends Shell.MonitorGroup {
_init(monitor, workspaceIndices, movingWindow) {
super._init({
clip_to_allocation: true,
style_class: 'workspace-animation',
index: monitor.index,
monitor_width: monitor.width,
monitor_height: monitor.height,
});
this._monitor = monitor;
const constraint = new Layout.MonitorConstraint({index: monitor.index});
this.add_constraint(constraint);
this._container = new Clutter.Actor();
this.add_child(this._container);
const stickyGroup = new WorkspaceGroup(null, monitor, movingWindow);
this.add_child(stickyGroup);
this._workspaceGroups = [];
const workspaceManager = global.workspace_manager;
const vertical = workspaceManager.layout_rows === -1;
const activeWorkspace = workspaceManager.get_active_workspace();
@ -186,21 +174,17 @@ export const MonitorGroup = GObject.registerClass({
y -= Main.panel.height;
}
const group = new WorkspaceGroup(ws, monitor, movingWindow);
this._workspaceGroups.push(group);
this._container.add_child(group);
group.set_position(x, y);
this.add_group(new WorkspaceGroup(ws, monitor, movingWindow), x, y);
if (vertical)
y += this.baseDistance;
y += this.base_distance;
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
x -= this.baseDistance;
x -= this.base_distance;
else
x += this.baseDistance;
x += this.base_distance;
}
this.progress = this.getWorkspaceProgress(activeWorkspace);
this.progress = this.get_workspace_progress(activeWorkspace);
if (monitor.index === Main.layoutManager.primaryIndex) {
this._workspacesAdjustment = Main.createWorkspacesAdjustment(this);
@ -221,88 +205,6 @@ export const MonitorGroup = GObject.registerClass({
});
}
}
get baseDistance() {
const spacing = WORKSPACE_SPACING * St.ThemeContext.get_for_stage(global.stage).scale_factor;
if (global.workspace_manager.layout_rows === -1)
return this._monitor.height + spacing;
else
return this._monitor.width + spacing;
}
get progress() {
if (global.workspace_manager.layout_rows === -1)
return -this._container.y / this.baseDistance;
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
return this._container.x / this.baseDistance;
else
return -this._container.x / this.baseDistance;
}
set progress(p) {
if (global.workspace_manager.layout_rows === -1)
this._container.y = -Math.round(p * this.baseDistance);
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
this._container.x = Math.round(p * this.baseDistance);
else
this._container.x = -Math.round(p * this.baseDistance);
this.notify('progress');
}
get index() {
return this._monitor.index;
}
getWorkspaceProgress(workspace) {
const group = this._workspaceGroups.find(g =>
g.workspace.index() === workspace.index());
return this._getWorkspaceGroupProgress(group);
}
_getWorkspaceGroupProgress(group) {
if (global.workspace_manager.layout_rows === -1)
return group.y / this.baseDistance;
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
return -group.x / this.baseDistance;
else
return group.x / this.baseDistance;
}
getSnapPoints() {
return this._workspaceGroups.map(g =>
this._getWorkspaceGroupProgress(g));
}
findClosestWorkspace(progress) {
const distances = this.getSnapPoints().map(p =>
Math.abs(p - progress));
const index = distances.indexOf(Math.min(...distances));
return this._workspaceGroups[index].workspace;
}
_interpolateProgress(progress, monitorGroup) {
if (this.index === monitorGroup.index)
return progress;
const points1 = monitorGroup.getSnapPoints();
const points2 = this.getSnapPoints();
const upper = points1.indexOf(points1.find(p => p >= progress));
const lower = points1.indexOf(points1.slice().reverse().find(p => p <= progress));
if (points1[upper] === points1[lower])
return points2[upper];
const t = (progress - points1[lower]) / (points1[upper] - points1[lower]);
return points2[lower] + (points2[upper] - points2[lower]) * t;
}
updateSwipeForMonitor(progress, monitorGroup) {
this.progress = this._interpolateProgress(progress, monitorGroup);
}
});
export class WorkspaceAnimationController {
@ -414,8 +316,8 @@ export class WorkspaceAnimationController {
const toWs = global.workspace_manager.get_workspace_by_index(to);
for (const monitorGroup of this._switchData.monitors) {
monitorGroup.progress = monitorGroup.getWorkspaceProgress(fromWs);
const progress = monitorGroup.getWorkspaceProgress(toWs);
monitorGroup.progress = monitorGroup.get_workspace_progress(fromWs);
const progress = monitorGroup.get_workspace_progress(toWs);
const params = {
duration: WINDOW_ANIMATION_TIME,
@ -461,12 +363,12 @@ export class WorkspaceAnimationController {
}
const monitorGroup = this._findMonitorGroup(monitor);
const baseDistance = monitorGroup.baseDistance;
const baseDistance = monitorGroup.base_distance;
const progress = monitorGroup.progress;
const closestWs = monitorGroup.findClosestWorkspace(progress);
const cancelProgress = monitorGroup.getWorkspaceProgress(closestWs);
const points = monitorGroup.getSnapPoints();
const closestWs = monitorGroup.find_closest_workspace(progress);
const cancelProgress = monitorGroup.get_workspace_progress(closestWs);
const points = monitorGroup.get_snap_points();
this._switchData.baseMonitorGroup = monitorGroup;
@ -478,7 +380,7 @@ export class WorkspaceAnimationController {
return;
for (const monitorGroup of this._switchData.monitors)
monitorGroup.updateSwipeForMonitor(progress, this._switchData.baseMonitorGroup);
monitorGroup.update_swipe_for_monitor(progress, this._switchData.baseMonitorGroup);
}
_switchWorkspaceEnd(tracker, duration, endProgress) {
@ -488,11 +390,11 @@ export class WorkspaceAnimationController {
const switchData = this._switchData;
switchData.gestureActivated = true;
const newWs = switchData.baseMonitorGroup.findClosestWorkspace(endProgress);
const newWs = switchData.baseMonitorGroup.find_closest_workspace(endProgress);
const endTime = Clutter.get_current_event_time();
for (const monitorGroup of this._switchData.monitors) {
const progress = monitorGroup.getWorkspaceProgress(newWs);
const progress = monitorGroup.get_workspace_progress(newWs);
const params = {
duration,

View file

@ -104,6 +104,7 @@ libshell_public_headers = [
'shell-global.h',
'shell-invert-lightness-effect.h',
'shell-action-modes.h',
'shell-monitor-group.h',
'shell-mount-operation.h',
'shell-perf-log.h',
'shell-screenshot.h',
@ -146,6 +147,7 @@ libshell_sources = [
'shell-invert-lightness-effect.c',
'shell-keyring-prompt.c',
'shell-keyring-prompt.h',
'shell-monitor-group.c',
'shell-mount-operation.c',
'shell-perf-log.c',
'shell-polkit-authentication-agent.c',

453
src/shell-monitor-group.c Normal file
View file

@ -0,0 +1,453 @@
/* shell-monitor-group.c
*
* Copyright 2023 Zander Brown <zbrown@gnome.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include <clutter/clutter.h>
#include "shell-monitor-group.h"
#include "shell-workspace-group.h"
#include "shell-global.h"
#define WORKSPACE_SPACING 100
typedef struct _ShellMonitorGroupPrivate ShellMonitorGroupPrivate;
struct _ShellMonitorGroupPrivate
{
ClutterActor *container;
GPtrArray *workspace_groups;
int index, width, height;
};
enum
{
PROP_0,
PROP_INDEX,
PROP_MONITOR_WIDTH,
PROP_MONITOR_HEIGHT,
PROP_PROGRESS,
PROP_BASE_DISTANCE,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ShellMonitorGroup, shell_monitor_group, ST_TYPE_WIDGET)
static void
shell_monitor_group_dispose (GObject *object)
{
ShellMonitorGroupPrivate *priv =
shell_monitor_group_get_instance_private (SHELL_MONITOR_GROUP (object));
g_clear_weak_pointer (&priv->container);
g_clear_pointer (&priv->workspace_groups, g_ptr_array_unref);
G_OBJECT_CLASS (shell_monitor_group_parent_class)->dispose (object);
}
static void
shell_monitor_group_set_property (GObject *object,
unsigned int property_id,
const GValue *value,
GParamSpec *pspec)
{
ShellMonitorGroupPrivate *priv =
shell_monitor_group_get_instance_private (SHELL_MONITOR_GROUP (object));
switch (property_id)
{
case PROP_INDEX:
priv->index = g_value_get_int (value);
break;
case PROP_MONITOR_WIDTH:
priv->width = g_value_get_int (value);
clutter_actor_set_size (CLUTTER_ACTOR (object), g_value_get_int (value), priv->height);
break;
case PROP_MONITOR_HEIGHT:
priv->height = g_value_get_int (value);
clutter_actor_set_size (CLUTTER_ACTOR (object), priv->width, g_value_get_int (value));
break;
case PROP_PROGRESS:
shell_monitor_group_set_progress (SHELL_MONITOR_GROUP (object),
g_value_get_double (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
shell_monitor_group_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
ShellMonitorGroup *self = SHELL_MONITOR_GROUP (object);
ShellMonitorGroupPrivate *priv =
shell_monitor_group_get_instance_private (self);
switch (property_id)
{
case PROP_INDEX:
g_value_set_int (value, priv->index);
break;
case PROP_MONITOR_WIDTH:
g_value_set_int (value, priv->width);
break;
case PROP_MONITOR_HEIGHT:
g_value_set_int (value, priv->height);
break;
case PROP_PROGRESS:
g_value_set_double (value,
shell_monitor_group_get_progress (self));
break;
case PROP_BASE_DISTANCE:
g_value_set_double (value,
shell_monitor_group_get_base_distance (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
shell_monitor_group_class_init (ShellMonitorGroupClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = shell_monitor_group_dispose;
object_class->set_property = shell_monitor_group_set_property;
object_class->get_property = shell_monitor_group_get_property;
obj_props[PROP_INDEX] =
g_param_spec_int ("index", NULL, NULL,
0, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
obj_props[PROP_MONITOR_WIDTH] =
g_param_spec_int ("monitor-width", NULL, NULL,
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
obj_props[PROP_MONITOR_HEIGHT] =
g_param_spec_int ("monitor-height", NULL, NULL,
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
obj_props[PROP_PROGRESS] =
g_param_spec_double ("progress", NULL, NULL,
-INFINITY, INFINITY, 0.0,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
obj_props[PROP_BASE_DISTANCE] =
g_param_spec_double ("base-distance", NULL, NULL,
-INFINITY, INFINITY, 0.0,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
}
static void
shell_monitor_group_init (ShellMonitorGroup *self)
{
ShellMonitorGroupPrivate *priv =
shell_monitor_group_get_instance_private (self);
ClutterActor *container = clutter_actor_new ();
g_set_weak_pointer (&priv->container, container);
clutter_actor_add_child (CLUTTER_ACTOR (self), container);
priv->workspace_groups = g_ptr_array_new_with_free_func (g_object_unref);
}
float
shell_monitor_group_get_base_distance (ShellMonitorGroup *self)
{
ShellMonitorGroupPrivate *priv;
ShellGlobal *global = shell_global_get ();
StThemeContext *context;
int scale, spacing, rows;
g_return_val_if_fail (SHELL_IS_MONITOR_GROUP (self), 0.0);
priv = shell_monitor_group_get_instance_private (self);
context = st_theme_context_get_for_stage (shell_global_get_stage (global));
scale = st_theme_context_get_scale_factor (context);
/* TODO: Add mutter API? */
g_object_get (shell_global_get_workspace_manager (global),
"layout-rows", &rows,
NULL);
spacing = WORKSPACE_SPACING * scale;
if (rows == -1)
return priv->height + spacing;
return priv->width + spacing;
}
float
shell_monitor_group_get_progress (ShellMonitorGroup *self)
{
ShellMonitorGroupPrivate *priv;
ShellGlobal *global = shell_global_get ();
int rows;
float base_distance;
g_return_val_if_fail (SHELL_IS_MONITOR_GROUP (self), 0.0);
priv = shell_monitor_group_get_instance_private (self);
/* TODO: Add mutter API? */
g_object_get (shell_global_get_workspace_manager (global),
"layout-rows", &rows,
NULL);
base_distance = shell_monitor_group_get_base_distance (self);
if (rows == -1)
return -clutter_actor_get_y (priv->container) / base_distance;
else if (clutter_actor_get_text_direction (CLUTTER_ACTOR (self)) == CLUTTER_TEXT_DIRECTION_RTL)
return clutter_actor_get_x (priv->container) / base_distance;
else
return -clutter_actor_get_x (priv->container) / base_distance;
}
void
shell_monitor_group_set_progress (ShellMonitorGroup *self,
float progress)
{
ShellMonitorGroupPrivate *priv;
ShellGlobal *global = shell_global_get ();
int rows;
float base_distance;
g_return_if_fail (SHELL_IS_MONITOR_GROUP (self));
priv = shell_monitor_group_get_instance_private (self);
/* TODO: Add mutter API? */
g_object_get (shell_global_get_workspace_manager (global),
"layout-rows", &rows,
NULL);
base_distance = shell_monitor_group_get_base_distance (self);
if (rows == -1)
clutter_actor_set_y (priv->container, -roundf (progress * base_distance));
else if (clutter_actor_get_text_direction (CLUTTER_ACTOR (self)) == CLUTTER_TEXT_DIRECTION_RTL)
clutter_actor_set_x (priv->container, roundf (progress * base_distance));
else
clutter_actor_set_x (priv->container, -roundf (progress * base_distance));
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PROGRESS]);
}
static float
get_workspace_group_progress (ShellMonitorGroup *self,
ShellWorkspaceGroup *group)
{
ShellGlobal *global = shell_global_get ();
int rows;
float base_distance;
/* TODO: Add mutter API? */
g_object_get (shell_global_get_workspace_manager (global),
"layout-rows", &rows,
NULL);
base_distance = shell_monitor_group_get_base_distance (self);
if (rows == -1)
return clutter_actor_get_y (CLUTTER_ACTOR (group)) / base_distance;
else if (clutter_actor_get_text_direction (CLUTTER_ACTOR (self)) == CLUTTER_TEXT_DIRECTION_RTL)
return -clutter_actor_get_x (CLUTTER_ACTOR (group)) / base_distance;
else
return clutter_actor_get_x (CLUTTER_ACTOR (group)) / base_distance;
}
float
shell_monitor_group_get_workspace_progress (ShellMonitorGroup *self,
MetaWorkspace *workspace)
{
ShellMonitorGroupPrivate *priv;
ShellWorkspaceGroup *workspace_group;
int target_index;
g_return_val_if_fail (SHELL_IS_MONITOR_GROUP (self), 0.0);
g_return_val_if_fail (META_IS_WORKSPACE (workspace), 0.0);
priv = shell_monitor_group_get_instance_private (self);
target_index = meta_workspace_index (workspace);
for (size_t i = 0; i < priv->workspace_groups->len; i++)
{
workspace_group = g_ptr_array_index (priv->workspace_groups, i);
if (meta_workspace_index (shell_workspace_group_get_workspace (workspace_group)) == target_index)
return get_workspace_group_progress (self, workspace_group);
}
g_return_val_if_reached (0.0);
}
/**
* shell_monitor_group_get_snap_points:
* @self: the #ShellMonitorGroup
* @n_points: length of @points
* @points: (array length=n_points) (out): array of snap points
*/
void
shell_monitor_group_get_snap_points (ShellMonitorGroup *self,
size_t *n_points,
float *points[])
{
ShellMonitorGroupPrivate *priv;
float *data;
g_return_if_fail (SHELL_IS_MONITOR_GROUP (self));
g_return_if_fail (n_points != NULL);
g_return_if_fail (points != NULL);
priv = shell_monitor_group_get_instance_private (self);
*n_points = priv->workspace_groups->len;
data = g_new0 (float, priv->workspace_groups->len + 1);
for (size_t i = 0; i < priv->workspace_groups->len; i++)
data[i] = get_workspace_group_progress (self, g_ptr_array_index (priv->workspace_groups, i));
*points = data;
}
void
shell_monitor_group_add_group (ShellMonitorGroup *self,
ShellWorkspaceGroup *group,
float x,
float y)
{
ShellMonitorGroupPrivate *priv;
g_return_if_fail (SHELL_IS_MONITOR_GROUP (self));
g_return_if_fail (SHELL_IS_WORKSPACE_GROUP (group));
priv = shell_monitor_group_get_instance_private (self);
g_ptr_array_add (priv->workspace_groups, g_object_ref (group));
clutter_actor_add_child (priv->container, CLUTTER_ACTOR (group));
clutter_actor_set_position (CLUTTER_ACTOR (group), x, y);
}
static float
interpolate_progress (ShellMonitorGroup *self,
float progress,
ShellMonitorGroup *monitor_group)
{
ShellMonitorGroupPrivate *priv =
shell_monitor_group_get_instance_private (self);
ShellMonitorGroupPrivate *monitor_group_priv =
shell_monitor_group_get_instance_private (monitor_group);
size_t points1_len, points2_len;
g_autofree float *points1 = NULL, *points2 = NULL;
size_t upper = 0, lower = 0;
float t;
if (priv->index == monitor_group_priv->index)
return progress;
shell_monitor_group_get_snap_points (monitor_group, &points1_len, &points1);
shell_monitor_group_get_snap_points (self, &points2_len, &points2);
for (size_t i = 0; i < points1_len; i++)
if (points1[i] >= progress)
{
upper = i;
break;
}
for (size_t i = points2_len - 1; i >= 0; i--)
if (points2[i] <= progress)
{
lower = i;
break;
}
if (G_APPROX_VALUE (points1[upper], points1[lower], FLT_EPSILON))
return points2[upper];
t = (progress - points1[lower]) / (points1[upper] - points1[lower]);
return points2[lower] + (points2[upper] - points2[lower]) * t;
}
void
shell_monitor_group_update_swipe_for_monitor (ShellMonitorGroup *self,
float progress,
ShellMonitorGroup *monitor_group)
{
g_return_if_fail (SHELL_IS_MONITOR_GROUP (self));
g_return_if_fail (SHELL_IS_MONITOR_GROUP (monitor_group));
shell_monitor_group_set_progress (self,
interpolate_progress (self, progress, monitor_group));
}
/**
* shell_monitor_group_find_closest_workspace:
* @self: the #ShellMonitorGroup
* @progress: current progress
*
* Returns: (transfer none): the #MetaWorkspace
*/
MetaWorkspace *
shell_monitor_group_find_closest_workspace (ShellMonitorGroup *self,
float progress)
{
ShellMonitorGroupPrivate *priv;
ShellWorkspaceGroup *group, *min_group = NULL;
float min_distance = -1.0, distance, value;
g_return_val_if_fail (SHELL_IS_MONITOR_GROUP (self), NULL);
priv = shell_monitor_group_get_instance_private (self);
for (size_t i = 0; i < priv->workspace_groups->len; i++)
{
group = g_ptr_array_index (priv->workspace_groups, i);
distance = get_workspace_group_progress (self, group);
value = fabsf (distance - progress);
if (G_UNLIKELY (min_distance < 0))
{
min_distance = value;
min_group = group;
}
else
{
min_distance = MIN (min_distance, value);
if (G_APPROX_VALUE (min_distance, value, FLT_EPSILON))
min_group = group;
}
}
return shell_workspace_group_get_workspace (min_group);
}

57
src/shell-monitor-group.h Normal file
View file

@ -0,0 +1,57 @@
/* shell-monitor-group.h
*
* Copyright 2023 Zander Brown <zbrown@gnome.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <st/st.h>
#include <meta/workspace.h>
#include "shell-workspace-group.h"
G_BEGIN_DECLS
#define SHELL_TYPE_MONITOR_GROUP (shell_monitor_group_get_type ())
G_DECLARE_DERIVABLE_TYPE (ShellMonitorGroup, shell_monitor_group, SHELL, MONITOR_GROUP, StWidget)
struct _ShellMonitorGroupClass
{
StWidgetClass parent_class;
};
float shell_monitor_group_get_base_distance (ShellMonitorGroup *self);
float shell_monitor_group_get_progress (ShellMonitorGroup *self);
void shell_monitor_group_set_progress (ShellMonitorGroup *self,
float progress);
float shell_monitor_group_get_workspace_progress (ShellMonitorGroup *self,
MetaWorkspace *workspace);
void shell_monitor_group_get_snap_points (ShellMonitorGroup *self,
size_t *n_points,
float *points[]);
void shell_monitor_group_add_group (ShellMonitorGroup *self,
ShellWorkspaceGroup *group,
float x,
float y);
void shell_monitor_group_update_swipe_for_monitor (ShellMonitorGroup *self,
float progress,
ShellMonitorGroup *monitor_group);
MetaWorkspace *shell_monitor_group_find_closest_workspace (ShellMonitorGroup *self,
float progress);
G_END_DECLS