/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Johan Bilien * * Copyright (C) 2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:clutter-x11-texture-pixmap * @short_description: A texture which displays the content of an X Pixmap. * * #ClutterX11TexturePixmap is a class for displaying the content of an * X Pixmap as a ClutterActor. Used together with the X Composite extension, * it allows to display the content of X Windows inside Clutter. * * The class uses the GLX_EXT_texture_from_pixmap OpenGL extension * (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt) * if available */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "../clutter-marshal.h" #include "clutter-x11-texture-pixmap.h" #include "clutter-x11.h" #include "clutter-backend-x11.h" #include "cogl.h" enum { PROP_PIXMAP = 1, PROP_PIXMAP_WIDTH, PROP_PIXMAP_HEIGHT, PROP_DEPTH }; enum { UPDATE_AREA, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; struct _ClutterX11TexturePixmapPrivate { Pixmap pixmap; guint pixmap_width, pixmap_height; guint depth; XImage *image; }; static ClutterBackendX11 *backend = NULL; static void clutter_x11_texture_pixmap_class_init (ClutterX11TexturePixmapClass *klass); static void clutter_x11_texture_pixmap_init (ClutterX11TexturePixmap *self); static GObject *clutter_x11_texture_pixmap_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void clutter_x11_texture_pixmap_dispose (GObject *object); static void clutter_x11_texture_pixmap_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void clutter_x11_texture_pixmap_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void clutter_x11_texture_pixmap_realize (ClutterActor *actor); static void clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height); G_DEFINE_TYPE (ClutterX11TexturePixmap, clutter_x11_texture_pixmap, CLUTTER_TYPE_TEXTURE); static void clutter_x11_texture_pixmap_class_init (ClutterX11TexturePixmapClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; ClutterBackend *default_backend; g_type_class_add_private (klass, sizeof (ClutterX11TexturePixmapPrivate)); object_class->constructor = clutter_x11_texture_pixmap_constructor; object_class->dispose = clutter_x11_texture_pixmap_dispose; object_class->set_property = clutter_x11_texture_pixmap_set_property; object_class->get_property = clutter_x11_texture_pixmap_get_property; actor_class->realize = clutter_x11_texture_pixmap_realize; klass->update_area = clutter_x11_texture_pixmap_update_area_real; pspec = g_param_spec_uint ("pixmap", "Pixmap", "The X Pixmap to which this texture will be bound", 0, G_MAXINT, None, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_PIXMAP, pspec); pspec = g_param_spec_uint ("pixmap-width", "Pixmap width", "The width of the " "pixmap bound to this texture", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_PIXMAP_WIDTH, pspec); pspec = g_param_spec_uint ("pixmap-height", "Pixmap height", "The height of the " "pixmap bound to this texture", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_PIXMAP_HEIGHT, pspec); pspec = g_param_spec_uint ("depth", "Depth", "The depth (in number of bits) of the " "pixmap bound to this texture", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_property (object_class, PROP_DEPTH, pspec); /** * ClutterX11TexturePixmap::update-area: * @texture: the object which received the signal * * The ::hide signal is emitted to ask the texture to update its * content from its source pixmap. * * Since: 0.8 */ signals[UPDATE_AREA] = g_signal_new ("update-area", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterX11TexturePixmapClass, update_area), NULL, NULL, clutter_marshal_VOID__INT_INT_INT_INT, G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); default_backend = clutter_get_default_backend (); if (!CLUTTER_IS_BACKEND_X11 (default_backend)) { g_critical ("ClutterX11TexturePixmap instanciated with a " "non-X11 backend"); return; } backend = (ClutterBackendX11 *)default_backend; } static void clutter_x11_texture_pixmap_init (ClutterX11TexturePixmap *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_X11_TYPE_TEXTURE_PIXMAP, ClutterX11TexturePixmapPrivate); } static GObject * clutter_x11_texture_pixmap_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object = G_OBJECT_CLASS (clutter_x11_texture_pixmap_parent_class)-> constructor (type, n_construct_properties, construct_properties); g_object_set (object, "sync-size", FALSE, NULL); return object; } static void clutter_x11_texture_pixmap_dispose (GObject *object) { ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object); ClutterX11TexturePixmapPrivate *priv = texture->priv; if (priv->image) { XDestroyImage (priv->image); priv->image = NULL; } G_OBJECT_CLASS (clutter_x11_texture_pixmap_parent_class)->dispose (object); } static void clutter_x11_texture_pixmap_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object); ClutterX11TexturePixmapPrivate *priv = texture->priv; switch (prop_id) { case PROP_PIXMAP: clutter_x11_texture_pixmap_set_pixmap (texture, g_value_get_uint (value), priv->pixmap_width, priv->pixmap_height, priv->depth); break; case PROP_PIXMAP_WIDTH: clutter_x11_texture_pixmap_set_pixmap (texture, priv->pixmap, g_value_get_uint (value), priv->pixmap_height, priv->depth); break; case PROP_PIXMAP_HEIGHT: clutter_x11_texture_pixmap_set_pixmap (texture, priv->pixmap, priv->pixmap_width, g_value_get_uint (value), priv->depth); break; case PROP_DEPTH: clutter_x11_texture_pixmap_set_pixmap (texture, priv->pixmap, priv->pixmap_width, priv->pixmap_height, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_x11_texture_pixmap_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object); ClutterX11TexturePixmapPrivate *priv = texture->priv; switch (prop_id) { case PROP_PIXMAP: g_value_set_uint (value, priv->pixmap); break; case PROP_PIXMAP_WIDTH: g_value_set_uint (value, priv->pixmap_width); break; case PROP_PIXMAP_HEIGHT: g_value_set_uint (value, priv->pixmap_height); break; case PROP_DEPTH: g_value_set_uint (value, priv->depth); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_x11_texture_pixmap_realize (ClutterActor *actor) { ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (actor); ClutterX11TexturePixmapPrivate *priv = texture->priv; CLUTTER_ACTOR_CLASS (clutter_x11_texture_pixmap_parent_class)-> realize (actor); CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); clutter_x11_texture_pixmap_update_area_real (texture, 0, 0, priv->pixmap_width, priv->pixmap_height); } static void clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height) { ClutterX11TexturePixmapPrivate *priv; Display *dpy; XImage *image; guint *pixel, *l; GError *error = NULL; guint bytes_per_line; guint8 *data; gboolean data_allocated = FALSE; int err_code; if (!CLUTTER_ACTOR_IS_REALIZED (texture)) return; priv = texture->priv; dpy = ((ClutterBackendX11 *)backend)->xdpy; clutter_x11_trap_x_errors (); if (!priv->image) priv->image = XGetImage (dpy, priv->pixmap, 0, 0, priv->pixmap_width, priv->pixmap_height, AllPlanes, ZPixmap); else XGetSubImage (dpy, priv->pixmap, x, y, width, height, AllPlanes, ZPixmap, priv->image, x, y); XSync (dpy, FALSE); if ((err_code = clutter_x11_untrap_x_errors ())) return; image = priv->image; if (priv->depth == 24) { guint *first_line = (guint *)image->data + y * image->bytes_per_line / 4; for (l = first_line; l != (first_line + height * image->bytes_per_line / 4); l = l + image->bytes_per_line / 4) { for (pixel = l + x; pixel != l + x + width; pixel ++) { ((guint8 *)pixel)[3] = 0xFF; } } data = (guint8 *)first_line + x * 4; bytes_per_line = image->bytes_per_line; } else if (priv->depth == 16) { guint16 *p, *lp; data = g_malloc (height * width * 4); data_allocated = TRUE; bytes_per_line = priv->pixmap_width * 4; for (l = (guint *)data, lp = (guint16 *)image->data + y * image->bytes_per_line / 2; l != ((guint *)data + height * width); l = l + width, lp = lp + image->bytes_per_line / 2) { for (pixel = l, p = lp + x; pixel != l + width; pixel ++, p++) { *pixel = 0xFF000000 | (guint)((*p) & 0xf800) << 8 | (guint)((*p) & 0x07e0) << 5 | (guint)((*p) & 0x001f) << 3; } } } else if (priv->depth == 32) { bytes_per_line = image->bytes_per_line; data = (guint8 *)image->data + y * bytes_per_line + x * 4; } else return; if (x != 0 || y != 0 || width != priv->pixmap_width || height != priv->pixmap_height) clutter_texture_set_area_from_rgb_data (CLUTTER_TEXTURE (texture), data, TRUE, x, y, width, height, bytes_per_line, 4, CLUTTER_TEXTURE_RGB_FLAG_BGR, &error); else clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (texture), data, TRUE, width, height, bytes_per_line, 4, CLUTTER_TEXTURE_RGB_FLAG_BGR, &error); if (error) { g_warning ("Error when uploading from pixbuf: %s", error->message); g_error_free (error); } if (data_allocated) g_free (data); } /** * clutter_x11_texture_pixmap_new: * * Return value: A new #ClutterX11TexturePixmap * * Since: 0.8 **/ ClutterActor * clutter_x11_texture_pixmap_new (void) { ClutterActor *actor; actor = g_object_new (CLUTTER_X11_TYPE_TEXTURE_PIXMAP, NULL); return actor; } /** * clutter_x11_texture_pixmap_new_with_pixmap: * @pixmap: the X Pixmap to which this texture should be bound * @width: the width of the X pixmap * @height: the height of the X pixmap * @depth: the depth of the X pixmap * * Return value: A new #ClutterX11TexturePixmap bound to the given X Pixmap * * Since 0.8 **/ ClutterActor * clutter_x11_texture_pixmap_new_with_pixmap (Pixmap pixmap, guint width, guint height, guint depth) { ClutterActor *actor; actor = g_object_new (CLUTTER_X11_TYPE_TEXTURE_PIXMAP, "pixmap", pixmap, "pixmap-width", width, "pixmap-height", height, "depth", depth, NULL); return actor; } /** * clutter_x11_texture_pixmap_set_pixmap: * @texture: the texture to bind * @pixmap: the X Pixmap to which the texture should be bound * @width: the Pixmap width * @height: the Pixmap height * @depth: the Pixmap depth, in number of bits * * Sets the X Pixmap to which the texture should be bound. * * Since: 0.8 **/ void clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, Pixmap pixmap, guint width, guint height, guint depth) { ClutterX11TexturePixmapPrivate *priv; g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture)); priv = texture->priv; if (priv->pixmap != pixmap) { priv->pixmap = pixmap; g_object_notify (G_OBJECT (texture), "pixmap"); } if (priv->pixmap_width != width) { priv->pixmap_width = width; g_object_notify (G_OBJECT (texture), "pixmap-width"); } if (priv->pixmap_height != height) { priv->pixmap_height = height; g_object_notify (G_OBJECT (texture), "pixmap-height"); } if (priv->depth != depth) { priv->depth = depth; g_object_notify (G_OBJECT (texture), "depth"); } if (priv->depth != 0 && priv->pixmap != None && priv->pixmap_width != 0 && priv->pixmap_height != 0) { if (priv->image) { XDestroyImage (priv->image); priv->image = NULL; } if (CLUTTER_ACTOR_IS_REALIZED (texture)) clutter_x11_texture_pixmap_update_area (texture, 0, 0, priv->pixmap_width, priv->pixmap_height); } } /** * clutter_x11_texture_pixmap_update_area: * @texture: The texture whose content shall be updated. * @x: the X coordinate of the area to update * @y: the Y coordinate of the area to update * @width: the width of the area to update * @height: the height of the area to update * * Performs the actual binding of texture to the current content of * the pixmap. Can be called to update the texture if the pixmap * content has changed. * * Since: 0.8 **/ void clutter_x11_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height) { g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture)); g_signal_emit (texture, signals[UPDATE_AREA], 0, x, y, width, height); }