/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "shell-embedded-window-private.h" #include #include #include enum { PROP_0, PROP_WINDOW }; typedef struct _ShellGtkEmbedPrivate ShellGtkEmbedPrivate; /* The reason that the instance/class structures are here is to avoid * problems with g-ir-scanner chocking on ClutterGLXTexturePixmap. We * stick with having a separate private structure so that we can move * the instance/class structures back to the public header if this * code is reused in another context where inheritance from C is useful. */ struct _ShellGtkEmbed { ClutterGLXTexturePixmap parent; ShellGtkEmbedPrivate *priv; }; struct _ShellGtkEmbedClass { ClutterGLXTexturePixmapClass parent_class; }; struct _ShellGtkEmbedPrivate { ShellEmbeddedWindow *window; }; G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_GLX_TYPE_TEXTURE_PIXMAP); static void shell_gtk_embed_set_window (ShellGtkEmbed *embed, ShellEmbeddedWindow *window); static void shell_gtk_embed_on_window_destroy (GtkObject *object, ShellGtkEmbed *embed) { shell_gtk_embed_set_window (embed, NULL); } static void shell_gtk_embed_on_window_realize (GtkWidget *widget, ShellGtkEmbed *embed) { /* Here automatic=FALSE means to use CompositeRedirectManual. * That is, the X server shouldn't draw the window onto the * screen. */ clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed), GDK_WINDOW_XWINDOW (widget->window), FALSE); } static void shell_gtk_embed_set_window (ShellGtkEmbed *embed, ShellEmbeddedWindow *window) { if (embed->priv->window) { _shell_embedded_window_set_actor (embed->priv->window, NULL); g_object_unref (embed->priv->window); clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed), None, FALSE); g_signal_handlers_disconnect_by_func (embed->priv->window, (gpointer)shell_gtk_embed_on_window_destroy, embed); g_signal_handlers_disconnect_by_func (embed->priv->window, (gpointer)shell_gtk_embed_on_window_realize, embed); } embed->priv->window = window; if (embed->priv->window) { g_object_ref (embed->priv->window); _shell_embedded_window_set_actor (embed->priv->window, embed); g_signal_connect (embed->priv->window, "destroy", G_CALLBACK (shell_gtk_embed_on_window_destroy), embed); g_signal_connect (embed->priv->window, "realize", G_CALLBACK (shell_gtk_embed_on_window_realize), embed); if (GTK_WIDGET_REALIZED (window)) shell_gtk_embed_on_window_realize (GTK_WIDGET (embed->priv->window), embed); } clutter_actor_queue_relayout (CLUTTER_ACTOR (embed)); } static void shell_gtk_embed_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (object); switch (prop_id) { case PROP_WINDOW: shell_gtk_embed_set_window (embed, (ShellEmbeddedWindow *)g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void shell_gtk_embed_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, embed->priv->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void shell_gtk_embed_get_preferred_width (ClutterActor *actor, float for_height, float *min_width_p, float *natural_width_p) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); if (embed->priv->window && GTK_WIDGET_VISIBLE (embed->priv->window)) { GtkRequisition requisition; gtk_widget_size_request (GTK_WIDGET (embed->priv->window), &requisition); *min_width_p = *natural_width_p = requisition.width; } else *min_width_p = *natural_width_p = 0; } static void shell_gtk_embed_get_preferred_height (ClutterActor *actor, float for_width, float *min_height_p, float *natural_height_p) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); if (embed->priv->window && GTK_WIDGET_VISIBLE (embed->priv->window)) { GtkRequisition requisition; gtk_widget_size_request (GTK_WIDGET (embed->priv->window), &requisition); *min_height_p = *natural_height_p = requisition.height; } else *min_height_p = *natural_height_p = 0; } static void shell_gtk_embed_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); float wx = 0.0, wy = 0.0, x, y, ax, ay; CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)-> allocate (actor, box, flags); /* Find the actor's new coordinates in terms of the stage (which is * priv->window's parent window. */ while (actor) { clutter_actor_get_position (actor, &x, &y); clutter_actor_get_anchor_point (actor, &ax, &ay); wx += x - ax; wy += y - ay; actor = clutter_actor_get_parent (actor); } _shell_embedded_window_allocate (embed->priv->window, (int)(0.5 + wx), (int)(0.5 + wy), box->x2 - box->x1, box->y2 - box->y1); } static void shell_gtk_embed_realize (ClutterActor *actor) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); _shell_embedded_window_realize (embed->priv->window); CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->realize (actor); } static void shell_gtk_embed_unrealize (ClutterActor *actor) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); _shell_embedded_window_unrealize (embed->priv->window); CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->unrealize (actor); } static void shell_gtk_embed_dispose (GObject *object) { ShellGtkEmbed *embed = SHELL_GTK_EMBED (object); shell_gtk_embed_set_window (embed, NULL); G_OBJECT_CLASS (shell_gtk_embed_parent_class)->dispose (object); } static void shell_gtk_embed_class_init (ShellGtkEmbedClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); g_type_class_add_private (klass, sizeof (ShellGtkEmbedPrivate)); object_class->get_property = shell_gtk_embed_get_property; object_class->set_property = shell_gtk_embed_set_property; object_class->dispose = shell_gtk_embed_dispose; actor_class->get_preferred_width = shell_gtk_embed_get_preferred_width; actor_class->get_preferred_height = shell_gtk_embed_get_preferred_height; actor_class->allocate = shell_gtk_embed_allocate; actor_class->realize = shell_gtk_embed_realize; actor_class->unrealize = shell_gtk_embed_unrealize; g_object_class_install_property (object_class, PROP_WINDOW, g_param_spec_object ("window", "Window", "ShellEmbeddedWindow to embed", SHELL_TYPE_EMBEDDED_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void shell_gtk_embed_init (ShellGtkEmbed *embed) { embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, SHELL_TYPE_GTK_EMBED, ShellGtkEmbedPrivate); /* automatic here means whether ClutterX11TexturePixmap should * process damage update and refresh the pixmap itself. */ clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (embed), TRUE); } /* * Public API */ ClutterActor * shell_gtk_embed_new (ShellEmbeddedWindow *window) { g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL); return g_object_new (SHELL_TYPE_GTK_EMBED, "window", window, NULL); }