[Glamor] [PATCH v3] Add DRI3 support to glamor

zhigang gong zhigang.gong at gmail.com
Fri Dec 6 08:00:39 PST 2013


LGTM, pushed, thanks for your contribution.


On Thu, Dec 5, 2013 at 3:49 PM, Axel Davy <axel.davy at ens.fr> wrote:

> This implements some DRI3 helpers to help the DDXs using
> glamor to support DRI3.
>
> Signed-off-by: Axel Davy <axel.davy at ens.fr>
> ---
> v3: fix whitespace errors of v2
>
>  configure.ac      |  13 ++++
>  src/glamor.c      |  76 +++++++++++++++++-
>  src/glamor.h      |  68 ++++++++++++++++-
>  src/glamor_egl.c  | 225
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/glamor_fbo.c  |  36 ++++++---
>  src/glamor_priv.h |   1 +
>  6 files changed, 405 insertions(+), 14 deletions(-)
>
> diff --git a/configure.ac b/configure.ac
> index b7b25d0..21bc794 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -134,6 +134,19 @@ if test "x$EGL = xyes"; then
>     fi
>  fi
>
> +AC_MSG_CHECKING([Enable Glamor Dri3 helpers])
> +AC_ARG_ENABLE(glamor-dri3, AS_HELP_STRING([--enable-glamor-dri3], [Build
> glamor Dri3 helpers (default: yes if gbm is detected)]),
> [GLAMOR_DRI3_HELPERS="$enableval"], [GLAMOR_DRI3_HELPERS=yes])
> +
> +if test "x$GLAMOR_DRI3_HELPERS" = xyes -a "x$GLAMOR_HAS_GBM" = xno; then
> +   GLAMOR_DRI3_HELPERS=no
> +fi
> +
> +AC_MSG_RESULT([$GLAMOR_DRI3_HELPERS])
> +
> +if test "x$GLAMOR_DRI3_HELPERS" = xyes; then
> +    AC_DEFINE(GLAMOR_HAS_DRI3_SUPPORT, 1, [Enable Dri3 helpers])
> +fi
> +
>  dnl
>  dnl TLS detection
>  dnl
> diff --git a/src/glamor.c b/src/glamor.c
> index e8e68be..93d3c5e 100644
> --- a/src/glamor.c
> +++ b/src/glamor.c
> @@ -209,7 +209,12 @@ glamor_destroy_textured_pixmap(PixmapPtr pixmap)
>  Bool
>  glamor_destroy_pixmap(PixmapPtr pixmap)
>  {
> -       glamor_destroy_textured_pixmap(pixmap);
> +       glamor_screen_private
> +         *glamor_priv =
> glamor_get_screen_private(pixmap->drawable.pScreen);
> +       if (glamor_priv->dri3_enabled)
> +               glamor_egl_destroy_textured_pixmap(pixmap);
> +       else
> +               glamor_destroy_textured_pixmap(pixmap);
>         return fbDestroyPixmap(pixmap);
>  }
>
> @@ -552,3 +557,72 @@ glamor_fini(ScreenPtr screen)
>  {
>         /* Do nothing currently. */
>  }
> +
> +void glamor_enable_dri3(ScreenPtr screen)
> +{
> +       glamor_screen_private *glamor_priv =
> +           glamor_get_screen_private(screen);
> +       glamor_priv->dri3_enabled = TRUE;
> +}
> +
> +Bool glamor_is_dri3_support_enabled(ScreenPtr screen)
> +{
> +       glamor_screen_private *glamor_priv =
> +           glamor_get_screen_private(screen);
> +       return glamor_priv->dri3_enabled;
> +}
> +
> +int
> +glamor_dri3_fd_from_pixmap (ScreenPtr screen,
> +                            PixmapPtr pixmap,
> +                            CARD16 *stride,
> +                            CARD32 *size)
> +{
> +       glamor_pixmap_private *pixmap_priv;
> +       glamor_screen_private *glamor_priv =
> +           glamor_get_screen_private(pixmap->drawable.pScreen);
> +
> +       pixmap_priv = glamor_get_pixmap_private(pixmap);
> +       if (pixmap_priv == NULL || !glamor_priv->dri3_enabled)
> +               return -1;
> +       switch (pixmap_priv->type)
> +       {
> +               case GLAMOR_TEXTURE_DRM:
> +               case GLAMOR_TEXTURE_ONLY:
> +                       glamor_pixmap_ensure_fbo(pixmap, GL_RGBA, 0);
> +                       return glamor_egl_dri3_fd_name_from_tex(screen,
> +                                                               pixmap,
> +
> pixmap_priv->base.fbo->tex,
> +                                                               FALSE,
> +                                                               stride,
> +                                                               size);
> +               default: break;
> +       }
> +       return -1;
> +}
> +
> +int
> +glamor_dri3_name_from_pixmap (PixmapPtr pixmap)
> +{
> +       glamor_pixmap_private *pixmap_priv;
> +       glamor_screen_private *glamor_priv =
> +           glamor_get_screen_private(pixmap->drawable.pScreen);
> +
> +       pixmap_priv = glamor_get_pixmap_private(pixmap);
> +       if (pixmap_priv == NULL || !glamor_priv->dri3_enabled)
> +               return -1;
> +       switch (pixmap_priv->type)
> +       {
> +               case GLAMOR_TEXTURE_DRM:
> +               case GLAMOR_TEXTURE_ONLY:
> +                       glamor_pixmap_ensure_fbo(pixmap, GL_RGBA, 0);
> +                       return
> glamor_egl_dri3_fd_name_from_tex(pixmap->drawable.pScreen,
> +                                                               pixmap,
> +
> pixmap_priv->base.fbo->tex,
> +                                                               TRUE,
> +                                                               NULL,
> +                                                               NULL);
> +               default: break;
> +       }
> +       return -1;
> +}
> diff --git a/src/glamor.h b/src/glamor.h
> index 927892f..1bb48ed 100644
> --- a/src/glamor.h
> +++ b/src/glamor.h
> @@ -164,6 +164,71 @@ extern _X_EXPORT void
> glamor_egl_exchange_buffers(PixmapPtr front, PixmapPtr bac
>
>  extern _X_EXPORT void glamor_pixmap_exchange_fbos(PixmapPtr front,
> PixmapPtr back);
>
> +/* The DDX is not supposed to call these three functions */
> +extern _X_EXPORT void glamor_enable_dri3(ScreenPtr screen);
> +extern _X_EXPORT unsigned int
> glamor_egl_create_argb8888_based_texture(ScreenPtr screen, int w, int h);
> +extern _X_EXPORT int glamor_egl_dri3_fd_name_from_tex(ScreenPtr,
> PixmapPtr, unsigned int, Bool, CARD16*, CARD32*);
> +
> +/* @glamor_is_dri3_support_enabled: Returns if DRI3 support is enabled.
> + *
> + * @screen: Current screen pointer.
> + *
> + * To have DRI3 support enabled, glamor and glamor_egl need to be
> initialized,
> + * and glamor_egl_init_textured_pixmap need to be called. glamor also
> + * has to be compiled with gbm support.
> + * The EGL layer need to have the following extensions working:
> + * .EGL_KHR_gl_texture_2D_image
> + * .EGL_EXT_image_dma_buf_import
> + * If DRI3 support is not enabled, the following helpers will return an
> error.
> + * */
> +extern _X_EXPORT Bool glamor_is_dri3_support_enabled(ScreenPtr screen);
> +
> +/* @glamor_dri3_fd_from_pixmap: DRI3 helper to get a dma-buf fd from a
> pixmap.
> + *
> + * @screen: Current screen pointer.
> + * @pixmap: The pixmap from which we want the fd.
> + * @stride, @size: Pointers to fill the stride and size of the
> + *                buffer associated to the fd.
> + *
> + * the pixmap and the buffer associated by the fd will share the same
> + * content.
> + * Returns the fd on success, -1 on error.
> + * */
> +extern _X_EXPORT int glamor_dri3_fd_from_pixmap (ScreenPtr screen,
> +                                                PixmapPtr pixmap,
> +                                                CARD16 *stride,
> +                                                CARD32 *size);
> +
> +/* @glamor_dri3_name_from_pixmap: helper to get an gem name from a pixmap.
> + *
> + * @pixmap: The pixmap from which we want the gem name.
> + *
> + * the pixmap and the buffer associated by the gem name will share the
> same
> + * content. This function can be used by the DDX to support DRI2, but
> needs
> + * glamor DRI3 support to be activated.
> + * Returns the name on success, -1 on error.
> + * */
> +extern _X_EXPORT int glamor_dri3_name_from_pixmap (PixmapPtr pixmap);
> +
> +/* @glamor_egl_dri3_pixmap_from_fd: DRI3 helper to get a pixmap from a
> dma-buf fd.
> + *
> + * @screen: Current screen pointer.
> + * @fd: The dma-buf fd to import.
> + * @width: The width of the buffer.
> + * @height: The height of the buffer.
> + * @stride: The stride of the buffer.
> + * @depth: The depth of the buffer.
> + * @bpp: The number of bpp of the buffer.
> + *
> + * Returns a valid pixmap if the import succeeded, else NULL.
> + * */
> +extern _X_EXPORT PixmapPtr glamor_egl_dri3_pixmap_from_fd (ScreenPtr
> screen,
> +                                                          int fd,
> +                                                          CARD16 width,
> +                                                          CARD16 height,
> +                                                          CARD16 stride,
> +                                                          CARD8 depth,
> +                                                          CARD8 bpp);
>
>  #ifdef GLAMOR_FOR_XORG
>
> @@ -243,9 +308,10 @@ extern _X_EXPORT Bool
>         glamor_egl_create_textured_pixmap_from_gbm_bo(PixmapPtr pixmap,
>                                                       void *bo);
>
> -extern _X_EXPORT void glamor_egl_destroy_textured_pixmap(PixmapPtr
> pixmap);
>  #endif
>
> +extern _X_EXPORT void glamor_egl_destroy_textured_pixmap(PixmapPtr
> pixmap);
> +
>  extern _X_EXPORT int glamor_create_gc(GCPtr gc);
>
>  extern _X_EXPORT void glamor_validate_gc(GCPtr gc, unsigned long changes,
> DrawablePtr drawable);
> diff --git a/src/glamor_egl.c b/src/glamor_egl.c
> index 13b7f44..bf0db3a 100644
> --- a/src/glamor_egl.c
> +++ b/src/glamor_egl.c
> @@ -45,6 +45,7 @@
>
>  #ifdef GLAMOR_HAS_GBM
>  #include <gbm.h>
> +#include <drm_fourcc.h>
>  #endif
>
>  #if GLAMOR_GLES2
> @@ -95,6 +96,7 @@ struct glamor_egl_screen_private {
>         void *glamor_context;
>         void *current_context;
>         int gl_context_depth;
> +       int dri3_capable;
>
>         PFNEGLCREATEIMAGEKHRPROC egl_create_image_khr;
>         PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image_khr;
> @@ -218,6 +220,47 @@ glamor_create_texture_from_image(struct
> glamor_egl_screen_private
>         return TRUE;
>  }
>
> +unsigned int
> +glamor_egl_create_argb8888_based_texture(ScreenPtr screen,
> +                                        int w,
> +                                        int h)
> +{
> +       ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
> +       struct glamor_egl_screen_private *glamor_egl;
> +       EGLImageKHR image;
> +       GLuint texture;
> +#ifdef GLAMOR_HAS_DRI3_SUPPORT
> +       struct gbm_bo *bo;
> +       EGLNativePixmapType native_pixmap;
> +       glamor_egl = glamor_egl_get_screen_private(scrn);
> +       bo = gbm_bo_create (glamor_egl->gbm, w, h, GBM_FORMAT_ARGB8888,
> +                                     GBM_BO_USE_RENDERING |
> +                                     GBM_BO_USE_SCANOUT);
> +       if (!bo)
> +               return 0;
> +
> +       /* If the following assignment raises an error or a warning
> +        * then that means EGLNativePixmapType is not struct gbm_bo *
> +        * on your platform: This code won't work and you should not
> +        * compile with dri3 support enabled */
> +       native_pixmap = bo;
> +
> +       image = glamor_egl->egl_create_image_khr(glamor_egl->display,
> +                                                EGL_NO_CONTEXT,
> +                                                EGL_NATIVE_PIXMAP_KHR,
> +                                                native_pixmap, NULL);
> +       gbm_bo_destroy(bo);
> +       if (image == EGL_NO_IMAGE_KHR)
> +               return 0;
> +       glamor_create_texture_from_image(glamor_egl, image, &texture);
> +       glamor_egl->egl_destroy_image_khr(glamor_egl->display, image);
> +
> +       return texture;
> +#else
> +       return 0; /* this path should never happen */
> +#endif
> +}
> +
>  Bool
>  glamor_egl_create_textured_screen(ScreenPtr screen, int handle, int
> stride)
>  {
> @@ -349,6 +392,178 @@ done:
>         return ret;
>  }
>
> +#ifdef GLAMOR_HAS_DRI3_SUPPORT
> +int glamor_get_fd_from_bo (int gbm_fd, struct gbm_bo *bo, int *fd);
> +void glamor_get_name_from_bo (int gbm_fd, struct gbm_bo *bo, int *name);
> +int
> +glamor_get_fd_from_bo (int gbm_fd, struct gbm_bo *bo, int *fd)
> +{
> +       union gbm_bo_handle handle;
> +       struct drm_prime_handle args;
> +
> +       handle = gbm_bo_get_handle(bo);
> +       args.handle = handle.u32;
> +       args.flags = DRM_CLOEXEC;
> +       if (ioctl (gbm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
> +               return FALSE;
> +       *fd = args.fd;
> +       return TRUE;
> +}
> +
> +void
> +glamor_get_name_from_bo (int gbm_fd, struct gbm_bo *bo, int *name)
> +{
> +       union gbm_bo_handle handle;
> +
> +       handle = gbm_bo_get_handle(bo);
> +       if (!glamor_get_flink_name(gbm_fd, handle.u32, name))
> +               *name = -1;
> +}
> +#endif
> +
> +int glamor_egl_dri3_fd_name_from_tex (ScreenPtr screen,
> +                                     PixmapPtr pixmap,
> +                                     unsigned int tex,
> +                                     Bool want_name,
> +                                     CARD16 *stride,
> +                                     CARD32 *size)
> +{
> +#ifdef GLAMOR_HAS_DRI3_SUPPORT
> +       ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
> +       struct glamor_egl_screen_private *glamor_egl;
> +       EGLImageKHR image;
> +       struct gbm_bo* bo;
> +       int fd = -1;
> +
> +       EGLint attribs[] = {
> +               EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
> +               EGL_GL_TEXTURE_LEVEL_KHR, 0,
> +               EGL_NONE
> +       };
> +
> +       glamor_egl = glamor_egl_get_screen_private(scrn);
> +
> +       glamor_egl_make_current(screen);
> +
> +       image = dixLookupPrivate(&pixmap->devPrivates,
> +                                glamor_egl_pixmap_private_key);
> +
> +       if (image == EGL_NO_IMAGE_KHR || image == NULL)
> +       {
> +               image =
> glamor_egl->egl_create_image_khr(glamor_egl->display,
> +
>  glamor_egl->context,
> +
>  EGL_GL_TEXTURE_2D_KHR,
> +                                                        tex, attribs);
> +               if (image == EGL_NO_IMAGE_KHR)
> +                       goto failure;
> +
> +               dixSetPrivate(&pixmap->devPrivates,
> +                             glamor_egl_pixmap_private_key,
> +                             image);
> +               glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
> +       }
> +
> +       bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_EGL_IMAGE,
> image, 0);
> +       if (!bo)
> +               goto failure;
> +
> +       pixmap->devKind = gbm_bo_get_stride(bo);
> +
> +       if (want_name)
> +       {
> +               if (glamor_egl->has_gem)
> +                       glamor_get_name_from_bo(glamor_egl->fd, bo, &fd);
> +       }
> +       else
> +       {
> +               if (glamor_get_fd_from_bo(glamor_egl->fd, bo, &fd))
> +               {
> +                       *stride = pixmap->devKind;
> +                       *size = pixmap->devKind * gbm_bo_get_height(bo);
> +               }
> +       }
> +
> +       gbm_bo_destroy(bo);
> +failure:
> +       glamor_egl_restore_context(screen);
> +       return fd;
> +#else
> +       return -1;
> +#endif
> +}
> +
> +PixmapPtr glamor_egl_dri3_pixmap_from_fd (ScreenPtr screen,
> +                                         int fd,
> +                                         CARD16 width,
> +                                         CARD16 height,
> +                                         CARD16 stride,
> +                                         CARD8 depth,
> +                                         CARD8 bpp)
> +{
> +#ifdef GLAMOR_HAS_DRI3_SUPPORT
> +       ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
> +       struct glamor_egl_screen_private *glamor_egl;
> +       struct gbm_bo* bo;
> +       EGLImageKHR image;
> +       PixmapPtr pixmap;
> +       Bool ret = FALSE;
> +       EGLint attribs[] = {
> +               EGL_WIDTH, 0,
> +               EGL_HEIGHT, 0,
> +               EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888,
> +               EGL_DMA_BUF_PLANE0_FD_EXT, 0,
> +               EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
> +               EGL_DMA_BUF_PLANE0_PITCH_EXT, 0,
> +               EGL_NONE
> +       };
> +
> +       glamor_egl = glamor_egl_get_screen_private(scrn);
> +
> +       if (!glamor_egl->dri3_capable)
> +               return NULL;
> +
> +       if (bpp != 32 || !(depth == 24 || depth == 32) || width == 0 ||
> height == 0)
> +               return NULL;
> +
> +       attribs[1] = width;
> +       attribs[3] = height;
> +       attribs[7] = fd;
> +       attribs[11] = stride;
> +       image = glamor_egl->egl_create_image_khr(glamor_egl->display,
> +                                                EGL_NO_CONTEXT,
> +                                                EGL_LINUX_DMA_BUF_EXT,
> +                                                NULL, attribs);
> +
> +       if (image == EGL_NO_IMAGE_KHR)
> +               return NULL;
> +
> +       /* EGL_EXT_image_dma_buf_import can impose restrictions on the
> +        * usage of the image. Use gbm_bo to bypass the limitations. */
> +
> +       bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_EGL_IMAGE,
> image, 0);
> +       glamor_egl->egl_destroy_image_khr(glamor_egl->display, image);
> +
> +       if (!bo)
> +               return NULL;
> +
> +       pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0);
> +       screen->ModifyPixmapHeader (pixmap, width, height, 0, 0, stride,
> NULL);
> +
> +       ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo);
> +       gbm_bo_destroy(bo);
> +
> +       if (ret)
> +               return pixmap;
> +       else
> +       {
> +               screen->DestroyPixmap(pixmap);
> +               return NULL;
> +       }
> +#else
> +       return NULL;
> +#endif
> +}
> +
>  static void
>  _glamor_egl_destroy_pixmap_image(PixmapPtr pixmap)
>  {
> @@ -558,6 +773,11 @@ glamor_egl_init(ScrnInfoPtr scrn, int fd)
>         GLAMOR_CHECK_EGL_EXTENSIONS(KHR_surfaceless_context,
> KHR_surfaceless_opengl);
>  #endif
>
> +#ifdef GLAMOR_HAS_DRI3_SUPPORT
> +       if (glamor_egl_has_extension(glamor_egl,
> "EGL_KHR_gl_texture_2D_image") &&
> +           glamor_egl_has_extension(glamor_egl,
> "EGL_EXT_image_dma_buf_import") )
> +           glamor_egl->dri3_capable = TRUE;
> +#endif
>         glamor_egl->egl_create_image_khr = (PFNEGLCREATEIMAGEKHRPROC)
>             eglGetProcAddress("eglCreateImageKHR");
>
> @@ -609,6 +829,9 @@ glamor_egl_init(ScrnInfoPtr scrn, int fd)
>  Bool
>  glamor_egl_init_textured_pixmap(ScreenPtr screen)
>  {
> +       ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
> +       struct glamor_egl_screen_private *glamor_egl =
> +           glamor_egl_get_screen_private(scrn);
>         if (!dixRegisterPrivateKey
>             (glamor_egl_pixmap_private_key, PRIVATE_PIXMAP, 0)) {
>                 LogMessage(X_WARNING,
> @@ -616,6 +839,8 @@ glamor_egl_init_textured_pixmap(ScreenPtr screen)
>                            screen->myNum);
>                 return FALSE;
>         }
> +       if (glamor_egl->dri3_capable)
> +               glamor_enable_dri3(screen);
>         return TRUE;
>  }
>
> diff --git a/src/glamor_fbo.c b/src/glamor_fbo.c
> index 4838a27..d1b087e 100644
> --- a/src/glamor_fbo.c
> +++ b/src/glamor_fbo.c
> @@ -328,18 +328,30 @@ _glamor_create_tex(glamor_screen_private
> *glamor_priv,
>                    int w, int h, GLenum format)
>  {
>         glamor_gl_dispatch *dispatch;
> -       unsigned int tex;
> -
> -       dispatch = glamor_get_dispatch(glamor_priv);
> -       dispatch->glGenTextures(1, &tex);
> -       dispatch->glBindTexture(GL_TEXTURE_2D, tex);
> -       dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
> -                                 GL_NEAREST);
> -       dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
> -                                 GL_NEAREST);
> -       dispatch->glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format,
> -                              GL_UNSIGNED_BYTE, NULL);
> -       glamor_put_dispatch(glamor_priv);
> +       unsigned int tex = 0;
> +
> +       /* With dri3, we want to allocate ARGB8888 pixmaps only.
> +        * Depending on the implementation, GL_RGBA might not
> +        * give us ARGB8888. We ask glamor_egl to use get
> +        * an ARGB8888 based texture for us. */
> +       if (glamor_priv->dri3_enabled && format == GL_RGBA)
> +       {
> +               tex =
> glamor_egl_create_argb8888_based_texture(glamor_priv->screen,
> +                                                               w, h);
> +       }
> +       if (!tex)
> +       {
> +               dispatch = glamor_get_dispatch(glamor_priv);
> +               dispatch->glGenTextures(1, &tex);
> +               dispatch->glBindTexture(GL_TEXTURE_2D, tex);
> +               dispatch->glTexParameteri(GL_TEXTURE_2D,
> GL_TEXTURE_MIN_FILTER,
> +                                         GL_NEAREST);
> +               dispatch->glTexParameteri(GL_TEXTURE_2D,
> GL_TEXTURE_MAG_FILTER,
> +                                         GL_NEAREST);
> +               dispatch->glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0,
> +                                      format, GL_UNSIGNED_BYTE, NULL);
> +               glamor_put_dispatch(glamor_priv);
> +       }
>         return tex;
>  }
>
> diff --git a/src/glamor_priv.h b/src/glamor_priv.h
> index b6a1075..7b8f762 100644
> --- a/src/glamor_priv.h
> +++ b/src/glamor_priv.h
> @@ -305,6 +305,7 @@ typedef struct glamor_screen_private {
>         int state;
>         unsigned int render_idle_cnt;
>         ScreenPtr screen;
> +       int dri3_enabled;
>
>         /* xv */
>         GLint xv_prog;
> --
> 1.8.1.2
>
> _______________________________________________
> Glamor mailing list
> Glamor at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/glamor
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/glamor/attachments/20131207/7ab6b6ed/attachment-0001.html>


More information about the Glamor mailing list