From faff0738eb04923aa70019cf748c788959073a54 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Mon, 21 Nov 2011 11:56:24 -0500 Subject: [PATCH] Do not use the default stage https://bugzilla.gnome.org/show_bug.cgi?id=664052 --- js/ui/magnifier.js | 2 +- src/gnome-shell-plugin.c | 2 +- src/main.c | 249 -------------------------------------- src/shell-global.c | 248 +++++++++++++++++++++++++++++++++++++ src/shell-xfixes-cursor.c | 24 ++-- src/shell-xfixes-cursor.h | 2 +- src/st/test-theme.c | 2 +- src/test-recorder.c | 3 +- 8 files changed, 271 insertions(+), 261 deletions(-) diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js index daa02a70f..13ac64e34 100644 --- a/js/ui/magnifier.js +++ b/js/ui/magnifier.js @@ -44,7 +44,7 @@ const Magnifier = new Lang.Class({ this._zoomRegions = []; // Create small clutter tree for the magnified mouse. - let xfixesCursor = Shell.XFixesCursor.get_default(); + let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage); this._mouseSprite = new Clutter.Texture(); xfixesCursor.update_texture_image(this._mouseSprite); this._cursorRoot = new Clutter.Group(); diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c index 4708e0abe..5f3fab4d4 100644 --- a/src/gnome-shell-plugin.c +++ b/src/gnome-shell-plugin.c @@ -323,7 +323,7 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin, #endif if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify) - && xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (clutter_stage_get_default ()))) + && xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (meta_plugin_get_stage (plugin)))) { /* If the pointer enters a child of the stage window (eg, a * trayicon), we want to consider it to still be in the stage, diff --git a/src/main.c b/src/main.c index 42d144679..e1e8722dc 100644 --- a/src/main.c +++ b/src/main.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -159,157 +158,6 @@ shell_dbus_init (gboolean replace) g_object_unref (session); } -static void -constrain_tooltip (StTooltip *tooltip, - const ClutterGeometry *geometry, - ClutterGeometry *adjusted_geometry, - gpointer data) -{ - const ClutterGeometry *tip_area = st_tooltip_get_tip_area (tooltip); - ShellGlobal *global = shell_global_get (); - MetaScreen *screen = shell_global_get_screen (global); - int n_monitors = meta_screen_get_n_monitors (screen); - int i; - - *adjusted_geometry = *geometry; - - /* A point that determines what screen we'll constrain to */ - int x = tip_area->x + tip_area->width / 2; - int y = tip_area->y + tip_area->height / 2; - - for (i = 0; i < n_monitors; i++) - { - MetaRectangle rect; - meta_screen_get_monitor_geometry (screen, i, &rect); - if (x >= rect.x && x < rect.x + rect.width && - y >= rect.y && y < rect.y + rect.height) - { - if (adjusted_geometry->x + adjusted_geometry->width > rect.x + rect.width) - adjusted_geometry->x = rect.x + rect.width - adjusted_geometry->width; - if (adjusted_geometry->x < rect.x) - adjusted_geometry->x = rect.x; - - if (adjusted_geometry->y + adjusted_geometry->height > rect.y + rect.height) - adjusted_geometry->y = rect.y + rect.height - adjusted_geometry->height; - if (adjusted_geometry->y < rect.y) - adjusted_geometry->y = rect.y; - - return; - } - } -} - -static void -update_font_options (GtkSettings *settings) -{ - StThemeContext *context; - ClutterStage *stage; - ClutterBackend *backend; - gint dpi; - gint hinting; - gchar *hint_style_str; - cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_NONE; - gint antialias; - cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_NONE; - cairo_font_options_t *options; - - g_object_get (settings, - "gtk-xft-dpi", &dpi, - "gtk-xft-antialias", &antialias, - "gtk-xft-hinting", &hinting, - "gtk-xft-hintstyle", &hint_style_str, - NULL); - - stage = CLUTTER_STAGE (clutter_stage_get_default ()); - context = st_theme_context_get_for_stage (stage); - - if (dpi != -1) - /* GTK stores resolution as 1024 * dots/inch */ - st_theme_context_set_resolution (context, dpi / 1024); - else - st_theme_context_set_default_resolution (context); - - st_tooltip_set_constrain_func (stage, constrain_tooltip, NULL, NULL); - - /* Clutter (as of 0.9) passes comprehensively wrong font options - * override whatever set_font_flags() did above. - * - * http://bugzilla.openedhand.com/show_bug.cgi?id=1456 - */ - backend = clutter_get_default_backend (); - options = cairo_font_options_create (); - - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); - - if (hinting >= 0 && !hinting) - { - hint_style = CAIRO_HINT_STYLE_NONE; - } - else if (hint_style_str) - { - if (strcmp (hint_style_str, "hintnone") == 0) - hint_style = CAIRO_HINT_STYLE_NONE; - else if (strcmp (hint_style_str, "hintslight") == 0) - hint_style = CAIRO_HINT_STYLE_SLIGHT; - else if (strcmp (hint_style_str, "hintmedium") == 0) - hint_style = CAIRO_HINT_STYLE_MEDIUM; - else if (strcmp (hint_style_str, "hintfull") == 0) - hint_style = CAIRO_HINT_STYLE_FULL; - } - - g_free (hint_style_str); - - cairo_font_options_set_hint_style (options, hint_style); - - /* We don't want to turn on subpixel anti-aliasing; since Clutter - * doesn't currently have the code to support ARGB masks, - * generating them then squashing them back to A8 is pointless. - */ - antialias_mode = (antialias < 0 || antialias) ? CAIRO_ANTIALIAS_GRAY - : CAIRO_ANTIALIAS_NONE; - - cairo_font_options_set_antialias (options, antialias_mode); - - clutter_backend_set_font_options (backend, options); - cairo_font_options_destroy (options); -} - -static void -settings_notify_cb (GtkSettings *settings, - GParamSpec *pspec, - gpointer data) -{ - update_font_options (settings); -} - -static void -shell_fonts_init (void) -{ - GtkSettings *settings; - CoglPangoFontMap *fontmap; - - /* Disable text mipmapping; it causes problems on pre-GEM Intel - * drivers and we should just be rendering text at the right - * size rather than scaling it. If we do effects where we dynamically - * zoom labels, then we might want to reconsider. - */ - fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ()); - cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE); - - settings = gtk_settings_get_default (); - g_object_connect (settings, - "signal::notify::gtk-xft-dpi", - G_CALLBACK (settings_notify_cb), NULL, - "signal::notify::gtk-xft-antialias", - G_CALLBACK (settings_notify_cb), NULL, - "signal::notify::gtk-xft-hinting", - G_CALLBACK (settings_notify_cb), NULL, - "signal::notify::gtk-xft-hintstyle", - G_CALLBACK (settings_notify_cb), NULL, - NULL); - update_font_options (settings); -} - static void shell_prefs_init (void) { @@ -323,100 +171,6 @@ shell_prefs_init (void) OVERRIDES_SCHEMA); } -/* This is an IBus workaround. The flow of events with IBus is that every time - * it gets gets a key event, it: - * - * Sends it to the daemon via D-Bus asynchronously - * When it gets an reply, synthesizes a new GdkEvent and puts it into the - * GDK event queue with gdk_event_put(), including - * IBUS_FORWARD_MASK = 1 << 25 in the state to prevent a loop. - * - * (Normally, IBus uses the GTK+ key snooper mechanism to get the key - * events early, but since our key events aren't visible to GTK+ key snoopers, - * IBus will instead get the events via the standard - * GtkIMContext.filter_keypress() mechanism.) - * - * There are a number of potential problems here; probably the worst - * problem is that IBus doesn't forward the timestamp with the event - * so that every key event that gets delivered ends up with - * GDK_CURRENT_TIME. This creates some very subtle bugs; for example - * if you have IBus running and a keystroke is used to trigger - * launching an application, focus stealing prevention won't work - * right. http://code.google.com/p/ibus/issues/detail?id=1184 - * - * In any case, our normal flow of key events is: - * - * GDK filter function => clutter_x11_handle_event => clutter actor - * - * So, if we see a key event that gets delivered via the GDK event handler - * function - then we know it must be one of these synthesized events, and - * we should push it back to clutter. - * - * To summarize, the full key event flow with IBus is: - * - * GDK filter function - * => Mutter - * => gnome_shell_plugin_xevent_filter() - * => clutter_x11_handle_event() - * => clutter event delivery to actor - * => gtk_im_context_filter_event() - * => sent to IBus daemon - * => response received from IBus daemon - * => gdk_event_put() - * => GDK event handler - * => - * => clutter_event_put() - * => clutter event delivery to actor - * - * Anything else we see here we just pass on to the normal GDK event handler - * gtk_main_do_event(). - */ -static void -gnome_shell_gdk_event_handler (GdkEvent *event_gdk, - gpointer data) -{ - if (event_gdk->type == GDK_KEY_PRESS || event_gdk->type == GDK_KEY_RELEASE) - { - ClutterActor *stage; - Window stage_xwindow; - - stage = clutter_stage_get_default (); - stage_xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); - - if (GDK_WINDOW_XID (event_gdk->key.window) == stage_xwindow) - { - ClutterDeviceManager *device_manager = clutter_device_manager_get_default (); - ClutterInputDevice *keyboard = clutter_device_manager_get_core_device (device_manager, - CLUTTER_KEYBOARD_DEVICE); - - ClutterEvent *event_clutter = clutter_event_new ((event_gdk->type == GDK_KEY_PRESS) ? - CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE); - event_clutter->key.time = event_gdk->key.time; - event_clutter->key.flags = CLUTTER_EVENT_NONE; - event_clutter->key.stage = CLUTTER_STAGE (stage); - event_clutter->key.source = NULL; - - /* This depends on ClutterModifierType and GdkModifierType being - * identical, which they are currently. (They both match the X - * modifier state in the low 16-bits and have the same extensions.) */ - event_clutter->key.modifier_state = event_gdk->key.state; - - event_clutter->key.keyval = event_gdk->key.keyval; - event_clutter->key.hardware_keycode = event_gdk->key.hardware_keycode; - event_clutter->key.unicode_value = gdk_keyval_to_unicode (event_clutter->key.keyval); - event_clutter->key.device = keyboard; - - clutter_event_put (event_clutter); - clutter_event_free (event_clutter); - - return; - } - } - - gtk_main_do_event (event_gdk); -} - - static void malloc_statistics_callback (ShellPerfLog *perf_log, gpointer data) @@ -559,12 +313,9 @@ main (int argc, char **argv) shell_dbus_init (meta_get_replace_current_wm ()); shell_a11y_init (); - shell_fonts_init (); shell_perf_log_init (); shell_prefs_init (); - gdk_event_handler_set (gnome_shell_gdk_event_handler, NULL, NULL); - g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR); #if HAVE_BLUETOOTH g_irepository_prepend_search_path (BLUETOOTH_DIR); diff --git a/src/shell-global.c b/src/shell-global.c index b5f2eddec..bc0faf104 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -15,6 +15,7 @@ #endif #include +#include #include #include #include @@ -793,6 +794,249 @@ global_stage_after_paint (ClutterStage *stage, "clutter.stagePaintDone"); } +static void +constrain_tooltip (StTooltip *tooltip, + const ClutterGeometry *geometry, + ClutterGeometry *adjusted_geometry, + gpointer data) +{ + const ClutterGeometry *tip_area = st_tooltip_get_tip_area (tooltip); + ShellGlobal *global = shell_global_get (); + MetaScreen *screen = shell_global_get_screen (global); + int n_monitors = meta_screen_get_n_monitors (screen); + int i; + + *adjusted_geometry = *geometry; + + /* A point that determines what screen we'll constrain to */ + int x = tip_area->x + tip_area->width / 2; + int y = tip_area->y + tip_area->height / 2; + + for (i = 0; i < n_monitors; i++) + { + MetaRectangle rect; + meta_screen_get_monitor_geometry (screen, i, &rect); + if (x >= rect.x && x < rect.x + rect.width && + y >= rect.y && y < rect.y + rect.height) + { + if (adjusted_geometry->x + adjusted_geometry->width > rect.x + rect.width) + adjusted_geometry->x = rect.x + rect.width - adjusted_geometry->width; + if (adjusted_geometry->x < rect.x) + adjusted_geometry->x = rect.x; + + if (adjusted_geometry->y + adjusted_geometry->height > rect.y + rect.height) + adjusted_geometry->y = rect.y + rect.height - adjusted_geometry->height; + if (adjusted_geometry->y < rect.y) + adjusted_geometry->y = rect.y; + + return; + } + } +} + +static void +update_font_options (GtkSettings *settings, + ClutterStage *stage) +{ + StThemeContext *context; + ClutterBackend *backend; + gint dpi; + gint hinting; + gchar *hint_style_str; + cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_NONE; + gint antialias; + cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_NONE; + cairo_font_options_t *options; + + g_object_get (settings, + "gtk-xft-dpi", &dpi, + "gtk-xft-antialias", &antialias, + "gtk-xft-hinting", &hinting, + "gtk-xft-hintstyle", &hint_style_str, + NULL); + + context = st_theme_context_get_for_stage (stage); + + if (dpi != -1) + /* GTK stores resolution as 1024 * dots/inch */ + st_theme_context_set_resolution (context, dpi / 1024); + else + st_theme_context_set_default_resolution (context); + + st_tooltip_set_constrain_func (stage, constrain_tooltip, NULL, NULL); + + /* Clutter (as of 0.9) passes comprehensively wrong font options + * override whatever set_font_flags() did above. + * + * http://bugzilla.openedhand.com/show_bug.cgi?id=1456 + */ + backend = clutter_get_default_backend (); + options = cairo_font_options_create (); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); + + if (hinting >= 0 && !hinting) + { + hint_style = CAIRO_HINT_STYLE_NONE; + } + else if (hint_style_str) + { + if (strcmp (hint_style_str, "hintnone") == 0) + hint_style = CAIRO_HINT_STYLE_NONE; + else if (strcmp (hint_style_str, "hintslight") == 0) + hint_style = CAIRO_HINT_STYLE_SLIGHT; + else if (strcmp (hint_style_str, "hintmedium") == 0) + hint_style = CAIRO_HINT_STYLE_MEDIUM; + else if (strcmp (hint_style_str, "hintfull") == 0) + hint_style = CAIRO_HINT_STYLE_FULL; + } + + g_free (hint_style_str); + + cairo_font_options_set_hint_style (options, hint_style); + + /* We don't want to turn on subpixel anti-aliasing; since Clutter + * doesn't currently have the code to support ARGB masks, + * generating them then squashing them back to A8 is pointless. + */ + antialias_mode = (antialias < 0 || antialias) ? CAIRO_ANTIALIAS_GRAY + : CAIRO_ANTIALIAS_NONE; + + cairo_font_options_set_antialias (options, antialias_mode); + + clutter_backend_set_font_options (backend, options); + cairo_font_options_destroy (options); +} + +static void +settings_notify_cb (GtkSettings *settings, + GParamSpec *pspec, + gpointer data) +{ + update_font_options (settings, CLUTTER_STAGE (data)); +} + +static void +shell_fonts_init (ClutterStage *stage) +{ + GtkSettings *settings; + CoglPangoFontMap *fontmap; + + /* Disable text mipmapping; it causes problems on pre-GEM Intel + * drivers and we should just be rendering text at the right + * size rather than scaling it. If we do effects where we dynamically + * zoom labels, then we might want to reconsider. + */ + fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ()); + cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE); + + settings = gtk_settings_get_default (); + g_object_connect (settings, + "signal::notify::gtk-xft-dpi", + G_CALLBACK (settings_notify_cb), stage, + "signal::notify::gtk-xft-antialias", + G_CALLBACK (settings_notify_cb), stage, + "signal::notify::gtk-xft-hinting", + G_CALLBACK (settings_notify_cb), stage, + "signal::notify::gtk-xft-hintstyle", + G_CALLBACK (settings_notify_cb), stage, + NULL); + update_font_options (settings, stage); +} + +/* This is an IBus workaround. The flow of events with IBus is that every time + * it gets gets a key event, it: + * + * Sends it to the daemon via D-Bus asynchronously + * When it gets an reply, synthesizes a new GdkEvent and puts it into the + * GDK event queue with gdk_event_put(), including + * IBUS_FORWARD_MASK = 1 << 25 in the state to prevent a loop. + * + * (Normally, IBus uses the GTK+ key snooper mechanism to get the key + * events early, but since our key events aren't visible to GTK+ key snoopers, + * IBus will instead get the events via the standard + * GtkIMContext.filter_keypress() mechanism.) + * + * There are a number of potential problems here; probably the worst + * problem is that IBus doesn't forward the timestamp with the event + * so that every key event that gets delivered ends up with + * GDK_CURRENT_TIME. This creates some very subtle bugs; for example + * if you have IBus running and a keystroke is used to trigger + * launching an application, focus stealing prevention won't work + * right. http://code.google.com/p/ibus/issues/detail?id=1184 + * + * In any case, our normal flow of key events is: + * + * GDK filter function => clutter_x11_handle_event => clutter actor + * + * So, if we see a key event that gets delivered via the GDK event handler + * function - then we know it must be one of these synthesized events, and + * we should push it back to clutter. + * + * To summarize, the full key event flow with IBus is: + * + * GDK filter function + * => Mutter + * => gnome_shell_plugin_xevent_filter() + * => clutter_x11_handle_event() + * => clutter event delivery to actor + * => gtk_im_context_filter_event() + * => sent to IBus daemon + * => response received from IBus daemon + * => gdk_event_put() + * => GDK event handler + * => + * => clutter_event_put() + * => clutter event delivery to actor + * + * Anything else we see here we just pass on to the normal GDK event handler + * gtk_main_do_event(). + */ +static void +gnome_shell_gdk_event_handler (GdkEvent *event_gdk, + gpointer data) +{ + if (event_gdk->type == GDK_KEY_PRESS || event_gdk->type == GDK_KEY_RELEASE) + { + ClutterActor *stage; + Window stage_xwindow; + + stage = CLUTTER_ACTOR (data); + stage_xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + + if (GDK_WINDOW_XID (event_gdk->key.window) == stage_xwindow) + { + ClutterDeviceManager *device_manager = clutter_device_manager_get_default (); + ClutterInputDevice *keyboard = clutter_device_manager_get_core_device (device_manager, + CLUTTER_KEYBOARD_DEVICE); + + ClutterEvent *event_clutter = clutter_event_new ((event_gdk->type == GDK_KEY_PRESS) ? + CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE); + event_clutter->key.time = event_gdk->key.time; + event_clutter->key.flags = CLUTTER_EVENT_NONE; + event_clutter->key.stage = CLUTTER_STAGE (stage); + event_clutter->key.source = NULL; + + /* This depends on ClutterModifierType and GdkModifierType being + * identical, which they are currently. (They both match the X + * modifier state in the low 16-bits and have the same extensions.) */ + event_clutter->key.modifier_state = event_gdk->key.state; + + event_clutter->key.keyval = event_gdk->key.keyval; + event_clutter->key.hardware_keycode = event_gdk->key.hardware_keycode; + event_clutter->key.unicode_value = gdk_keyval_to_unicode (event_clutter->key.keyval); + event_clutter->key.device = keyboard; + + clutter_event_put (event_clutter); + clutter_event_free (event_clutter); + + return; + } + } + + gtk_main_do_event (event_gdk); +} + void _shell_global_set_plugin (ShellGlobal *global, MetaPlugin *plugin) @@ -838,6 +1082,10 @@ _shell_global_set_plugin (ShellGlobal *global, g_signal_connect (global->meta_display, "notify::focus-window", G_CALLBACK (focus_window_changed), global); + shell_fonts_init (global->stage); + + gdk_event_handler_set (gnome_shell_gdk_event_handler, global->stage, NULL); + global->focus_manager = st_focus_manager_get_for_stage (global->stage); } diff --git a/src/shell-xfixes-cursor.c b/src/shell-xfixes-cursor.c index fd8f2e156..4f1a36386 100644 --- a/src/shell-xfixes-cursor.c +++ b/src/shell-xfixes-cursor.c @@ -321,19 +321,29 @@ shell_xfixes_cursor_class_init (ShellXFixesCursorClass *klass) } /** - * shell_xfixes_cursor_get_default: + * shell_xfixes_cursor_get_for_stage: + * @stage: (transfer none): The #ClutterStage to get the cursor for * - * Return value: (transfer none): The global #ShellXFixesCursor singleton + * Return value: (transfer none): A #ShellXFixesCursor instance */ ShellXFixesCursor * -shell_xfixes_cursor_get_default () +shell_xfixes_cursor_get_for_stage (ClutterStage *stage) { - static ShellXFixesCursor *instance = NULL; + ShellXFixesCursor *instance; + static GQuark xfixes_cursor_quark; + + if (G_UNLIKELY (xfixes_cursor_quark == 0)) + xfixes_cursor_quark = g_quark_from_static_string ("gnome-shell-xfixes-cursor"); + + instance = g_object_get_qdata (G_OBJECT (stage), xfixes_cursor_quark); if (instance == NULL) - instance = g_object_new (SHELL_TYPE_XFIXES_CURSOR, - "stage", clutter_stage_get_default (), - NULL); + { + instance = g_object_new (SHELL_TYPE_XFIXES_CURSOR, + "stage", stage, + NULL); + g_object_set_qdata (G_OBJECT (stage), xfixes_cursor_quark, instance); + } return instance; } diff --git a/src/shell-xfixes-cursor.h b/src/shell-xfixes-cursor.h index 28af3d1d4..d84d5aa4b 100644 --- a/src/shell-xfixes-cursor.h +++ b/src/shell-xfixes-cursor.h @@ -19,7 +19,7 @@ typedef struct _ShellXFixesCursorClass ShellXFixesCursorClass; GType shell_xfixes_cursor_get_type (void) G_GNUC_CONST; -ShellXFixesCursor *shell_xfixes_cursor_get_default (void); +ShellXFixesCursor *shell_xfixes_cursor_get_for_stage (ClutterStage *stage); void shell_xfixes_cursor_show (ShellXFixesCursor *xfixes_cursor); void shell_xfixes_cursor_hide (ShellXFixesCursor *xfixes_cursor); diff --git a/src/st/test-theme.c b/src/st/test-theme.c index db7f747ce..73dbc93a4 100644 --- a/src/st/test-theme.c +++ b/src/st/test-theme.c @@ -432,7 +432,7 @@ main (int argc, char **argv) theme = st_theme_new ("st/test-theme.css", NULL, NULL); - stage = clutter_stage_get_default (); + stage = clutter_stage_new (); context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage)); st_theme_context_set_theme (context, theme); st_theme_context_set_resolution (context, 96.); diff --git a/src/test-recorder.c b/src/test-recorder.c index e443322a5..ba5aa749b 100644 --- a/src/test-recorder.c +++ b/src/test-recorder.c @@ -36,7 +36,8 @@ int main (int argc, char **argv) clutter_color_from_string (&red, "red"); clutter_color_from_string (&green, "green"); clutter_color_from_string (&blue, "blue"); - stage = clutter_stage_get_default (); + stage = clutter_stage_new (); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); text = g_object_new (CLUTTER_TYPE_TEXT, "text", "Red",