2008-04-15 Matthew Allum <mallum@openedhand.com>

Work related to #873;

        * clutter/glx/clutter-backend-glx.c:
        * clutter/glx/clutter-backend-glx.h:
        * clutter/x11/clutter-x11-texture-pixmap.c:
        * clutter/x11/clutter-x11-texture-pixmap.h:
        General cleanup of texture pixmap code, adding;
          - Pixmap dimentions and depth now auto probed, read only props.
          - More X safety traps
          - Add support for optionally tracking damage and automatically
            updating texture.

        * clutter/glx/clutter-glx-texture-pixmap.c:
        * clutter/glx/clutter-glx-texture-pixmap.h:
        General cleanup and some safety additions. Needs more work so
        'proper' subclass - dependent on new COGL.

        * clutter/x11/clutter-backend-x11.c: (clutter_x11_remove_filter):
        Invert g_return_if check.

        * configure.ac:
        Pull in XComposite and XDamage (at least for now)

        * tests/Makefile.am:
        * tests/test-pixmap.c:
        Add a modified test from Johan for above.
This commit is contained in:
Matthew Allum
2008-04-15 21:18:13 +00:00
parent 3d1f2a0859
commit c2cb5bf64a
11 changed files with 778 additions and 330 deletions

View File

@ -47,21 +47,36 @@
#include "cogl.h"
/* FIXME: Check exts exist in autogen */
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xcomposite.h>
enum
{
PROP_PIXMAP = 1,
PROP_PIXMAP_WIDTH,
PROP_PIXMAP_HEIGHT,
PROP_DEPTH
PROP_DEPTH,
PROP_AUTO
};
enum
{
UPDATE_AREA,
/* FIXME: Pixmap lost signal? */
LAST_SIGNAL
};
static ClutterX11FilterReturn
on_x_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data);
static void
clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
gint x,
gint y,
gint width,
gint height);
static guint signals[LAST_SIGNAL] = { 0, };
struct _ClutterX11TexturePixmapPrivate
@ -72,133 +87,128 @@ struct _ClutterX11TexturePixmapPrivate
XImage *image;
gboolean automatic_updates;
Damage damage;
};
static ClutterBackendX11 *backend = NULL;
static int _damage_event_base = 0;
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);
/* FIXME: Ultimatly with current cogl we should subclass clutter actor */
G_DEFINE_TYPE (ClutterX11TexturePixmap, \
clutter_x11_texture_pixmap, \
CLUTTER_TYPE_TEXTURE);
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)
static gboolean
check_extensions (ClutterX11TexturePixmap *texture)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
ClutterBackend *default_backend;
int event_base, error_base;
int damage_error;
ClutterX11TexturePixmapPrivate *priv;
Display *dpy;
g_type_class_add_private (klass, sizeof (ClutterX11TexturePixmapPrivate));
priv = texture->priv;
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;
if (_damage_event_base)
return TRUE;
actor_class->realize = clutter_x11_texture_pixmap_realize;
dpy = clutter_x11_get_default_display();
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))
if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
{
g_critical ("ClutterX11TexturePixmap instanciated with a "
"non-X11 backend");
return;
g_warning ("No composite extension\n");
return FALSE;
}
backend = (ClutterBackendX11 *)default_backend;
if (!XDamageQueryExtension (dpy,
&_damage_event_base, &damage_error))
{
g_warning ("No Damage extension\n");
return FALSE;
}
return TRUE;
}
static ClutterX11FilterReturn
on_x_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data)
{
ClutterX11TexturePixmap *texture;
ClutterX11TexturePixmapPrivate *priv;
Display *dpy;
texture = CLUTTER_X11_TEXTURE_PIXMAP (data);
g_return_val_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture), \
CLUTTER_X11_FILTER_CONTINUE);
dpy = clutter_x11_get_default_display();
priv = texture->priv;
if (xev->type == _damage_event_base + XDamageNotify)
{
XserverRegion parts;
gint i, r_count;
XRectangle *r_damage;
XRectangle r_bounds;
XDamageNotifyEvent *dev = (XDamageNotifyEvent*)xev;
if (dev->drawable != priv->pixmap)
return CLUTTER_X11_FILTER_CONTINUE;
clutter_x11_trap_x_errors ();
/*
* Retrieve the damaged region and break it down into individual
* rectangles so we do not have to update the whole shebang.
*/
parts = XFixesCreateRegion (dpy, 0, 0);
XDamageSubtract (dpy, priv->damage, None, parts);
r_damage = XFixesFetchRegionAndBounds (dpy,
parts,
&r_count,
&r_bounds);
clutter_x11_untrap_x_errors ();
if (r_damage)
{
for (i = 0; i < r_count; ++i)
clutter_x11_texture_pixmap_update_area (texture,
r_damage[i].x,
r_damage[i].y,
r_damage[i].width,
r_damage[i].height);
XFree (r_damage);
}
XFixesDestroyRegion (dpy, parts);
}
return CLUTTER_X11_FILTER_CONTINUE;
}
static void
free_damage_resources (ClutterX11TexturePixmap *texture)
{
ClutterX11TexturePixmapPrivate *priv;
Display *dpy;
priv = texture->priv;
dpy = clutter_x11_get_default_display();
if (priv->damage)
{
XDamageDestroy (dpy, priv->damage);
priv->damage = None;
}
clutter_x11_remove_filter (on_x_event_filter, (gpointer)texture);
}
static void
clutter_x11_texture_pixmap_init (ClutterX11TexturePixmap *self)
{
@ -207,15 +217,21 @@ clutter_x11_texture_pixmap_init (ClutterX11TexturePixmap *self)
CLUTTER_X11_TYPE_TEXTURE_PIXMAP,
ClutterX11TexturePixmapPrivate);
if (!check_extensions (self))
{
/* FIMXE: means display lacks needed extensions for at least auto.
* - a _can_autoupdate() method ?
*/
}
}
static GObject *
clutter_x11_texture_pixmap_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
guint n_props,
GObjectConstructParam *props)
{
GObject *object = G_OBJECT_CLASS (clutter_x11_texture_pixmap_parent_class)->
constructor (type, n_construct_properties, construct_properties);
constructor (type, n_props, props);
g_object_set (object,
"sync-size", FALSE,
@ -227,9 +243,11 @@ clutter_x11_texture_pixmap_constructor (GType type,
static void
clutter_x11_texture_pixmap_dispose (GObject *object)
{
ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
ClutterX11TexturePixmapPrivate *priv = texture->priv;
free_damage_resources (texture);
if (priv->image)
{
XDestroyImage (priv->image);
@ -237,7 +255,6 @@ clutter_x11_texture_pixmap_dispose (GObject *object)
}
G_OBJECT_CLASS (clutter_x11_texture_pixmap_parent_class)->dispose (object);
}
static void
@ -246,42 +263,21 @@ clutter_x11_texture_pixmap_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
ClutterX11TexturePixmapPrivate *priv = texture->priv;
ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
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;
clutter_x11_texture_pixmap_set_pixmap (texture,
g_value_get_uint (value));
break;
case PROP_AUTO:
clutter_x11_texture_pixmap_set_automatic (texture,
g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
@ -291,26 +287,29 @@ clutter_x11_texture_pixmap_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
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;
g_value_set_uint (value, priv->pixmap);
break;
case PROP_PIXMAP_WIDTH:
g_value_set_uint (value, priv->pixmap_width);
break;
g_value_set_uint (value, priv->pixmap_width);
break;
case PROP_PIXMAP_HEIGHT:
g_value_set_uint (value, priv->pixmap_height);
break;
g_value_set_uint (value, priv->pixmap_height);
break;
case PROP_DEPTH:
g_value_set_uint (value, priv->depth);
break;
g_value_set_uint (value, priv->depth);
break;
case PROP_AUTO:
g_value_set_boolean (value, priv->automatic_updates);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
@ -331,6 +330,107 @@ clutter_x11_texture_pixmap_realize (ClutterActor *actor)
priv->pixmap_height);
}
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 X11 Pixmap to 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_READABLE);
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_READABLE);
g_object_class_install_property (object_class, PROP_PIXMAP_HEIGHT, pspec);
pspec = g_param_spec_uint ("pixmap-depth",
"Pixmap Depth",
"The depth (in number of bits) of the "
"pixmap bound to this texture",
0, G_MAXUINT,
0,
G_PARAM_READABLE);
g_object_class_install_property (object_class, PROP_DEPTH, pspec);
pspec = g_param_spec_boolean ("automatic-updates",
"Automatic Updates",
"If the texture should be kept in "
"sync with any pixmap changes.",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_AUTO, 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;
}
}
static void
clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
gint x,
@ -352,9 +452,11 @@ clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
return;
priv = texture->priv;
dpy = ((ClutterBackendX11 *)backend)->xdpy;
dpy = clutter_x11_get_default_display();
clutter_x11_trap_x_errors ();
/* FIXME: Use XSHM here! */
if (!priv->image)
priv->image = XGetImage (dpy,
priv->pixmap,
@ -373,8 +475,15 @@ clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
x, y);
XSync (dpy, FALSE);
if ((err_code = clutter_x11_untrap_x_errors ()))
return;
{
g_warning ("Failed to get XImage of pixmap: %lx, removing.",
priv->pixmap);
/* safe to assume pixmap has gone away? - therefor reset */
clutter_x11_texture_pixmap_set_pixmap (texture, None);
return;
}
image = priv->image;
@ -490,18 +599,12 @@ clutter_x11_texture_pixmap_new (void)
* Since 0.8
**/
ClutterActor *
clutter_x11_texture_pixmap_new_with_pixmap (Pixmap pixmap,
guint width,
guint height,
guint depth)
clutter_x11_texture_pixmap_new_with_pixmap (Pixmap pixmap)
{
ClutterActor *actor;
actor = g_object_new (CLUTTER_X11_TYPE_TEXTURE_PIXMAP,
"pixmap", pixmap,
"pixmap-width", width,
"pixmap-height", height,
"depth", depth,
NULL);
return actor;
@ -521,67 +624,87 @@ clutter_x11_texture_pixmap_new_with_pixmap (Pixmap pixmap,
**/
void
clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture,
Pixmap pixmap,
guint width,
guint height,
guint depth)
Pixmap pixmap)
{
Window root;
int x, y;
unsigned int width, height, border_width, depth;
Status status = 0;
ClutterX11TexturePixmapPrivate *priv;
g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
priv = texture->priv;
clutter_x11_trap_x_errors ();
status = XGetGeometry (clutter_x11_get_default_display(),
(Drawable)pixmap,
&root,
&x,
&y,
&width,
&height,
&border_width,
&depth);
if (clutter_x11_untrap_x_errors () || status == 0)
{
if (pixmap != None)
g_warning ("Unable to query pixmap: %lx\n", pixmap);
pixmap = None;
width = height = depth = 0;
}
if (priv->image)
{
XDestroyImage (priv->image);
priv->image = NULL;
}
g_object_ref (texture);
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");
g_object_notify (G_OBJECT (texture), "pixmap-depth");
}
g_object_unref (texture);
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_actor_set_size (CLUTTER_ACTOR(texture),
priv->pixmap_width, priv->pixmap_height);
}
/**
@ -609,3 +732,71 @@ clutter_x11_texture_pixmap_update_area (ClutterX11TexturePixmap *texture,
g_signal_emit (texture, signals[UPDATE_AREA], 0, x, y, width, height);
}
/* FIXME: to implement */
void
clutter_x11_texture_pixmap_set_from_window (ClutterX11TexturePixmap *texture,
Window win,
gboolean reflect)
{
ClutterX11TexturePixmapPrivate *priv;
g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
/* This would mainly be used for compositing type situations
* with named pixmap (cannot be regular pixmap) and setting up
* actual window redirection.
*
* It also seems to can pass a window to texture_pixmap and it
* it works like redirectwindow automatic.
*
* Note windows do however change size, whilst pixmaps do not.
*/
priv = texture->priv;
/*
priv->window_pixmap = XCompositeNameWindowPixmap (dpy, win);
XCompositeRedirectWindow(clutter_x11_get_default_display(),
win_remote,
CompositeRedirectAutomatic);
*/
}
/* FIXME: Below will change, just proof of concept atm - it will not work
* 100% for named pixmaps.
*/
void
clutter_x11_texture_pixmap_set_automatic (ClutterX11TexturePixmap *texture,
gboolean setting)
{
ClutterX11TexturePixmapPrivate *priv;
Display *dpy;
g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
priv = texture->priv;
if (setting == priv->automatic_updates)
return;
dpy = clutter_x11_get_default_display();
if (setting == TRUE)
{
clutter_x11_add_filter (on_x_event_filter, (gpointer)texture);
/* NOTE: Appears this will not work for a named pixmap */
priv->damage = XDamageCreate (dpy,
priv->pixmap,
XDamageReportNonEmpty);
}
else
free_damage_resources (texture);
priv->automatic_updates = setting;
}