298 lines
9.0 KiB
C
298 lines
9.0 KiB
C
|
/* -*- 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);
|
||
|
}
|