diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index 0520cae8a436c7667911fe479963acc349f41456..d390e565ac07bb804206ebe8287c0bc76b383120 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -62,6 +62,9 @@ void clutter_stage_emit_after_paint (ClutterStage void clutter_stage_after_update (ClutterStage *stage, ClutterStageView *view, ClutterFrame *frame); +void clutter_stage_frame_discarded (ClutterStage *stage, + ClutterStageView *view, + ClutterFrame *frame); CLUTTER_EXPORT void _clutter_stage_set_window (ClutterStage *stage, diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index 661d0934d70f0cba64f0c0df981a94ad1dff3951..2e0137bba6a0294c50bb4b4e145207cac1567f3c 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -1050,11 +1050,12 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return CLUTTER_FRAME_RESULT_IDLE; - if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage))) - return CLUTTER_FRAME_RESULT_IDLE; - - if (!clutter_actor_is_mapped (CLUTTER_ACTOR (stage))) - return CLUTTER_FRAME_RESULT_IDLE; + if (!clutter_actor_is_realized (CLUTTER_ACTOR (stage)) || + !clutter_actor_is_mapped (CLUTTER_ACTOR (stage))) + { + clutter_stage_frame_discarded (stage, view, frame); + return CLUTTER_FRAME_RESULT_IDLE; + } if (clutter_context_get_show_fps (context)) begin_frame_timing_measurement (view); diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 2d390bbf50bebce5a7e01552f6d66f39c8304d8c..1e8496e5287fc5261c3ef56b313b7b85d292dcf6 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -535,6 +535,16 @@ clutter_stage_after_update (ClutterStage *stage, priv->update_scheduled = FALSE; } +void +clutter_stage_frame_discarded (ClutterStage *stage, + ClutterStageView *view, + ClutterFrame *frame) +{ + ClutterStagePrivate *priv = clutter_stage_get_instance_private (stage); + + priv->update_scheduled = FALSE; +} + static gboolean clutter_stage_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index b1f465c01e2c3f5a412fabb64aadb7960e4924b1..a2b54071651d0318dc66ae706c772ad2ee07d6d3 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -91,6 +91,7 @@ void meta_compositor_grab_end (MetaCompositor *compositor); void meta_compositor_destroy (MetaCompositor *compositor); gboolean meta_compositor_manage (MetaCompositor *compositor, + GVariant *plugin_options, GError **error); void meta_compositor_unmanage (MetaCompositor *compositor); diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index e0b44a47e3ed0ff044e46cd404be1def17242de8..51b4228bf856aa650d186a7c1ec3ec63af2f7515 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -319,6 +319,7 @@ meta_compositor_create_view (MetaCompositor *compositor, gboolean meta_compositor_manage (MetaCompositor *compositor, + GVariant *plugin_options, GError **error) { MetaCompositorPrivate *priv = @@ -346,7 +347,7 @@ meta_compositor_manage (MetaCompositor *compositor, if (!META_COMPOSITOR_GET_CLASS (compositor)->manage (compositor, error)) return FALSE; - priv->plugin_mgr = meta_plugin_manager_new (compositor); + priv->plugin_mgr = meta_plugin_manager_new (compositor, plugin_options); meta_plugin_manager_start (priv->plugin_mgr); return TRUE; diff --git a/src/compositor/meta-plugin-manager.c b/src/compositor/meta-plugin-manager.c index fb38917b5ce3cfb6c3af0d04923f9059331bb635..65b7fa66c89b196169f549eccc092acbbfc96596 100644 --- a/src/compositor/meta-plugin-manager.c +++ b/src/compositor/meta-plugin-manager.c @@ -113,7 +113,8 @@ on_prepare_shutdown (MetaContext *context, } MetaPluginManager * -meta_plugin_manager_new (MetaCompositor *compositor) +meta_plugin_manager_new (MetaCompositor *compositor, + GVariant *plugin_options) { MetaBackend *backend = meta_compositor_get_backend (compositor); MetaMonitorManager *monitor_manager = @@ -123,10 +124,21 @@ meta_plugin_manager_new (MetaCompositor *compositor) MetaDisplay *display; MetaContext *context; + if (plugin_options) + { + plugin = g_object_new (plugin_type, + "options", plugin_options, + NULL); + } + else + { + plugin = g_object_new (plugin_type, NULL); + } + plugin_mgr = g_new0 (MetaPluginManager, 1); plugin_mgr->state = PLUGIN_MANAGER_STATE_STARTING; plugin_mgr->compositor = compositor; - plugin_mgr->plugin = plugin = g_object_new (plugin_type, NULL); + plugin_mgr->plugin = plugin; _meta_plugin_set_compositor (plugin, compositor); diff --git a/src/compositor/meta-plugin-manager.h b/src/compositor/meta-plugin-manager.h index f011d6276159fdb5b20656248276260344b14a32..8e5251f3f1cc9794d5f9634b3aad6b450b1fe398 100644 --- a/src/compositor/meta-plugin-manager.h +++ b/src/compositor/meta-plugin-manager.h @@ -42,7 +42,8 @@ typedef enum */ typedef struct MetaPluginManager MetaPluginManager; -MetaPluginManager * meta_plugin_manager_new (MetaCompositor *compositor); +MetaPluginManager * meta_plugin_manager_new (MetaCompositor *compositor, + GVariant *plugin_options); void meta_plugin_manager_start (MetaPluginManager *plugin_mgr); diff --git a/src/core/display-private.h b/src/core/display-private.h index b60a9587b2dfcb33d8fee1c044c4ddc57d2b3d6e..15f3e38eebb7ccab27b512f7ab7f67e599b4c104 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -189,6 +189,7 @@ struct _MetaDisplayClass ) MetaDisplay * meta_display_new (MetaContext *context, + GVariant *plugin_options, GError **error); #ifdef HAVE_X11_CLIENT diff --git a/src/core/display.c b/src/core/display.c index 03f3ad0d81f3b0fe9dc98b453f0324c765ecba5a..960d071daee1f14bd80b0dfc21003814f139b6db 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -938,6 +938,7 @@ meta_display_shutdown_x11 (MetaDisplay *display) MetaDisplay * meta_display_new (MetaContext *context, + GVariant *plugin_options, GError **error) { MetaBackend *backend = meta_context_get_backend (context); @@ -1063,7 +1064,7 @@ meta_display_new (MetaContext *context, display->last_focus_time = timestamp; display->last_user_time = timestamp; - if (!meta_compositor_manage (display->compositor, error)) + if (!meta_compositor_manage (display->compositor, plugin_options, error)) { g_object_unref (display); return NULL; diff --git a/src/core/meta-context-private.h b/src/core/meta-context-private.h index 2f46ab9944c82fddf48e4d8be91d524368ce0bff..a53f901fb6dedd967897b5424790e48db1a956a6 100644 --- a/src/core/meta-context-private.h +++ b/src/core/meta-context-private.h @@ -91,3 +91,7 @@ void meta_context_set_trace_file (MetaContext *context, #endif MetaSessionManager * meta_context_get_session_manager (MetaContext *context); + +META_EXPORT_TEST +void meta_context_set_plugin_options (MetaContext *context, + GVariant *plugin_options); diff --git a/src/core/meta-context.c b/src/core/meta-context.c index dd9bd4974cb015088cc9247fcb878809c98aa09a..996dcdd078c99883ee6128f359696becb43df4d3 100644 --- a/src/core/meta-context.c +++ b/src/core/meta-context.c @@ -78,6 +78,7 @@ typedef struct _MetaContextPrivate char *nick; char *plugin_name; GType plugin_gtype; + GVariant *plugin_options; char *gnome_wm_keybindings; gboolean unsafe_mode; @@ -185,6 +186,15 @@ meta_context_set_plugin_name (MetaContext *context, priv->plugin_name = g_strdup (plugin_name); } +void +meta_context_set_plugin_options (MetaContext *context, + GVariant *plugin_options) +{ + MetaContextPrivate *priv = meta_context_get_instance_private (context); + + priv->plugin_options = g_variant_ref (plugin_options); +} + void meta_context_set_gnome_wm_keybindings (MetaContext *context, const char *wm_keybindings) @@ -510,6 +520,7 @@ meta_context_start (MetaContext *context, GError **error) { MetaContextPrivate *priv = meta_context_get_instance_private (context); + g_autoptr (GVariant) plugin_options = NULL; g_return_val_if_fail (META_IS_CONTEXT (context), FALSE); @@ -523,7 +534,8 @@ meta_context_start (MetaContext *context, priv->wayland_compositor = meta_wayland_compositor_new (context); #endif - priv->display = meta_display_new (context, error); + plugin_options = g_steal_pointer (&priv->plugin_options), + priv->display = meta_display_new (context, plugin_options, error); if (!priv->display) { priv->state = META_CONTEXT_STATE_TERMINATED; @@ -882,6 +894,7 @@ meta_context_finalize (GObject *object) g_clear_pointer (&priv->trace_file, g_free); #endif + g_clear_pointer (&priv->plugin_options, g_variant_unref); g_clear_pointer (&priv->gnome_wm_keybindings, g_free); g_clear_pointer (&priv->plugin_name, g_free); g_clear_pointer (&priv->name, g_free); diff --git a/src/tests/meson.build b/src/tests/meson.build index 935f990a331094d8cceb84ac51d9d4f5019b0968..ab359b36437f9945ceff5fd64dfc0a29ffb9dae5 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -313,6 +313,11 @@ test_cases += [ '--compile-schemas', ], }, + { + 'name': 'stage', + 'suite': 'backend', + 'sources': [ 'stage-tests.c', ], + }, ] screen_cast_client = executable('mutter-screen-cast-client', diff --git a/src/tests/meta-test-shell.c b/src/tests/meta-test-shell.c index 13676920bd32148d7a18608a44fcfd926560b611..e4037eb834d854febf196a3a94402162e136f7c3 100644 --- a/src/tests/meta-test-shell.c +++ b/src/tests/meta-test-shell.c @@ -33,6 +33,17 @@ #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, @@ -66,6 +77,8 @@ struct _MetaTestShell ClutterGrab *grab; ClutterActor *prev_focus; } overview; + + gboolean show_stage; }; typedef struct _ActorPrivate @@ -348,7 +361,8 @@ meta_test_shell_start (MetaPlugin *plugin) G_CALLBACK (prepare_shutdown), test_shell); - clutter_actor_show (meta_get_stage_for_display (display)); + if (test_shell->show_stage) + clutter_actor_show (meta_get_stage_for_display (display)); } static void @@ -779,11 +793,46 @@ meta_test_shell_kill_window_effects (MetaPlugin *plugin, 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_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; + plugin_class->start = meta_test_shell_start; plugin_class->map = meta_test_shell_map; plugin_class->minimize = meta_test_shell_minimize; @@ -793,9 +842,19 @@ meta_test_shell_class_init (MetaTestShellClass *klass) 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; } diff --git a/src/tests/stage-tests.c b/src/tests/stage-tests.c new file mode 100644 index 0000000000000000000000000000000000000000..e5b96f3313c29a03a59157874a28f5db21d1c0e9 --- /dev/null +++ b/src/tests/stage-tests.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2024 Red Hat Inc. + * + * 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 . + * + */ + +#include "config.h" + +#include "backends/meta-backend-private.h" +#include "core/meta-context-private.h" +#include "tests/meta-test-utils.h" +#include "tests/meta-test/meta-context-test.h" + +static MetaContext *test_context; + +static gboolean +event_filter_cb (const ClutterEvent *event, + ClutterActor *event_actor, + gpointer user_data) +{ + gboolean *saw_event = user_data; + + if (clutter_event_type (event) == CLUTTER_DEVICE_ADDED) + *saw_event = TRUE; + + return CLUTTER_EVENT_PROPAGATE; +} + +static void +meta_test_stage_scheduling_delayed_show (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterActor *stage = meta_backend_get_stage (backend); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (MetaVirtualMonitor) virtual_monitor = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + guint filter_id; + gboolean saw_event = FALSE; + + virtual_monitor = meta_create_test_monitor (test_context, 800, 600, 60.0f); + g_debug ("Wait for initial dummy dispatch"); + while (TRUE) + { + if (!g_main_context_iteration (NULL, FALSE)) + break; + } + + filter_id = clutter_event_add_filter (NULL, event_filter_cb, NULL, &saw_event); + g_debug ("Creating virtual pointer"); + virtual_pointer = + clutter_seat_create_virtual_device (seat, CLUTTER_KEYBOARD_DEVICE); + while (!saw_event) + g_main_context_iteration (NULL, TRUE); + g_debug ("Scheduling update with DEVICE_ADDED in stage queue"); + clutter_stage_schedule_update (CLUTTER_STAGE (stage)); + g_debug ("Showing stage"); + clutter_actor_show (stage); + g_debug ("Waiting for paint"); + clutter_actor_queue_redraw (stage); + meta_wait_for_paint (test_context); + clutter_event_remove_filter (filter_id); +} + +int +main (int argc, + char **argv) +{ + g_autoptr (MetaContext) context = NULL; + g_auto (GVariantBuilder) plugin_options_builder = + G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT); + g_autoptr (GVariant) plugin_options = NULL; + + context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS, + META_CONTEXT_TEST_FLAG_NO_X11); + g_assert (meta_context_configure (context, &argc, &argv, NULL)); + + g_variant_builder_add (&plugin_options_builder, "{sv}", + "show-stage", g_variant_new_boolean (FALSE)); + plugin_options = + g_variant_ref_sink (g_variant_builder_end (&plugin_options_builder)); + meta_context_set_plugin_options (context, plugin_options); + + test_context = context; + + g_test_add_func ("/stage/scheduling/delayed-show", + meta_test_stage_scheduling_delayed_show); + + return meta_context_test_run_tests (META_CONTEXT_TEST (context), + META_TEST_RUN_FLAG_NONE); +}