Beispiel #1
0
ClutterActor *
_clutter_backend_create_stage (ClutterBackend  *backend,
                               ClutterStage    *wrapper,
                               GError         **error)
{
  ClutterBackendClass *klass;
  ClutterStageManager *stage_manager;
  ClutterActor        *stage = NULL;

  g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), FALSE);
  g_return_val_if_fail (CLUTTER_IS_STAGE (wrapper), FALSE);

  stage_manager = clutter_stage_manager_get_default ();

  klass = CLUTTER_BACKEND_GET_CLASS (backend);
  if (klass->create_stage)
    stage = klass->create_stage (backend, wrapper, error);

  if (!stage)
    return NULL;

  g_assert (CLUTTER_IS_STAGE_WINDOW (stage));
  _clutter_stage_set_window (wrapper, CLUTTER_STAGE_WINDOW (stage));
  _clutter_stage_manager_add_stage (stage_manager, wrapper);

  return stage;
}
/**
 * clutter_test_check_color_at_point:
 * @stage: a #ClutterStage
 * @point: coordinates to check
 * @color: expected color
 * @result: (out caller-allocates): color at the given coordinates
 *
 * Checks the color at the given coordinates on @stage, and matches
 * it with the red, green, and blue channels of @color. The alpha
 * component of @color and @result is ignored.
 *
 * Returns: %TRUE if the colors match
 *
 * Since: 1.18
 */
gboolean
clutter_test_check_color_at_point (ClutterActor       *stage,
                                   const ClutterPoint *point,
                                   const ClutterColor *color,
                                   ClutterColor       *result)
{
  ValidateData *data;
  gboolean retval;
  guint8 *buffer;
  guint press_id = 0;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
  g_return_val_if_fail (point != NULL, FALSE);
  g_return_val_if_fail (color != NULL, FALSE);
  g_return_val_if_fail (result != NULL, FALSE);

  data = g_new0 (ValidateData, 1);
  data->stage = stage;
  data->point = *point;
  data->check_color = TRUE;

  if (g_test_verbose ())
    {
      g_printerr ("Press ESC to close the stage and resume the test\n");
      press_id = g_signal_connect (stage, "key-press-event",
                                   G_CALLBACK (on_key_press_event),
                                   data);
    }

  clutter_actor_show (stage);

  clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
                                         validate_stage,
                                         data,
                                         NULL);

  while (!data->was_painted)
    g_main_context_iteration (NULL, TRUE);

  if (press_id != 0)
    g_signal_handler_disconnect (stage, press_id);

  buffer = data->result;

  clutter_color_init (result, buffer[0], buffer[1], buffer[2], 255);

  /* we only check the color channels, so we can't use clutter_color_equal() */
  retval = buffer[0] == color->red &&
           buffer[1] == color->green &&
           buffer[2] == color->blue;

  g_free (data->result);
  g_free (data);

  return retval;
}
Beispiel #3
0
void
_clutter_backend_ensure_context (ClutterBackend *backend,
                                 ClutterStage   *stage)
{
  static ClutterStage *current_context_stage = NULL;
  ClutterBackendClass *klass;

  g_return_if_fail (CLUTTER_IS_BACKEND (backend));
  g_return_if_fail (CLUTTER_IS_STAGE (stage));

  if (current_context_stage != stage || !CLUTTER_ACTOR_IS_REALIZED (stage))
    {
      ClutterStage *new_stage = NULL;

      if (!CLUTTER_ACTOR_IS_REALIZED (stage))
        {
          new_stage = NULL;

          CLUTTER_NOTE (MULTISTAGE,
                        "Stage [%p] is not realized, unsetting the stage",
                        stage);
        }
      else
        {
          new_stage = stage;

          CLUTTER_NOTE (MULTISTAGE,
                        "Setting the new stage [%p]",
                        new_stage);
        }

      klass = CLUTTER_BACKEND_GET_CLASS (backend);
      if (G_LIKELY (klass->ensure_context))
        klass->ensure_context (backend, new_stage);
      
      /* FIXME: With a NULL stage and thus no active context it may make more
       * sense to clean the context but then re call with the default stage 
       * so at least there is some kind of context in place (as to avoid
       * potential issue of GL calls with no context)
       */
      current_context_stage = new_stage;

      /* if the new stage has a different size than the previous one
       * we need to update the viewport; we do it by simply setting the
       * SYNC_MATRICES flag and letting the next redraw cycle take care
       * of calling glViewport()
       */
      if (current_context_stage)
        {
          CLUTTER_SET_PRIVATE_FLAGS (current_context_stage,
                                     CLUTTER_ACTOR_SYNC_MATRICES);
        }
    }
  else
    CLUTTER_NOTE (MULTISTAGE, "Stage is the same");
}
/**
 * clutter_win32_set_stage_foreign:
 * @stage: a #ClutterStage
 * @hwnd: an existing window handle
 *
 * Target the #ClutterStage to use an existing external window handle.
 *
 * Return value: %TRUE if foreign window is valid
 *
 * Since: 0.8
 */
gboolean
clutter_win32_set_stage_foreign (ClutterStage *stage,
                                 HWND          hwnd)
{
    ClutterStageWin32 *stage_win32;
    ClutterStageWindow *impl;
    ClutterActor *actor;
    RECT client_rect;
    ForeignWindowData fwd;

    g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
    g_return_val_if_fail (hwnd != NULL, FALSE);

    actor = CLUTTER_ACTOR (stage);

    impl = _clutter_stage_get_window (stage);
    stage_win32 = CLUTTER_STAGE_WIN32 (impl);

    if (!GetClientRect (hwnd, &client_rect))
    {
        g_warning ("Unable to retrieve the new window geometry");
        return FALSE;
    }

    fwd.stage_win32 = stage_win32;
    fwd.hwnd = hwnd;

    /* destroy the old HWND, if we have one and it's ours */
    if (stage_win32->hwnd != NULL && !stage_win32->is_foreign_win)
        fwd.destroy_old_hwnd = TRUE;
    else
        fwd.destroy_old_hwnd = FALSE;

    fwd.geom.x = 0;
    fwd.geom.y = 0;
    fwd.geom.width = client_rect.right - client_rect.left;
    fwd.geom.height = client_rect.bottom - client_rect.top;

    _clutter_actor_rerealize (actor,
                              set_foreign_window_callback,
                              &fwd);

    /* Queue a relayout - so the stage will be allocated the new
     * window size.
     *
     * Note also that when the stage gets allocated the new
     * window size that will result in the stage's
     * priv->viewport being changed, which will in turn result
     * in the Cogl viewport changing when _clutter_do_redraw
     * calls _clutter_stage_maybe_setup_viewport().
     */
    clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));

    return TRUE;
}
/**
 * clutter_event_set_stage:
 * @event: a #ClutterEvent
 * @stage: (allow-none): a #ClutterStage, or %NULL
 *
 * Sets the source #ClutterStage of the event.
 *
 * Since: 1.8
 */
void
clutter_event_set_stage (ClutterEvent *event,
                         ClutterStage *stage)
{
  g_return_if_fail (event != NULL);
  g_return_if_fail (stage == NULL || CLUTTER_IS_STAGE (stage));

  if (event->any.stage == stage)
    return;

  event->any.stage = stage;
}
Beispiel #6
0
/**
 * clutter_x11_get_stage_window: (skip)
 * @stage: a #ClutterStage
 *
 * Gets the stages X Window.
 *
 * Return value: An XID for the stage window.
 *
 *
 */
Window
clutter_x11_get_stage_window (ClutterStage *stage)
{
  ClutterStageWindow *impl;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);

  impl = _clutter_stage_get_window (stage);
  g_assert (CLUTTER_IS_STAGE_X11 (impl));

  return CLUTTER_STAGE_X11 (impl)->xwin;
}
/**
 * clutter_win32_get_stage_window:
 * @stage: a #ClutterStage
 *
 * Gets the stage's window handle
 *
 * Return value: An HWND for the stage window.
 *
 * Since: 0.8
 */
HWND
clutter_win32_get_stage_window (ClutterStage *stage)
{
  ClutterStageWindow *impl;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);

  impl = _clutter_stage_get_window (stage);

  g_return_val_if_fail (CLUTTER_IS_STAGE_WIN32 (impl), NULL);

  return CLUTTER_STAGE_WIN32 (impl)->hwnd;
}
/**
 * clutter_win32_set_stage_foreign:
 * @stage: a #ClutterStage
 * @hwnd: an existing window handle
 *
 * Target the #ClutterStage to use an existing external window handle.
 *
 * Return value: %TRUE if foreign window is valid
 *
 * Since: 0.8
 */
gboolean
clutter_win32_set_stage_foreign (ClutterStage *stage,
				 HWND          hwnd)
{
  ClutterStageWin32 *stage_win32;
  ClutterStageWindow *impl;
  ClutterActor *actor;
  RECT client_rect;
  POINT window_pos;
  ClutterGeometry geom;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
  g_return_val_if_fail (hwnd != NULL, FALSE);

  actor = CLUTTER_ACTOR (stage);

  impl = _clutter_stage_get_window (stage);
  stage_win32 = CLUTTER_STAGE_WIN32 (impl);

  /* FIXME this needs updating to use _clutter_actor_rerealize(),
   * see the analogous code in x11 backend. Probably best if
   * win32 maintainer does it so they can be sure it compiles
   * and works.
   */

  clutter_actor_unrealize (actor);

  if (!GetClientRect (hwnd, &client_rect))
    {
      g_warning ("Unable to retrieve the new window geometry");
      return FALSE;
    }
  window_pos.x = client_rect.left;
  window_pos.y = client_rect.right;
  ClientToScreen (hwnd, &window_pos);

  CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)", (int) hwnd);

  stage_win32->hwnd = hwnd;
  stage_win32->is_foreign_win = TRUE;

  geom.x = 0;
  geom.y = 0;
  geom.width = client_rect.right - client_rect.left;
  geom.height = client_rect.bottom - client_rect.top;

  clutter_actor_set_geometry (actor, &geom);
  clutter_actor_realize (actor);

  return TRUE;
}
/**
 * clutter_stage_manager_set_default_stage:
 * @stage_manager: a #ClutterStageManager
 * @stage: a #ClutterStage
 *
 * Sets @stage as the default stage.
 *
 * Since: 0.8
 */
void
clutter_stage_manager_set_default_stage (ClutterStageManager *stage_manager,
                                         ClutterStage        *stage)
{
  g_return_if_fail (CLUTTER_IS_STAGE_MANAGER (stage_manager));
  g_return_if_fail (CLUTTER_IS_STAGE (stage));

  if (!g_slist_find (stage_manager->stages, stage))
    _clutter_stage_manager_add_stage (stage_manager, stage);

  default_stage = stage;

  g_object_notify (G_OBJECT (stage_manager), "default-stage");
}
Beispiel #10
0
/**
 * clutter_gdk_set_stage_foreign:
 * @stage: a #ClutterStage
 * @window: an existing #GdkWindow
 *
 * Target the #ClutterStage to use an existing external #GdkWindow
 *
 * Return value: %TRUE if foreign window is valid
 *
 * Since: 1.10
 */
gboolean
clutter_gdk_set_stage_foreign (ClutterStage *stage,
                               GdkWindow    *window)
{
  ForeignWindowClosure closure;
  ClutterStageGdk *stage_gdk;
  ClutterStageWindow *impl;
  ClutterActor *actor;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
  g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE);
  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);

  impl = _clutter_stage_get_window (stage);
  if (!CLUTTER_IS_STAGE_GDK (impl))
    {
      g_critical ("The Clutter backend is not a GDK backend");
      return FALSE;
    }

  stage_gdk = CLUTTER_STAGE_GDK (impl);

  if (g_object_get_data (G_OBJECT (window), "clutter-stage-window") != NULL)
    {
      g_critical ("The provided GdkWindow is already in use by another ClutterStage");
      return FALSE;
    }

  closure.stage_gdk = stage_gdk;
  closure.window = g_object_ref (window);

  actor = CLUTTER_ACTOR (stage);

  _clutter_actor_rerealize (actor,
                            set_foreign_window_callback,
                            &closure);

  /* Queue a relayout - so the stage will be allocated the new
   * window size.
   *
   * Note also that when the stage gets allocated the new
   * window size that will result in the stage's
   * priv->viewport being changed, which will in turn result
   * in the Cogl viewport changing when _clutter_do_redraw
   * calls _clutter_stage_maybe_setup_viewport().
   */
  clutter_actor_queue_relayout (actor);

  return TRUE;
}
Beispiel #11
0
void
penge_utils_signal_activated (ClutterActor *actor)
{
  while (actor)
  {
    if (clutter_actor_get_parent (actor) &&
        CLUTTER_IS_STAGE (clutter_actor_get_parent (actor)))
    {
      g_signal_emit_by_name (actor, "activated", NULL);
      return;
    }

    actor = clutter_actor_get_parent (actor);
  }
}
Beispiel #12
0
/**
 * clutter_gdk_get_stage_window:
 * @stage: a #ClutterStage
 *
 * Gets the stages GdkWindow.
 *
 * Return value: (transfer none): A GdkWindow* for the stage window.
 *
 * Since: 1.10
 */
GdkWindow *
clutter_gdk_get_stage_window (ClutterStage *stage)
{
  ClutterStageWindow *impl;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);

  impl = _clutter_stage_get_window (stage);
  if (!CLUTTER_IS_STAGE_GDK (impl))
    {
      g_critical ("The Clutter backend is not a GDK backend");
      return NULL;
    }

  return CLUTTER_STAGE_GDK (impl)->window;
}
Beispiel #13
0
/**
 * clutter_test_check_actor_at_point:
 * @stage: a #ClutterStage
 * @point: coordinates to check
 * @actor: the expected actor at the given coordinates
 * @result: (out) (nullable): actor at the coordinates
 *
 * Checks the given coordinates of the @stage and compares the
 * actor found there with the given @actor.
 *
 * Returns: %TRUE if the actor at the given coordinates matches
 *
 * Since: 1.18
 */
gboolean
clutter_test_check_actor_at_point (ClutterActor        *stage,
                                   const ClutterPoint  *point,
                                   ClutterActor        *actor,
                                   ClutterActor       **result)
{
  ValidateData *data;
  guint press_id = 0;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
  g_return_val_if_fail (point != NULL, FALSE);
  g_return_val_if_fail (CLUTTER_IS_ACTOR (stage), FALSE);
  g_return_val_if_fail (result != NULL, FALSE);

  data = g_new0 (ValidateData, 1);
  data->stage = stage;
  data->point = *point;
  data->check_actor = TRUE;

  if (g_test_verbose ())
    {
      g_printerr ("Press ESC to close the stage and resume the test\n");
      press_id = g_signal_connect (stage, "key-press-event",
                                   G_CALLBACK (on_key_press_event),
                                   data);
    }

  clutter_actor_show (stage);

  clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
                                         validate_stage,
                                         data,
                                         NULL);

  while (!data->was_painted)
    g_main_context_iteration (NULL, TRUE);

  *result = data->result;

  if (press_id != 0)
    g_signal_handler_disconnect (stage, press_id);

  g_free (data);

  return *result == actor;
}
Beispiel #14
0
/**
 * st_theme_context_get_for_stage:
 * @stage: a #ClutterStage
 *
 * Gets a singleton theme context associated with the stage.
 *
 * Return value: (transfer none): the singleton theme context for the stage
 */
StThemeContext *
st_theme_context_get_for_stage (ClutterStage *stage)
{
  StThemeContext *context;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);

  context = g_object_get_data (G_OBJECT (stage), "st-theme-context");
  if (context)
    return context;

  context = st_theme_context_new ();
  g_object_set_data (G_OBJECT (stage), "st-theme-context", context);
  g_signal_connect (stage, "destroy",
                    G_CALLBACK (on_stage_destroy), NULL);

  return context;
}
static void
key_focus_in_cb (ClutterActor *actor,
		 gpointer      data)
{
  ClutterActor *focus_box = CLUTTER_ACTOR (data);  

  if (CLUTTER_IS_STAGE (actor))
    clutter_actor_hide (focus_box);
  else
    {
      clutter_actor_set_position (focus_box,
				  clutter_actor_get_x (actor) - 5,
				  clutter_actor_get_y (actor) - 5);

      clutter_actor_set_size (focus_box,
			      clutter_actor_get_width (actor) + 10,
			      clutter_actor_get_height (actor) + 10);
      clutter_actor_show (focus_box);
    }
}
Beispiel #16
0
/**
 * st_widget_get_theme_node:
 * @widget: a #StWidget
 *
 * Gets the theme node holding style information for the widget.
 * The theme node is used to access standard and custom CSS
 * properties of the widget.
 *
 * Return value: (transfer none): the theme node for the widget.
 *   This is owned by the widget. When attributes of the widget
 *   or the environment that affect the styling change (for example
 *   the style_class property of the widget), it will be recreated,
 *   and the ::style-changed signal will be emitted on the widget.
 */
StThemeNode *
st_widget_get_theme_node (StWidget *widget)
{
  StWidgetPrivate *priv = widget->priv;

  if (priv->theme_node == NULL)
    {
      StThemeNode *parent_node = NULL;
      ClutterStage *stage = NULL;
      ClutterActor *parent;

      parent = clutter_actor_get_parent (CLUTTER_ACTOR (widget));
      while (parent != NULL)
        {
          if (parent_node == NULL && ST_IS_WIDGET (parent))
            parent_node = st_widget_get_theme_node (ST_WIDGET (parent));
          else if (CLUTTER_IS_STAGE (parent))
            stage = CLUTTER_STAGE (parent);

          parent = clutter_actor_get_parent (parent);
        }

      if (stage == NULL)
        {
          g_error ("st_widget_get_theme_node called on a widget not in a stage");
        }

      if (parent_node == NULL)
        parent_node = get_root_theme_node (CLUTTER_STAGE (stage));

      priv->theme_node = st_theme_node_new (st_theme_context_get_for_stage (stage),
                                            parent_node, priv->theme,
                                            G_OBJECT_TYPE (widget),
                                            clutter_actor_get_name (CLUTTER_ACTOR (widget)),
                                            priv->style_class,
                                            priv->pseudo_class,
                                            priv->inline_style);
    }

  return priv->theme_node;
}
ClutterStageWindow *
_clutter_backend_create_stage (ClutterBackend  *backend,
                               ClutterStage    *wrapper,
                               GError         **error)
{
  ClutterBackendClass *klass;
  ClutterStageWindow *stage_window;

  g_assert (CLUTTER_IS_BACKEND (backend));
  g_assert (CLUTTER_IS_STAGE (wrapper));

  klass = CLUTTER_BACKEND_GET_CLASS (backend);
  if (klass->create_stage != NULL)
    stage_window = klass->create_stage (backend, wrapper, error);
  else
    stage_window = NULL;

  if (stage_window == NULL)
    return NULL;

  g_assert (CLUTTER_IS_STAGE_WINDOW (stage_window));

  return stage_window;
}
void
_clutter_backend_ensure_context (ClutterBackend *backend,
                                 ClutterStage   *stage)
{
  static ClutterStage *current_context_stage = NULL;

  g_assert (CLUTTER_IS_BACKEND (backend));
  g_assert (CLUTTER_IS_STAGE (stage));

  if (current_context_stage != stage || !CLUTTER_ACTOR_IS_REALIZED (stage))
    {
      ClutterStage *new_stage = NULL;

      if (!CLUTTER_ACTOR_IS_REALIZED (stage))
        {
          new_stage = NULL;

          CLUTTER_NOTE (BACKEND,
                        "Stage [%p] is not realized, unsetting the stage",
                        stage);
        }
      else
        {
          new_stage = stage;

          CLUTTER_NOTE (BACKEND,
                        "Setting the new stage [%p]",
                        new_stage);
        }

      /* XXX: Until Cogl becomes fully responsible for backend windows
       * Clutter need to manually keep it informed of the current window size
       *
       * NB: This must be done after we ensure_context above because Cogl
       * always assumes there is a current GL context.
       */
      if (new_stage != NULL)
        {
          float width, height;

          _clutter_backend_ensure_context_internal (backend, new_stage);

          clutter_actor_get_size (CLUTTER_ACTOR (stage), &width, &height);

          cogl_onscreen_clutter_backend_set_size (width, height);

          /* Eventually we will have a separate CoglFramebuffer for
           * each stage and each one will track private projection
           * matrix and viewport state, but until then we need to make
           * sure we update the projection and viewport whenever we
           * switch between stages.
           *
           * This dirty mechanism will ensure they are asserted before
           * the next paint...
           */
          _clutter_stage_dirty_viewport (stage);
          _clutter_stage_dirty_projection (stage);
        }

      /* FIXME: With a NULL stage and thus no active context it may make more
       * sense to clean the context but then re call with the default stage 
       * so at least there is some kind of context in place (as to avoid
       * potential issue of GL calls with no context).
       */
      current_context_stage = new_stage;
    }
  else
    CLUTTER_NOTE (BACKEND, "Stage is the same");
}
Beispiel #19
0
static void moses_overview_dispose(GObject *object)
{
    MosesOverview *overview = MOSES_OVERVIEW(object);
    MosesOverviewPrivate* priv = overview->priv;

    if (priv->disposed) return;
    priv->disposed = TRUE;

    g_clear_pointer(&priv->ov_head, g_object_unref);

    MetaScreen* screen = meta_plugin_get_screen(priv->plugin);
    ClutterActor* stage = meta_get_stage_for_screen(screen);

    ClutterActor* to_focus = NULL;
    if (priv->selected_actor) {
        to_focus = clutter_clone_get_source(CLUTTER_CLONE(priv->selected_actor));
    }

    for (int i = 0; priv->clones && i < priv->clones->len; i++) {
        ClutterActor* clone = g_ptr_array_index(priv->clones, i);
        ClutterActor* orig = clutter_clone_get_source(CLUTTER_CLONE(clone));
        clutter_actor_show(orig); // FIXME: maybe some actors had not been shown.
        clutter_actor_destroy(clone);
    }

    for (int i = 0; priv->badges && i < priv->badges->len; i++) {
        clutter_actor_destroy(CLUTTER_ACTOR(g_ptr_array_index(priv->badges, i)));
    }

    if (priv->background_actor) {
        clutter_actor_show(clutter_clone_get_source(CLUTTER_CLONE(priv->background_actor)));
        g_clear_pointer(&priv->background_actor, clutter_actor_destroy);
    }

    if (priv->modaled) {
        meta_plugin_end_modal(priv->plugin, clutter_get_current_event_time());
        meta_enable_unredirect_for_screen(screen);

        if (priv->selected_workspace) {
            meta_workspace_activate(priv->selected_workspace, CLUTTER_CURRENT_TIME);
            MetaDisplay* display = meta_screen_get_display(screen);
            meta_compositor_switch_workspace(meta_display_get_compositor(display),
                    meta_screen_get_active_workspace(screen),
                    priv->selected_workspace, META_MOTION_DOWN);

        } else if (to_focus) {
            clutter_stage_set_key_focus(CLUTTER_STAGE(stage), to_focus);
            MetaWindowActor* actor = META_WINDOW_ACTOR(to_focus);
            MetaWindow* win = meta_window_actor_get_meta_window(actor);
            meta_window_raise(win);
            meta_window_focus(win, CLUTTER_CURRENT_TIME);

        } else if (priv->previous_focused) {
            if (!CLUTTER_IS_STAGE(priv->previous_focused)) {
                clutter_stage_set_key_focus(CLUTTER_STAGE(stage), priv->previous_focused);
            }
        }
    }

    G_OBJECT_CLASS(moses_overview_parent_class)->dispose(object);
}
Beispiel #20
0
/**
 * clutter_x11_set_stage_foreign:
 * @stage: a #ClutterStage
 * @xwindow: an existing X Window id
 *
 * Target the #ClutterStage to use an existing external X Window
 *
 * Return value: %TRUE if foreign window is valid
 *
 *
 */
gboolean
clutter_x11_set_stage_foreign (ClutterStage *stage,
                               Window        xwindow)
{
  ClutterBackendX11 *backend_x11;
  ClutterStageX11 *stage_x11;
  ClutterStageCogl *stage_cogl;
  ClutterStageWindow *impl;
  ClutterActor *actor;
  gint x, y;
  guint width, height, border, depth;
  Window root_return;
  Status status;
  ForeignWindowData fwd;
  XVisualInfo *xvisinfo;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
  g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE);
  g_return_val_if_fail (xwindow != None, FALSE);

  impl = _clutter_stage_get_window (stage);
  stage_x11 = CLUTTER_STAGE_X11 (impl);
  stage_cogl = CLUTTER_STAGE_COGL (impl);
  backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);

  xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11);
  g_return_val_if_fail (xvisinfo != NULL, FALSE);

  clutter_x11_trap_x_errors ();

  status = XGetGeometry (backend_x11->xdpy, xwindow,
                         &root_return,
                         &x, &y,
                         &width, &height,
                         &border,
                         &depth);

  if (clutter_x11_untrap_x_errors () || !status)
    {
      g_critical ("Unable to retrieve the geometry of the foreign window: "
                  "XGetGeometry() failed (status code: %d)", status);
      return FALSE;
    }

  if (width == 0 || height == 0)
    {
      g_warning ("The size of the foreign window is 0x0");
      return FALSE;
    }

  if (depth != xvisinfo->depth)
    {
      g_warning ("The depth of the visual of the foreign window is %d, but "
                 "Clutter has been initialized to require a visual depth "
                 "of %d",
                 depth,
                 xvisinfo->depth);
      return FALSE;
    }

  fwd.stage_x11 = stage_x11;
  fwd.xwindow = xwindow;

  /* destroy the old Window, if we have one and it's ours */
  if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
    fwd.destroy_old_xwindow = TRUE;
  else
    fwd.destroy_old_xwindow = FALSE;

  fwd.geom.x = x;
  fwd.geom.y = y;
  fwd.geom.width = width;
  fwd.geom.height = height;

  actor = CLUTTER_ACTOR (stage);

  _clutter_actor_rerealize (actor,
                            set_foreign_window_callback,
                            &fwd);

  /* Queue a relayout - so the stage will be allocated the new
   * window size.
   *
   * Note also that when the stage gets allocated the new
   * window size that will result in the stage's
   * priv->viewport being changed, which will in turn result
   * in the Cogl viewport changing when _clutter_do_redraw
   * calls _clutter_stage_maybe_setup_viewport().
   */
  clutter_actor_queue_relayout (actor);

  return TRUE;
}