/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Johan Bilien * Matthew Allum * Robert Bragg * * 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, see . * * */ /* TODO: * - Automagically handle named pixmaps, and window resizes (i.e * essentially handle window id's being passed in) ? */ /** * SECTION:clutter-glx-texture-pixmap * @short_description: A texture which displays the content of an X Pixmap. * * #ClutterGLXTexturePixmap 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 requires the GLX_EXT_texture_from_pixmap OpenGL extension * (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt) * * The GL_ARB_texture_non_power_of_two extension will be used if it is * available. Otherwise it will try to use the * GL_ARB_texture_rectangle extension. If both are available you can * force it to prefer the rectangle extension by setting the * CLUTTER_PIXMAP_TEXTURE_RECTANGLE to 'force'. To prevent it ever * using the rectangle extension you can set it to 'disable'. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "../x11/clutter-x11-texture-pixmap.h" #include "clutter-glx-texture-pixmap.h" #include "clutter-glx.h" #include "clutter-backend-glx.h" #include "../clutter-util.h" #include "../clutter-debug.h" #include "../clutter-private.h" #include "cogl/cogl.h" typedef void (*BindTexImage) (Display *display, GLXDrawable drawable, int buffer, int *attribList); typedef void (*ReleaseTexImage) (Display *display, GLXDrawable drawable, int buffer); typedef void (*GenerateMipmap) (GLenum target); typedef enum { CLUTTER_GLX_RECTANGLE_DISALLOW, CLUTTER_GLX_RECTANGLE_ALLOW, CLUTTER_GLX_RECTANGLE_FORCE } RectangleState; enum { PROP_0, PROP_AUTO_REDRAW, }; static BindTexImage _gl_bind_tex_image = NULL; static ReleaseTexImage _gl_release_tex_image = NULL; static GenerateMipmap _gl_generate_mipmap = NULL; static gboolean _have_tex_from_pixmap_ext = FALSE; static gboolean _ext_check_done = FALSE; static gboolean _have_tex_rectangle = FALSE; static RectangleState _rectangle_state = CLUTTER_GLX_RECTANGLE_ALLOW; struct _ClutterGLXTexturePixmapPrivate { GLXPixmap glx_pixmap; gboolean use_fallback; gboolean bound; gint can_mipmap; gint mipmap_generate_queued; gboolean using_rectangle; }; static void clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height); static void clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *tex); static ClutterX11TexturePixmapClass *parent_class = NULL; G_DEFINE_TYPE (ClutterGLXTexturePixmap, \ clutter_glx_texture_pixmap, \ CLUTTER_X11_TYPE_TEXTURE_PIXMAP); static gboolean texture_bind (ClutterGLXTexturePixmap *tex) { GLuint handle = 0; GLenum target = 0; CoglHandle cogl_tex; cogl_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE(tex)); if (!cogl_texture_get_gl_texture (cogl_tex, &handle, &target)) return FALSE; /* FIXME: fire off an error here? */ glBindTexture (target, handle); return TRUE; } static void on_glx_texture_pixmap_pre_paint (ClutterGLXTexturePixmap *texture, gpointer user_data) { if (texture->priv->mipmap_generate_queued) { GLuint handle = 0; GLenum target = 0; CoglHandle cogl_tex; cogl_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE(texture)); texture_bind (texture); cogl_texture_get_gl_texture (cogl_tex, &handle, &target); _gl_generate_mipmap (target); texture->priv->mipmap_generate_queued = 0; } /* Disable mipmaps if we can't support them */ if (clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)) == CLUTTER_TEXTURE_QUALITY_HIGH && !texture->priv->can_mipmap) { CoglHandle material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (texture)); cogl_material_set_layer_filters (material, 0, COGL_MATERIAL_FILTER_LINEAR, COGL_MATERIAL_FILTER_LINEAR); } } static void clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) { ClutterGLXTexturePixmapPrivate *priv; priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, ClutterGLXTexturePixmapPrivate); g_signal_connect (CLUTTER_ACTOR(self), "paint", G_CALLBACK (on_glx_texture_pixmap_pre_paint), NULL); if (_ext_check_done == FALSE) { const char *gl_extensions = NULL; const gchar *glx_extensions = NULL; const char *rect_env; glx_extensions = glXQueryExtensionsString (clutter_x11_get_default_display (), clutter_x11_get_default_screen ()); /* Check for the texture from pixmap extension */ if (_cogl_check_extension ("GLX_EXT_texture_from_pixmap", glx_extensions)) { _gl_bind_tex_image = (BindTexImage)cogl_get_proc_address ("glXBindTexImageEXT"); _gl_release_tex_image = (ReleaseTexImage)cogl_get_proc_address ("glXReleaseTexImageEXT"); if (_gl_bind_tex_image && _gl_release_tex_image) _have_tex_from_pixmap_ext = TRUE; } _gl_generate_mipmap = (GenerateMipmap)cogl_get_proc_address ("glGenerateMipmapEXT"); gl_extensions = (char *) glGetString (GL_EXTENSIONS); _have_tex_rectangle = _cogl_check_extension ("GL_ARB_texture_rectangle", gl_extensions); if ((rect_env = g_getenv ("CLUTTER_PIXMAP_TEXTURE_RECTANGLE"))) { if (g_ascii_strcasecmp (rect_env, "force") == 0) _rectangle_state = CLUTTER_GLX_RECTANGLE_FORCE; else if (g_ascii_strcasecmp (rect_env, "disable") == 0) _rectangle_state = CLUTTER_GLX_RECTANGLE_DISALLOW; else if (rect_env[0]) g_warning ("Unknown value for CLUTTER_PIXMAP_TEXTURE_RECTANGLE, " "should be 'force' or 'disable'"); } _ext_check_done = TRUE; } } static void clutter_glx_texture_pixmap_free_rectangle (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv = texture->priv; CoglHandle cogl_tex; /* Cogl won't free the GL texture resource if it was created with new_from_foreign so we need to free it manually */ if (priv->using_rectangle) { cogl_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture)); if (cogl_tex != COGL_INVALID_HANDLE) { GLuint gl_handle; GLenum gl_target; cogl_texture_get_gl_texture (cogl_tex, &gl_handle, &gl_target); if (gl_target == GL_TEXTURE_RECTANGLE_ARB) glDeleteTextures (1, &gl_handle); } priv->using_rectangle = FALSE; } } static void clutter_glx_texture_pixmap_dispose (GObject *object) { ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object); ClutterGLXTexturePixmapPrivate *priv = texture->priv; clutter_glx_texture_pixmap_free_rectangle (texture); if (priv->glx_pixmap != None) { clutter_x11_trap_x_errors (); glXDestroyPixmap (clutter_x11_get_default_display(), priv->glx_pixmap); XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); priv->glx_pixmap = None; } G_OBJECT_CLASS (clutter_glx_texture_pixmap_parent_class)->dispose (object); } static void clutter_glx_texture_pixmap_notify (GObject *object, GParamSpec *pspec) { if (g_str_equal (pspec->name, "pixmap")) { ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object); if (CLUTTER_ACTOR_IS_REALIZED (texture)) clutter_glx_texture_pixmap_create_glx_pixmap (texture); } } static gboolean should_use_rectangle (void) { /* Use the rectangle only if it is available and either: the CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable is set to 'force' *or* the env var is set to 'allow' (which is the default) and NPOTs textures are not available */ return _have_tex_rectangle && ((_rectangle_state == CLUTTER_GLX_RECTANGLE_ALLOW && !clutter_feature_available (CLUTTER_FEATURE_TEXTURE_NPOT)) || _rectangle_state == CLUTTER_GLX_RECTANGLE_FORCE); } static gboolean create_cogl_texture (ClutterTexture *texture, guint width, guint height) { ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; CoglHandle handle; gboolean using_rectangle; GLint gl_format; CoglPixelFormat cogl_format = COGL_PIXEL_FORMAT_RGBA_8888; guint depth; g_object_get (G_OBJECT (texture_glx), "pixmap-depth", &depth, NULL); if (depth == 32) { gl_format = GL_RGBA; cogl_format = COGL_PIXEL_FORMAT_RGBA_8888; } else if (depth == 24) { gl_format = GL_RGB; cogl_format = COGL_PIXEL_FORMAT_RGB_888; } else { g_critical ("Can't create a TFP cogl texture for pixmap with depth < 24"); return FALSE; } /* We want to use the GL_ARB_texture_rectangle extension on some chipsets because GL_ARB_texture_non_power_of_two is not always supported or might be buggy */ if (should_use_rectangle ()) { GLuint tex = 0; using_rectangle = TRUE; glGenTextures (1, &tex); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, tex); glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_format, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); handle = cogl_texture_new_from_foreign (tex, GL_TEXTURE_RECTANGLE_ARB, width, height, 0, 0, cogl_format | COGL_BGR_BIT | COGL_PREMULT_BIT); } else { handle = cogl_texture_new_with_size (width, height, COGL_TEXTURE_NO_SLICING, cogl_format | COGL_BGR_BIT | COGL_PREMULT_BIT); using_rectangle = FALSE; } if (handle) { clutter_glx_texture_pixmap_free_rectangle (texture_glx); priv->using_rectangle = using_rectangle; clutter_texture_set_cogl_texture (texture, handle); cogl_handle_unref(handle); return TRUE; } return FALSE; } static void clutter_glx_texture_pixmap_realize (ClutterActor *actor) { ClutterGLXTexturePixmap *texture; ClutterGLXTexturePixmapPrivate *priv; texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor); priv = texture->priv; clutter_glx_texture_pixmap_create_glx_pixmap (texture); if (priv->use_fallback) { CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> realize (actor); return; } CLUTTER_NOTE (TEXTURE, "texture pixmap realised"); } static void clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) { ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor); ClutterGLXTexturePixmapPrivate *priv = texture->priv; Display *dpy; dpy = clutter_x11_get_default_display(); clutter_glx_texture_pixmap_free_rectangle (texture); if (!_have_tex_from_pixmap_ext) { CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> unrealize (actor); return; } if (!CLUTTER_ACTOR_IS_REALIZED (actor)) return; if (priv->glx_pixmap && priv->bound) { clutter_x11_trap_x_errors (); (_gl_release_tex_image) (dpy, priv->glx_pixmap, GLX_FRONT_LEFT_EXT); XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); priv->bound = FALSE; } CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); } static GLXFBConfig * get_fbconfig_for_depth (ClutterGLXTexturePixmap *texture, guint depth) { GLXFBConfig *fbconfigs, *ret = NULL; int n_elements, i, found; Display *dpy; int db, stencil, alpha, mipmap, rgba, value; static GLXFBConfig *cached_config = NULL; static gboolean have_cached_config = FALSE; static int cached_mipmap = 0; if (have_cached_config) { texture->priv->can_mipmap = cached_mipmap; return cached_config; } dpy = clutter_x11_get_default_display (); fbconfigs = glXGetFBConfigs (dpy, clutter_x11_get_default_screen (), &n_elements); db = G_MAXSHORT; stencil = G_MAXSHORT; mipmap = 0; rgba = 0; found = n_elements; for (i = 0; i < n_elements; i++) { XVisualInfo *vi; int visual_depth; vi = glXGetVisualFromFBConfig (dpy, fbconfigs[i]); if (vi == NULL) continue; visual_depth = vi->depth; XFree (vi); if (visual_depth != depth) continue; glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_ALPHA_SIZE, &alpha); glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BUFFER_SIZE, &value); if (value != depth && (value - alpha) != depth) continue; value = 0; if (depth == 32) { glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); if (value) rgba = 1; } if (!value) { if (rgba) continue; glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &value); if (!value) continue; } glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_DOUBLEBUFFER, &value); if (value > db) continue; db = value; glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_STENCIL_SIZE, &value); if (value > stencil) continue; stencil = value; if (_gl_generate_mipmap) { glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &value); if (value < mipmap) continue; mipmap = value; } found = i; } if (found != n_elements) { ret = g_malloc (sizeof (GLXFBConfig)); *ret = fbconfigs[found]; } if (n_elements) XFree (fbconfigs); have_cached_config = TRUE; cached_config = ret; texture->priv->can_mipmap = cached_mipmap = mipmap; return ret; } static void clutter_glx_texture_pixmap_free_glx_pixmap (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv = texture->priv; Display *dpy; dpy = clutter_x11_get_default_display (); if (priv->glx_pixmap && priv->bound) { texture_bind (texture); clutter_x11_trap_x_errors (); (_gl_release_tex_image) (dpy, priv->glx_pixmap, GLX_FRONT_LEFT_EXT); XSync (clutter_x11_get_default_display(), FALSE); if (clutter_x11_untrap_x_errors ()) CLUTTER_NOTE (TEXTURE, "Failed to release?"); CLUTTER_NOTE (TEXTURE, "Destroyed pxm: %li", priv->glx_pixmap); priv->bound = FALSE; } clutter_x11_trap_x_errors (); if (priv->glx_pixmap) glXDestroyPixmap (dpy, priv->glx_pixmap); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); priv->glx_pixmap = None; } static void clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv = texture->priv; GLXPixmap glx_pixmap = None; int attribs[7], i = 0, mipmap = 0; GLXFBConfig *fbconfig; Display *dpy; guint depth; Pixmap pixmap = None; guint pixmap_width = 0, pixmap_height = 0; ClutterBackendGLX *backend_glx; ClutterTextureQuality quality; backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend ()); dpy = clutter_x11_get_default_display (); if (!_have_tex_from_pixmap_ext) goto cleanup; g_object_get (texture, "pixmap-width", &pixmap_width, "pixmap-height", &pixmap_height, "pixmap-depth", &depth, "pixmap", &pixmap, NULL); if (!pixmap) { goto cleanup; } CLUTTER_NOTE (TEXTURE, "Creating GLXPixmap"); fbconfig = get_fbconfig_for_depth (texture, depth); if (!fbconfig) { g_warning ("Could not find an FBConfig for selected pixmap"); goto cleanup; } attribs[i++] = GLX_TEXTURE_FORMAT_EXT; if (depth == 24) { attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; } else if (depth == 32) { attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; } else { g_warning ("Pixmap with depth below 24 are not supported"); goto cleanup; } quality = clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)); if (quality == CLUTTER_TEXTURE_QUALITY_HIGH && priv->can_mipmap) mipmap = priv->can_mipmap; attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; attribs[i++] = mipmap; attribs[i++] = GLX_TEXTURE_TARGET_EXT; attribs[i++] = (should_use_rectangle () ? GLX_TEXTURE_RECTANGLE_EXT : GLX_TEXTURE_2D_EXT); attribs[i++] = None; /* Note: some drivers (e.g. Nvidia) get upset if you effectivly create a glx * pixmap for the same server side object, even though you might have unique * client side names, we currently avoid hitting this problem by destroying * the current glx pixmap first */ if (priv->glx_pixmap) clutter_glx_texture_pixmap_free_glx_pixmap (texture); clutter_x11_trap_x_errors (); glx_pixmap = glXCreatePixmap (dpy, *fbconfig, pixmap, attribs); XSync (dpy, FALSE); if (clutter_x11_untrap_x_errors ()) { CLUTTER_NOTE (TEXTURE, "Failed to create GLXPixmap"); /* Make sure we don't think the call actually succeeded */ glx_pixmap = None; } if (!create_cogl_texture (CLUTTER_TEXTURE (texture), pixmap_width, pixmap_height)) { CLUTTER_NOTE (TEXTURE, "Unable to create texture for pixmap"); glXDestroyPixmap (dpy, glx_pixmap); glx_pixmap = None; goto cleanup; } cleanup: if (priv->glx_pixmap) clutter_glx_texture_pixmap_free_glx_pixmap (texture); if (glx_pixmap != None) { priv->use_fallback = FALSE; priv->glx_pixmap = glx_pixmap; CLUTTER_NOTE (TEXTURE, "Created GLXPixmap"); clutter_glx_texture_pixmap_update_area (CLUTTER_X11_TEXTURE_PIXMAP (texture), 0, 0, pixmap_width, pixmap_height); /* Get ready to queue initial mipmap generation */ if (_gl_generate_mipmap && priv->can_mipmap && clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)) == CLUTTER_TEXTURE_QUALITY_HIGH) { priv->mipmap_generate_queued++; } return; } else { if (pixmap) CLUTTER_NOTE (TEXTURE, "Falling back to X11 manual mechansim"); priv->use_fallback = TRUE; priv->glx_pixmap = None; } } static void clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height) { ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture); ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv; Display *dpy; CLUTTER_NOTE (TEXTURE, "Updating texture pixmap"); dpy = clutter_x11_get_default_display(); if (!CLUTTER_ACTOR_IS_REALIZED (texture)) return; if (priv->use_fallback) { CLUTTER_NOTE (TEXTURE, "Falling back to X11"); clutter_glx_texture_pixmap_free_rectangle (texture_glx); parent_class->update_area (texture, x, y, width, height); return; } if (priv->glx_pixmap == None) return; if (texture_bind (CLUTTER_GLX_TEXTURE_PIXMAP(texture))) { CLUTTER_NOTE (TEXTURE, "Really updating via GLX"); clutter_x11_trap_x_errors (); (_gl_bind_tex_image) (dpy, priv->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL); XSync (clutter_x11_get_default_display(), FALSE); /* Note above fires X error for non name pixmaps - but * things still seem to work - i.e pixmap updated */ if (clutter_x11_untrap_x_errors ()) CLUTTER_NOTE (TEXTURE, "Update bind_tex_image failed"); priv->bound = TRUE; if (_gl_generate_mipmap && priv->can_mipmap && clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)) == CLUTTER_TEXTURE_QUALITY_HIGH) { /* FIXME: It may make more sense to set a flag here and only * generate the mipmap on a pre paint.. compressing need * to call generate mipmap * May break clones however.. */ priv->mipmap_generate_queued++; } } else g_warning ("Failed to bind initial tex"); priv->bind_tex_image_queued = TRUE; } static void clutter_glx_texture_pixmap_class_init (ClutterGLXTexturePixmapClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterX11TexturePixmapClass *x11_texture_class = CLUTTER_X11_TEXTURE_PIXMAP_CLASS (klass); g_type_class_add_private (klass, sizeof (ClutterGLXTexturePixmapPrivate)); parent_class = g_type_class_peek_parent(klass); object_class->dispose = clutter_glx_texture_pixmap_dispose; object_class->notify = clutter_glx_texture_pixmap_notify; actor_class->realize = clutter_glx_texture_pixmap_realize; actor_class->unrealize = clutter_glx_texture_pixmap_unrealize; x11_texture_class->update_area = clutter_glx_texture_pixmap_update_area; } /** * clutter_glx_texture_pixmap_using_extension: * @texture: A #ClutterGLXTexturePixmap * * Checks whether @texture is using the GLX_EXT_texture_from_pixmap * extension; this extension can be optionally (though it is strongly * encouraged) implemented as a zero-copy between a GLX pixmap and * a GL texture. * * Return value: %TRUE if the texture is using the * GLX_EXT_texture_from_pixmap OpenGL extension or falling back to the * slower software mechanism. * * Since: 0.8 */ gboolean clutter_glx_texture_pixmap_using_extension (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv; priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; return (_have_tex_from_pixmap_ext && !priv->use_fallback); /* Assume NPOT TFP's are supported even if regular NPOT isn't advertised * but tfp is. Seemingly some Intel drivers do this ? */ /* && clutter_feature_available (COGL_FEATURE_TEXTURE_NPOT)); */ } /** * clutter_glx_texture_pixmap_new_with_pixmap: * @pixmap: the X Pixmap to which this texture should be bound * * Creates a new #ClutterGLXTexturePixmap for @pixmap * * Return value: A new #ClutterGLXTexturePixmap bound to the given X Pixmap * * Since: 0.8 */ ClutterActor * clutter_glx_texture_pixmap_new_with_pixmap (Pixmap pixmap) { return g_object_new (CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, "pixmap", pixmap, NULL); } /** * clutter_glx_texture_pixmap_new_with_window: * @window: the X window to which this texture should be bound * * Creates a new #ClutterGLXTexturePixmap for @window * * Return value: A new #ClutterGLXTexturePixmap bound to the given X window * * Since: 0.8 **/ ClutterActor * clutter_glx_texture_pixmap_new_with_window (Window window) { return g_object_new (CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, "window", window, NULL); } /** * clutter_glx_texture_pixmap_new: * * Creates a new, empty #ClutterGLXTexturePixmap * * Return value: A new #ClutterGLXTexturePixmap * * Since: 0.8 */ ClutterActor * clutter_glx_texture_pixmap_new (void) { return g_object_new (CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, NULL); }