gnome-shell/src/shell-gtk-embed.c

298 lines
9.0 KiB
C
Raw Normal View History

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "shell-embedded-window-private.h"
#include <clutter/glx/clutter-glx.h>
#include <clutter/x11/clutter-x11.h>
#include <gdk/gdkx.h>
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,
ClutterUnit for_height,
ClutterUnit *min_width_p,
ClutterUnit *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,
ClutterUnit for_width,
ClutterUnit *min_height_p,
ClutterUnit *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,
gboolean absolute_origin_changed)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
int wx = 0, wy = 0, x, y, ax, ay;
CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->
allocate (actor, box, absolute_origin_changed);
/* 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,
wx, 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);
}