mutter/src/tests/meta-test-shell.c
Bilal Elmoussaoui 38a4b2e102 compositor: Drop get_stage_for_display helper
As one can get that from the backend/compositor directly.
Expose the necessary functions to allow doing so.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4131>
2024-12-11 18:28:57 +01:00

907 lines
27 KiB
C

/*
* Copyright (c) 2008 Intel Corp.
* Copyright (c) 2023 Red Hat
*
* 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 2 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/>.
*/
#include "config.h"
#include "tests/meta-test-shell.h"
#include <string.h>
#include "clutter/clutter.h"
#include "meta/meta-backend.h"
#include "meta/meta-background-actor.h"
#include "meta/meta-background-content.h"
#include "meta/meta-background-group.h"
#include "meta/meta-context.h"
#include "meta/meta-monitor-manager.h"
#include "meta/meta-plugin.h"
#include "meta/util.h"
#include "meta/window.h"
enum
{
PROP_0,
PROP_OPTIONS,
N_PROPS
};
static GParamSpec *obj_props[N_PROPS];
typedef enum
{
ANIMATION_DESTROY,
ANIMATION_MINIMIZE,
ANIMATION_MAP,
ANIMATION_SWITCH,
} Animation;
static unsigned int animation_durations[] = {
100, /* destroy */
250, /* minimize */
250, /* map */
500, /* switch */
};
#define ACTOR_DATA_KEY "-test-shell-actor-data"
#define DISPLAY_TILE_PREVIEW_DATA_KEY "-test-shell-display-tile-preview-data"
struct _MetaTestShell
{
MetaPlugin parent;
ClutterTimeline *switch_workspace1_timeline;
ClutterTimeline *switch_workspace2_timeline;
ClutterActor *desktop1;
ClutterActor *desktop2;
ClutterActor *background_group;
CoglColor *background_color;
gboolean disable_animations;
struct {
ClutterGrab *grab;
ClutterActor *prev_focus;
} overview;
gboolean show_stage;
};
typedef struct _ActorPrivate
{
ClutterActor *orig_parent;
ClutterTimeline *minimize_timeline;
ClutterTimeline *destroy_timeline;
ClutterTimeline *map_timeline;
guint minimize_stopped_id;
} ActorPrivate;
typedef struct
{
ClutterActor *actor;
MetaPlugin *plugin;
gpointer effect_data;
} EffectCompleteData;
typedef struct _DisplayTilePreview
{
ClutterActor *actor;
MtkRectangle tile_rect;
} DisplayTilePreview;
G_DEFINE_TYPE (MetaTestShell, meta_test_shell, META_TYPE_PLUGIN)
static GQuark actor_data_quark = 0;
static GQuark display_tile_preview_data_quark = 0;
static void
free_actor_private (gpointer data)
{
ActorPrivate *actor_priv = data;
g_clear_handle_id (&actor_priv->minimize_stopped_id, g_source_remove);
g_free (data);
}
static ActorPrivate *
get_actor_private (MetaWindowActor *actor)
{
ActorPrivate *actor_priv = g_object_get_qdata (G_OBJECT (actor), actor_data_quark);
if (G_UNLIKELY (actor_data_quark == 0))
actor_data_quark = g_quark_from_static_string (ACTOR_DATA_KEY);
if (G_UNLIKELY (!actor_priv))
{
actor_priv = g_new0 (ActorPrivate, 1);
g_object_set_qdata_full (G_OBJECT (actor),
actor_data_quark, actor_priv,
free_actor_private);
}
return actor_priv;
}
static unsigned int
get_animation_duration (MetaTestShell *test_shell,
Animation animation)
{
if (test_shell->disable_animations)
return 0;
return animation_durations[animation];
}
static ClutterTimeline *
actor_animate (MetaTestShell *test_shell,
ClutterActor *actor,
ClutterAnimationMode mode,
Animation animation,
const char *first_property,
...)
{
va_list args;
ClutterTransition *transition;
clutter_actor_save_easing_state (actor);
clutter_actor_set_easing_mode (actor, mode);
clutter_actor_set_easing_duration (actor, get_animation_duration (test_shell,
animation));
va_start (args, first_property);
g_object_set_valist (G_OBJECT (actor), first_property, args);
va_end (args);
transition = clutter_actor_get_transition (actor, first_property);
clutter_actor_restore_easing_state (actor);
return CLUTTER_TIMELINE (transition);
}
static void
finish_timeline (ClutterTimeline *timeline)
{
g_object_ref (timeline);
clutter_timeline_stop (timeline);
g_object_unref (timeline);
}
static void
kill_workspace_switch_animation (MetaTestShell *test_shell)
{
if (test_shell->switch_workspace1_timeline)
{
g_autoptr (ClutterTimeline) timeline1 = NULL;
g_autoptr (ClutterTimeline) timeline2 = NULL;
timeline1 = g_object_ref (test_shell->switch_workspace1_timeline);
timeline2 = g_object_ref (test_shell->switch_workspace2_timeline);
finish_timeline (timeline1);
finish_timeline (timeline2);
}
}
static void
on_switch_workspace_effect_stopped (ClutterTimeline *timeline,
gboolean is_finished,
gpointer data)
{
MetaPlugin *plugin = META_PLUGIN (data);
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
MetaDisplay *display = meta_plugin_get_display (plugin);
GList *l = meta_get_window_actors (display);
while (l)
{
ClutterActor *a = l->data;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (a);
ActorPrivate *actor_priv = get_actor_private (window_actor);
if (actor_priv->orig_parent)
{
g_object_ref (a);
clutter_actor_remove_child (clutter_actor_get_parent (a), a);
clutter_actor_add_child (actor_priv->orig_parent, a);
g_object_unref (a);
actor_priv->orig_parent = NULL;
}
l = l->next;
}
clutter_actor_destroy (test_shell->desktop1);
clutter_actor_destroy (test_shell->desktop2);
test_shell->switch_workspace1_timeline = NULL;
test_shell->switch_workspace2_timeline = NULL;
test_shell->desktop1 = NULL;
test_shell->desktop2 = NULL;
meta_plugin_switch_workspace_completed (plugin);
}
static void
reload_backgrounds (MetaTestShell *test_shell)
{
MetaDisplay *display = meta_plugin_get_display (META_PLUGIN (test_shell));
GRand *rand;
int i, n;
rand = g_rand_new_with_seed (123456);
clutter_actor_destroy_all_children (test_shell->background_group);
n = meta_display_get_n_monitors (display);
for (i = 0; i < n; i++)
{
MetaBackgroundContent *background_content;
ClutterContent *content;
MtkRectangle rect;
ClutterActor *background_actor;
meta_display_get_monitor_geometry (display, i, &rect);
background_actor = meta_background_actor_new (display, i);
content = clutter_actor_get_content (background_actor);
background_content = META_BACKGROUND_CONTENT (content);
clutter_actor_set_position (background_actor, rect.x, rect.y);
clutter_actor_set_size (background_actor, rect.width, rect.height);
if (test_shell->background_color)
{
g_autoptr (MetaBackground) background = NULL;
background = meta_background_new (display);
meta_background_set_color (background, test_shell->background_color);
meta_background_content_set_background (background_content, background);
}
else
{
g_autoptr (MetaBackground) background = NULL;
uint8_t red;
uint8_t green;
uint8_t blue;
blue = g_rand_int_range (rand, 0, 255);
green = g_rand_int_range (rand, 0, 255);
red = g_rand_int_range (rand, 0, 255);
background = meta_background_new (display);
meta_background_set_color (background,
&COGL_COLOR_INIT (red, green, blue, 255));
meta_background_content_set_background (background_content, background);
meta_background_content_set_vignette (background_content,
TRUE, 0.5, 0.5);
}
clutter_actor_add_child (test_shell->background_group, background_actor);
}
g_rand_free (rand);
}
static void
on_monitors_changed (MetaMonitorManager *monitor_manager,
MetaPlugin *plugin)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
reload_backgrounds (test_shell);
}
static void
on_overlay_key (MetaDisplay *display,
MetaTestShell *test_shell)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
if (!test_shell->overview.grab)
{
test_shell->overview.grab = clutter_stage_grab (stage, CLUTTER_ACTOR (stage));
test_shell->overview.prev_focus = clutter_stage_get_key_focus (stage);
clutter_stage_set_key_focus (stage, CLUTTER_ACTOR (stage));
}
else
{
g_clear_pointer (&test_shell->overview.grab, clutter_grab_dismiss);
clutter_stage_set_key_focus (stage,
g_steal_pointer (&test_shell->overview.prev_focus));
}
}
static void
prepare_shutdown (MetaBackend *backend,
MetaTestShell *test_shell)
{
kill_workspace_switch_animation (test_shell);
}
static void
meta_test_shell_start (MetaPlugin *plugin)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
MetaDisplay *display = meta_plugin_get_display (plugin);
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
test_shell->background_group = meta_background_group_new ();
clutter_actor_insert_child_below (meta_get_window_group_for_display (display),
test_shell->background_group, NULL);
g_signal_connect (monitor_manager, "monitors-changed",
G_CALLBACK (on_monitors_changed), plugin);
on_monitors_changed (monitor_manager, plugin);
g_signal_connect (display, "overlay-key",
G_CALLBACK (on_overlay_key), plugin);
g_signal_connect (backend, "prepare-shutdown",
G_CALLBACK (prepare_shutdown),
test_shell);
if (test_shell->show_stage)
clutter_actor_show (meta_backend_get_stage (backend));
}
static void
meta_test_shell_switch_workspace (MetaPlugin *plugin,
int from,
int to,
MetaMotionDirection direction)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
MetaCompositor *compositor;
MetaDisplay *display;
ClutterActor *stage;
ClutterActor *workspace1, *workspace2;
int screen_width, screen_height;
GList *l;
if (from == to)
{
meta_plugin_switch_workspace_completed (plugin);
return;
}
display = meta_plugin_get_display (plugin);
compositor = meta_display_get_compositor (display);
stage = CLUTTER_ACTOR (meta_compositor_get_stage (compositor));
meta_display_get_size (display,
&screen_width,
&screen_height);
workspace1 = clutter_actor_new ();
workspace2 = clutter_actor_new ();
clutter_actor_set_pivot_point (workspace1, 1.0, 1.0);
clutter_actor_set_size (workspace1,
screen_width,
screen_height);
clutter_actor_set_size (workspace2,
screen_width,
screen_height);
clutter_actor_set_scale (workspace1, 0.0, 0.0);
clutter_actor_add_child (stage, workspace1);
clutter_actor_add_child (stage, workspace2);
for (l = g_list_last (meta_get_window_actors (display)); l; l = l->prev)
{
MetaWindowActor *window_actor = l->data;
ActorPrivate *actor_priv = get_actor_private (window_actor);
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
MetaWindow *window;
MetaWorkspace *workspace;
int workspace_idx;
window = meta_window_actor_get_meta_window (window_actor);
workspace = meta_window_get_workspace (window);
if (!workspace)
{
clutter_actor_hide (actor);
actor_priv->orig_parent = NULL;
continue;
}
if (meta_window_is_on_all_workspaces (window))
{
actor_priv->orig_parent = NULL;
continue;
}
workspace_idx = meta_workspace_index (workspace);
if (workspace_idx == to || workspace_idx == from)
{
ClutterActor *parent = workspace_idx == to ? workspace1
: workspace2;
actor_priv->orig_parent = clutter_actor_get_parent (actor);
g_object_ref (actor);
clutter_actor_remove_child (clutter_actor_get_parent (actor),
actor);
clutter_actor_add_child (parent, actor);
clutter_actor_set_child_below_sibling (parent, actor, NULL);
g_object_unref (actor);
continue;
}
clutter_actor_hide (actor);
actor_priv->orig_parent = NULL;
}
test_shell->desktop1 = workspace1;
test_shell->desktop2 = workspace2;
test_shell->switch_workspace1_timeline =
actor_animate (test_shell,
workspace1, CLUTTER_EASE_IN_SINE,
ANIMATION_SWITCH,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
g_signal_connect (test_shell->switch_workspace1_timeline,
"stopped",
G_CALLBACK (on_switch_workspace_effect_stopped),
plugin);
test_shell->switch_workspace2_timeline =
actor_animate (test_shell,
workspace2, CLUTTER_EASE_IN_SINE,
ANIMATION_SWITCH,
"scale-x", 0.0,
"scale-y", 0.0,
NULL);
}
static void
restore_scale_idle (gpointer user_data)
{
EffectCompleteData *data = user_data;
MetaPlugin *plugin = data->plugin;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
double original_scale = *(double *) data->effect_data;
ActorPrivate *actor_priv;
actor_priv = get_actor_private (META_WINDOW_ACTOR (data->actor));
actor_priv->minimize_timeline = NULL;
actor_priv->minimize_stopped_id = 0;
clutter_actor_hide (data->actor);
clutter_actor_set_scale (data->actor, original_scale, original_scale);
meta_plugin_minimize_completed (plugin, window_actor);
g_free (data->effect_data);
g_free (data);
}
static void
on_minimize_effect_stopped (ClutterTimeline *timeline,
gboolean is_finished,
EffectCompleteData *data)
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
ActorPrivate *actor_priv = get_actor_private (window_actor);
actor_priv->minimize_stopped_id =
g_idle_add_once (restore_scale_idle, data);
}
static void
meta_test_shell_minimize (MetaPlugin *plugin,
MetaWindowActor *window_actor)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
MetaWindowType type;
MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
ClutterTimeline *timeline = NULL;
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
type = meta_window_get_window_type (window);
if (type == META_WINDOW_NORMAL)
{
timeline = actor_animate (test_shell,
actor,
CLUTTER_EASE_IN_SINE,
ANIMATION_MINIMIZE,
"scale-x", 0.0,
"scale-y", 0.0,
"x", (double) 0,
"y", (double) 0,
NULL);
}
if (timeline)
{
EffectCompleteData *data;
ActorPrivate *actor_priv = get_actor_private (window_actor);
double scale_x, scale_y;
data = g_new0 (EffectCompleteData, 1);
actor_priv->minimize_timeline = timeline;
data->plugin = plugin;
data->actor = actor;
data->effect_data = g_new0 (double, 1);
clutter_actor_get_scale (actor, &scale_x, &scale_y);
g_assert_cmpfloat (scale_x, ==, scale_y);
*((double *) data->effect_data) = scale_x;
g_signal_connect (actor_priv->minimize_timeline, "stopped",
G_CALLBACK (on_minimize_effect_stopped),
data);
g_clear_handle_id (&actor_priv->minimize_stopped_id, g_source_remove);
}
else
{
meta_plugin_minimize_completed (plugin, window_actor);
}
}
static void
on_map_effect_stopped (ClutterTimeline *timeline,
gboolean is_finished,
EffectCompleteData *data)
{
MetaPlugin *plugin = data->plugin;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
ActorPrivate *actor_priv = get_actor_private (window_actor);
actor_priv->map_timeline = NULL;
meta_plugin_map_completed (plugin, window_actor);
g_free (data);
}
static void
meta_test_shell_map (MetaPlugin *plugin,
MetaWindowActor *window_actor)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
MetaWindowType type;
type = meta_window_get_window_type (window);
if (type == META_WINDOW_NORMAL)
{
EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
ActorPrivate *actor_priv = get_actor_private (window_actor);
clutter_actor_set_pivot_point (actor, 0.5, 0.5);
clutter_actor_set_opacity (actor, 0);
clutter_actor_set_scale (actor, 0.5, 0.5);
clutter_actor_show (actor);
actor_priv->map_timeline = actor_animate (test_shell,
actor,
CLUTTER_EASE_OUT_QUAD,
ANIMATION_MAP,
"opacity", 255,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
if (actor_priv->map_timeline)
{
data->actor = actor;
data->plugin = plugin;
g_signal_connect (actor_priv->map_timeline, "stopped",
G_CALLBACK (on_map_effect_stopped),
data);
}
else
{
g_free (data);
meta_plugin_map_completed (plugin, window_actor);
}
}
else
{
meta_plugin_map_completed (plugin, window_actor);
}
}
static void
on_destroy_effect_stopped (ClutterTimeline *timeline,
gboolean is_finished,
EffectCompleteData *data)
{
MetaPlugin *plugin = data->plugin;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
ActorPrivate *actor_priv = get_actor_private (window_actor);
actor_priv->destroy_timeline = NULL;
meta_plugin_destroy_completed (plugin, window_actor);
}
static void
meta_test_shell_destroy (MetaPlugin *plugin,
MetaWindowActor *window_actor)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
MetaWindowType type;
ClutterTimeline *timeline = NULL;
type = meta_window_get_window_type (window);
if (type == META_WINDOW_NORMAL)
{
timeline = actor_animate (test_shell,
actor,
CLUTTER_EASE_OUT_QUAD,
ANIMATION_DESTROY,
"opacity", 0,
"scale-x", 0.8,
"scale-y", 0.8,
NULL);
}
if (timeline)
{
EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
ActorPrivate *actor_priv = get_actor_private (window_actor);
actor_priv->destroy_timeline = timeline;
data->plugin = plugin;
data->actor = actor;
g_signal_connect (actor_priv->destroy_timeline, "stopped",
G_CALLBACK (on_destroy_effect_stopped),
data);
}
else
{
meta_plugin_destroy_completed (plugin, window_actor);
}
}
static void
free_display_tile_preview (DisplayTilePreview *preview)
{
if (preview)
{
clutter_actor_destroy (preview->actor);
g_free (preview);
}
}
static void
on_display_closing (MetaDisplay *display,
DisplayTilePreview *preview)
{
free_display_tile_preview (preview);
}
static DisplayTilePreview *
get_display_tile_preview (MetaDisplay *display)
{
DisplayTilePreview *preview;
if (!display_tile_preview_data_quark)
{
display_tile_preview_data_quark =
g_quark_from_static_string (DISPLAY_TILE_PREVIEW_DATA_KEY);
}
preview = g_object_get_qdata (G_OBJECT (display),
display_tile_preview_data_quark);
if (!preview)
{
preview = g_new0 (DisplayTilePreview, 1);
preview->actor = clutter_actor_new ();
clutter_actor_set_background_color (preview->actor, &COGL_COLOR_INIT (0, 0, 255, 255));
clutter_actor_set_opacity (preview->actor, 100);
clutter_actor_add_child (meta_get_window_group_for_display (display),
preview->actor);
g_signal_connect (display,
"closing",
G_CALLBACK (on_display_closing),
preview);
g_object_set_qdata (G_OBJECT (display),
display_tile_preview_data_quark,
preview);
}
return preview;
}
static void
meta_test_shell_show_tile_preview (MetaPlugin *plugin,
MetaWindow *window,
MtkRectangle *tile_rect,
int tile_monitor_number)
{
MetaDisplay *display = meta_plugin_get_display (plugin);
DisplayTilePreview *preview = get_display_tile_preview (display);
ClutterActor *window_actor;
if (clutter_actor_is_visible (preview->actor) &&
preview->tile_rect.x == tile_rect->x &&
preview->tile_rect.y == tile_rect->y &&
preview->tile_rect.width == tile_rect->width &&
preview->tile_rect.height == tile_rect->height)
return;
clutter_actor_set_position (preview->actor, tile_rect->x, tile_rect->y);
clutter_actor_set_size (preview->actor, tile_rect->width, tile_rect->height);
clutter_actor_show (preview->actor);
window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
clutter_actor_set_child_below_sibling (clutter_actor_get_parent (preview->actor),
preview->actor,
window_actor);
preview->tile_rect = *tile_rect;
}
static void
meta_test_shell_hide_tile_preview (MetaPlugin *plugin)
{
MetaDisplay *display = meta_plugin_get_display (plugin);
DisplayTilePreview *preview = get_display_tile_preview (display);
clutter_actor_hide (preview->actor);
}
static void
meta_test_shell_kill_switch_workspace (MetaPlugin *plugin)
{
MetaTestShell *test_shell = META_TEST_SHELL (plugin);
kill_workspace_switch_animation (test_shell);
}
static void
meta_test_shell_kill_window_effects (MetaPlugin *plugin,
MetaWindowActor *window_actor)
{
ActorPrivate *actor_priv;
actor_priv = get_actor_private (window_actor);
if (actor_priv->minimize_timeline)
finish_timeline (actor_priv->minimize_timeline);
if (actor_priv->map_timeline)
finish_timeline (actor_priv->map_timeline);
if (actor_priv->destroy_timeline)
finish_timeline (actor_priv->destroy_timeline);
}
static void
process_options (MetaTestShell *test_shell,
GVariant *options)
{
gboolean show_stage;
if (!options)
return;
if (g_variant_lookup (options, "show-stage", "b", &show_stage))
test_shell->show_stage = show_stage;
}
static void
meta_test_shell_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaTestShell *test_shell = META_TEST_SHELL (object);
switch (prop_id)
{
case PROP_OPTIONS:
process_options (test_shell, g_value_get_variant (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_test_shell_finalize (GObject *object)
{
MetaTestShell *test_shell = META_TEST_SHELL (object);
g_clear_pointer (&test_shell->background_color, cogl_color_free);
G_OBJECT_CLASS (meta_test_shell_parent_class)->finalize (object);
}
static void
meta_test_shell_class_init (MetaTestShellClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaPluginClass *plugin_class = META_PLUGIN_CLASS (klass);
object_class->set_property = meta_test_shell_set_property;
object_class->finalize = meta_test_shell_finalize;
plugin_class->start = meta_test_shell_start;
plugin_class->map = meta_test_shell_map;
plugin_class->minimize = meta_test_shell_minimize;
plugin_class->destroy = meta_test_shell_destroy;
plugin_class->switch_workspace = meta_test_shell_switch_workspace;
plugin_class->show_tile_preview = meta_test_shell_show_tile_preview;
plugin_class->hide_tile_preview = meta_test_shell_hide_tile_preview;
plugin_class->kill_window_effects = meta_test_shell_kill_window_effects;
plugin_class->kill_switch_workspace = meta_test_shell_kill_switch_workspace;
obj_props[PROP_OPTIONS] =
g_param_spec_variant ("options", NULL, NULL,
G_VARIANT_TYPE_VARDICT,
NULL,
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_WRITABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
}
static void
meta_test_shell_init (MetaTestShell *test_shell)
{
test_shell->show_stage = TRUE;
}
void
meta_test_shell_set_background_color (MetaTestShell *test_shell,
CoglColor color)
{
if (test_shell->background_color &&
cogl_color_equal (test_shell->background_color, &color))
return;
g_clear_pointer (&test_shell->background_color, cogl_color_free);
test_shell->background_color = cogl_color_copy (&color);
reload_backgrounds (test_shell);
}
void
meta_test_shell_disable_animations (MetaTestShell *test_shell)
{
test_shell->disable_animations = TRUE;
}