Logo Search packages:      
Sourcecode: xorg-server version File versions  Download package

dmxpict.c

Go to the documentation of this file.
/* $XFree86$ */
/*
 * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
 *
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation on the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Authors:
 *   Kevin E. Martin <kem@redhat.com>
 *
 */

/** \file
 *  Provide support for the RENDER extension (version 0.8).
 */

#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif

#include "dmx.h"
#include "dmxsync.h"
#include "dmxpict.h"
#include "dmxwindow.h"
#include "dmxpixmap.h"

#include "fb.h"
#include "pixmapstr.h"
#include "dixstruct.h"

#include <X11/extensions/render.h>
#include <X11/extensions/renderproto.h>
#include "picture.h"
#include "picturestr.h"
#include "mipict.h"
#include "fbpict.h"


extern int RenderErrBase;
extern int (*ProcRenderVector[RenderNumberRequests])(ClientPtr);

static int (*dmxSaveRenderVector[RenderNumberRequests])(ClientPtr);


static int dmxProcRenderCreateGlyphSet(ClientPtr client);
static int dmxProcRenderFreeGlyphSet(ClientPtr client);
static int dmxProcRenderAddGlyphs(ClientPtr client);
static int dmxProcRenderFreeGlyphs(ClientPtr client);
static int dmxProcRenderCompositeGlyphs(ClientPtr client);
static int dmxProcRenderSetPictureTransform(ClientPtr client);
static int dmxProcRenderSetPictureFilter(ClientPtr client);
#if 0
/* FIXME: Not (yet) supported */
static int dmxProcRenderCreateCursor(ClientPtr client);
static int dmxProcRenderCreateAnimCursor(ClientPtr client);
#endif

/** Catch errors that might occur when allocating Glyph Sets.  Errors
 *  are saved in dmxGlyphLastError for later handling. */
00082 static int dmxGlyphLastError;
static int dmxGlyphErrorHandler(Display *dpy, XErrorEvent *ev)
{
    dmxGlyphLastError = ev->error_code;
    return 0;
}


/** Initialize the Proc Vector for the RENDER extension.  The functions
 *  here cannot be handled by the mi layer RENDER hooks either because
 *  the required information is no longer available when it reaches the
 *  mi layer or no mi layer hooks exist.  This function is called from
 *  InitOutput() since it should be initialized only once per server
 *  generation. */
00096 void dmxInitRender(void)
{
    int i;

    for (i = 0; i < RenderNumberRequests; i++)
        dmxSaveRenderVector[i] = ProcRenderVector[i];

    ProcRenderVector[X_RenderCreateGlyphSet]
      = dmxProcRenderCreateGlyphSet;
    ProcRenderVector[X_RenderFreeGlyphSet]
      = dmxProcRenderFreeGlyphSet;
    ProcRenderVector[X_RenderAddGlyphs]
      = dmxProcRenderAddGlyphs;
    ProcRenderVector[X_RenderFreeGlyphs]
      = dmxProcRenderFreeGlyphs;
    ProcRenderVector[X_RenderCompositeGlyphs8]
      = dmxProcRenderCompositeGlyphs;
    ProcRenderVector[X_RenderCompositeGlyphs16]
      = dmxProcRenderCompositeGlyphs;
    ProcRenderVector[X_RenderCompositeGlyphs32]
      = dmxProcRenderCompositeGlyphs;
    ProcRenderVector[X_RenderSetPictureTransform]
      = dmxProcRenderSetPictureTransform;
    ProcRenderVector[X_RenderSetPictureFilter]
      = dmxProcRenderSetPictureFilter;
}

/** Reset the Proc Vector for the RENDER extension back to the original
 *  functions.  This function is called from dmxCloseScreen() during the
 *  server reset (only for screen #0). */
00126 void dmxResetRender(void)
{
    int i;

    for (i = 0; i < RenderNumberRequests; i++)
        ProcRenderVector[i] = dmxSaveRenderVector[i];
}

/** Initialize the RENDER extension, allocate the picture privates and
 *  wrap mi function hooks.  If the shadow frame buffer is used, then
 *  call the appropriate fb initialization function. */
00137 Bool dmxPictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats)
{
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps;

    /* The shadow framebuffer only relies on FB to be initialized */
    if (dmxShadowFB) return fbPictureInit(pScreen, formats, nformats);

    if (!miPictureInit(pScreen, formats, nformats))
      return FALSE;

    if (!AllocatePicturePrivate(pScreen, dmxPictPrivateIndex,
                        sizeof(dmxPictPrivRec)))
      return FALSE;

    ps = GetPictureScreen(pScreen);

    DMX_WRAP(CreatePicture,      dmxCreatePicture,      dmxScreen, ps);
    DMX_WRAP(DestroyPicture,     dmxDestroyPicture,     dmxScreen, ps);

    DMX_WRAP(ChangePictureClip,  dmxChangePictureClip,  dmxScreen, ps);
    DMX_WRAP(DestroyPictureClip, dmxDestroyPictureClip, dmxScreen, ps);

    DMX_WRAP(ChangePicture,      dmxChangePicture,      dmxScreen, ps);
    DMX_WRAP(ValidatePicture,    dmxValidatePicture,    dmxScreen, ps);

    DMX_WRAP(Composite,          dmxComposite,          dmxScreen, ps);
    DMX_WRAP(Glyphs,             dmxGlyphs,             dmxScreen, ps);
    DMX_WRAP(CompositeRects,     dmxCompositeRects,     dmxScreen, ps);

    DMX_WRAP(Trapezoids,         dmxTrapezoids,         dmxScreen, ps);
    DMX_WRAP(Triangles,          dmxTriangles,          dmxScreen, ps);
    DMX_WRAP(TriStrip,           dmxTriStrip,           dmxScreen, ps);
    DMX_WRAP(TriFan,             dmxTriFan,             dmxScreen, ps);

    return TRUE;
}


/** Find the appropriate format on the requested screen given the
 *  internal format requested.  The list of formats is searched
 *  sequentially as the XRenderFindFormat() function does not always
 *  find the appropriate format when a specific format is requested. */
00180 static XRenderPictFormat *dmxFindFormat(DMXScreenInfo *dmxScreen,
                              PictFormatPtr pFmt)
{
    XRenderPictFormat *pFormat = NULL;
    int                i       = 0;

    if (!pFmt || !dmxScreen->beDisplay) return pFormat;

    while (1) {
      pFormat = XRenderFindFormat(dmxScreen->beDisplay, 0, 0, i++);
      if (!pFormat) break;

      if (pFormat->type             != pFmt->type)             continue;
      if (pFormat->depth            != pFmt->depth)            continue;
      if (pFormat->direct.red       != pFmt->direct.red)       continue;
      if (pFormat->direct.redMask   != pFmt->direct.redMask)   continue;
      if (pFormat->direct.green     != pFmt->direct.green)     continue;
      if (pFormat->direct.greenMask != pFmt->direct.greenMask) continue;
      if (pFormat->direct.blue      != pFmt->direct.blue)      continue;
      if (pFormat->direct.blueMask  != pFmt->direct.blueMask)  continue;
      if (pFormat->direct.alpha     != pFmt->direct.alpha)     continue;
      if (pFormat->direct.alphaMask != pFmt->direct.alphaMask) continue;

      /* We have a match! */
      break;
    }

    return pFormat;
}

/** Free \a glyphSet on back-end screen number \a idx. */
00211 Bool dmxBEFreeGlyphSet(ScreenPtr pScreen, GlyphSetPtr glyphSet)
{
    dmxGlyphPrivPtr  glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet);
    int              idx       = pScreen->myNum;
    DMXScreenInfo   *dmxScreen = &dmxScreens[idx];

    if (glyphPriv->glyphSets[idx]) {
      XRenderFreeGlyphSet(dmxScreen->beDisplay, glyphPriv->glyphSets[idx]);
      glyphPriv->glyphSets[idx] = (GlyphSet)0;
      return TRUE;
    }

    return FALSE;
}

/** Create a Glyph Set on each screen.  Save the glyphset ID from each
 *  screen in the Glyph Set's private structure.  Fail if the format
 *  requested is not available or if the Glyph Set cannot be created on
 *  the screen. */
00230 static int dmxProcRenderCreateGlyphSet(ClientPtr client)
{
    int  ret;
    REQUEST(xRenderCreateGlyphSetReq);

    ret = dmxSaveRenderVector[stuff->renderReqType](client);

    if (ret == Success) {
      int              (*oldErrorHandler)(Display *, XErrorEvent *);
      GlyphSetPtr        glyphSet;
      dmxGlyphPrivPtr    glyphPriv;
      int                i;
      PictFormatPtr      pFmt;
      XRenderPictFormat *pFormat;

      /* Look up glyphSet that was just created ???? */
      /* Store glyphsets from backends in glyphSet->devPrivate ????? */
      /* Make sure we handle all errors here!! */
      
      glyphSet = SecurityLookupIDByType(client, stuff->gsid, GlyphSetType,
                                SecurityDestroyAccess);
      glyphPriv = xalloc(sizeof(dmxGlyphPrivRec));
      if (!glyphPriv) return BadAlloc;
        glyphPriv->glyphSets = NULL;
        MAXSCREENSALLOC_RETURN(glyphPriv->glyphSets, BadAlloc);
      DMX_SET_GLYPH_PRIV(glyphSet, glyphPriv);

      pFmt = SecurityLookupIDByType(client, stuff->format, PictFormatType,
                              SecurityReadAccess);

      oldErrorHandler = XSetErrorHandler(dmxGlyphErrorHandler);

      for (i = 0; i < dmxNumScreens; i++) {
          DMXScreenInfo *dmxScreen = &dmxScreens[i];

          if (!dmxScreen->beDisplay) {
            glyphPriv->glyphSets[i] = 0;
            continue;
          }

          pFormat = dmxFindFormat(dmxScreen, pFmt);
          if (!pFormat) {
            int  j;

            /* Free the glyph sets we've allocated thus far */
            for (j = 0; j < i; j++)
                dmxBEFreeGlyphSet(screenInfo.screens[j], glyphSet);

            /* Free the resource created by render */
            FreeResource(stuff->gsid, RT_NONE);

            ret = BadMatch;
            break;
          }

          /* Catch when this fails */
          glyphPriv->glyphSets[i]
            = XRenderCreateGlyphSet(dmxScreen->beDisplay, pFormat);

          if (dmxGlyphLastError) {
            int  j;

            /* Free the glyph sets we've allocated thus far */
            for (j = 0; j < i; j++)
                dmxBEFreeGlyphSet(screenInfo.screens[j], glyphSet);

            /* Free the resource created by render */
            FreeResource(stuff->gsid, RT_NONE);

            ret = dmxGlyphLastError;
            break;
          }
      }

      XSetErrorHandler(oldErrorHandler);
    }

    return ret;
}

/** Free the previously allocated Glyph Sets for each screen. */
00311 static int dmxProcRenderFreeGlyphSet(ClientPtr client)
{
    GlyphSetPtr  glyphSet;
    REQUEST(xRenderFreeGlyphSetReq);

    REQUEST_SIZE_MATCH(xRenderFreeGlyphSetReq);
    glyphSet = SecurityLookupIDByType(client, stuff->glyphset, GlyphSetType,
                              SecurityDestroyAccess);

    if (glyphSet && glyphSet->refcnt == 1) {
      dmxGlyphPrivPtr  glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet);
      int              i;

      for (i = 0; i < dmxNumScreens; i++) {
          DMXScreenInfo *dmxScreen = &dmxScreens[i];
          
          if (dmxScreen->beDisplay) {
            if (dmxBEFreeGlyphSet(screenInfo.screens[i], glyphSet))
                dmxSync(dmxScreen, FALSE);
          }
      }

        MAXSCREENSFREE(glyphPriv->glyphSets);
      xfree(glyphPriv);
      DMX_SET_GLYPH_PRIV(glyphSet, NULL);
    }

    return dmxSaveRenderVector[stuff->renderReqType](client);
}

/** Add glyphs to the Glyph Set on each screen. */
00342 static int dmxProcRenderAddGlyphs(ClientPtr client)
{
    int  ret;
    REQUEST(xRenderAddGlyphsReq);

    ret = dmxSaveRenderVector[stuff->renderReqType](client);

    if (ret == Success) {
      GlyphSetPtr      glyphSet;
      dmxGlyphPrivPtr  glyphPriv;
      int              i;
      int              nglyphs;
      CARD32          *gids;
      Glyph           *gidsCopy;
      xGlyphInfo      *gi;
      CARD8           *bits;
      int              nbytes;

      glyphSet = SecurityLookupIDByType(client, stuff->glyphset,
                                GlyphSetType, SecurityReadAccess);
      glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet);

      nglyphs = stuff->nglyphs;
      gids = (CARD32 *)(stuff + 1);
      gi = (xGlyphInfo *)(gids + nglyphs);
      bits = (CARD8 *)(gi + nglyphs);
      nbytes = ((stuff->length << 2) -
              sizeof(xRenderAddGlyphsReq) -
              (sizeof(CARD32) + sizeof(xGlyphInfo)) * nglyphs);

        gidsCopy = xalloc(sizeof(*gidsCopy) * nglyphs);
        for (i = 0; i < nglyphs; i++) gidsCopy[i] = gids[i];

      /* FIXME: Will this ever fail? */
      for (i = 0; i < dmxNumScreens; i++) {
          DMXScreenInfo *dmxScreen = &dmxScreens[i];

          if (dmxScreen->beDisplay) {
            XRenderAddGlyphs(dmxScreen->beDisplay,
                         glyphPriv->glyphSets[i],
                         gidsCopy,
                         (XGlyphInfo *)gi,
                         nglyphs,
                         (char *)bits,
                         nbytes);
            dmxSync(dmxScreen, FALSE);
          }
      }
        xfree(gidsCopy);
    }

    return ret;
}

/** Free glyphs from the Glyph Set for each screen. */
00397 static int dmxProcRenderFreeGlyphs(ClientPtr client)
{
    GlyphSetPtr  glyphSet;
    REQUEST(xRenderFreeGlyphsReq);

    REQUEST_AT_LEAST_SIZE(xRenderFreeGlyphsReq);
    glyphSet = SecurityLookupIDByType(client, stuff->glyphset, GlyphSetType,
                              SecurityWriteAccess);

    if (glyphSet) {
      dmxGlyphPrivPtr  glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet);
      int              i;
      int              nglyphs;
      Glyph           *gids;

      nglyphs = ((client->req_len << 2) - sizeof(xRenderFreeGlyphsReq)) >> 2;
      if (nglyphs) {
            gids    = xalloc(sizeof(*gids) * nglyphs);
            for (i = 0; i < nglyphs; i++)
                gids[i] = ((CARD32 *)(stuff + 1))[i];
            
          for (i = 0; i < dmxNumScreens; i++) {
            DMXScreenInfo *dmxScreen = &dmxScreens[i];

            if (dmxScreen->beDisplay) {
                XRenderFreeGlyphs(dmxScreen->beDisplay,
                              glyphPriv->glyphSets[i], gids, nglyphs);
                dmxSync(dmxScreen, FALSE);
            }
          }
            xfree(gids);
      }
    }

    return dmxSaveRenderVector[stuff->renderReqType](client);
}

/** Composite glyphs on each screen into the requested picture.  If
 *  either the src or dest picture has not been allocated due to lazy
 *  window creation, this request will gracefully return. */
00437 static int dmxProcRenderCompositeGlyphs(ClientPtr client)
{
    int  ret;
    REQUEST(xRenderCompositeGlyphsReq);

    ret = dmxSaveRenderVector[stuff->renderReqType](client);

    /* For the following to work with PanoramiX, it assumes that Render
     * wraps the ProcRenderVector after dmxRenderInit has been called.
     */
    if (ret == Success) {
      PicturePtr         pSrc;
      dmxPictPrivPtr     pSrcPriv;
      PicturePtr         pDst;
      dmxPictPrivPtr     pDstPriv;
      PictFormatPtr      pFmt;
      XRenderPictFormat *pFormat;
      int                size;

      int                scrnNum;
      DMXScreenInfo     *dmxScreen;

      CARD8             *buffer;
      CARD8             *end;
      int                space;

      int                nglyph;
      char              *glyphs;
      char              *curGlyph;

      xGlyphElt         *elt;
      int                nelt;
      XGlyphElt8        *elts;
      XGlyphElt8        *curElt;

      GlyphSetPtr        glyphSet;
      dmxGlyphPrivPtr    glyphPriv;

      pSrc = SecurityLookupIDByType(client, stuff->src, PictureType,
                              SecurityReadAccess);
      pSrcPriv = DMX_GET_PICT_PRIV(pSrc);
      if (!pSrcPriv->pict)
          return ret;

      pDst = SecurityLookupIDByType(client, stuff->dst, PictureType,
                              SecurityWriteAccess);
      pDstPriv = DMX_GET_PICT_PRIV(pDst);
      if (!pDstPriv->pict)
          return ret;

      scrnNum = pDst->pDrawable->pScreen->myNum;
      dmxScreen = &dmxScreens[scrnNum];

      /* Note: If the back-end display has been detached, then it
       * should not be possible to reach here since the pSrcPriv->pict
       * and pDstPriv->pict will have already been set to 0.
       */
      if (!dmxScreen->beDisplay)
          return ret;

      if (stuff->maskFormat)
          pFmt = SecurityLookupIDByType(client, stuff->maskFormat,
                                PictFormatType, SecurityReadAccess);
      else
          pFmt = NULL;

      pFormat = dmxFindFormat(dmxScreen, pFmt);

      switch (stuff->renderReqType) {
      case X_RenderCompositeGlyphs8:  size = sizeof(CARD8);  break;
      case X_RenderCompositeGlyphs16: size = sizeof(CARD16); break;
      case X_RenderCompositeGlyphs32: size = sizeof(CARD32); break;
        default:                        return BadPictOp; /* Can't happen */
      }

      buffer = (CARD8 *)(stuff + 1);
      end = (CARD8 *)stuff + (stuff->length << 2);
      nelt = 0;
      nglyph = 0;
      while (buffer + sizeof(xGlyphElt) < end) {
          elt = (xGlyphElt *)buffer;
          buffer += sizeof(xGlyphElt);

          if (elt->len == 0xff) {
            buffer += 4;
          } else {
            nelt++;
            nglyph += elt->len;
            space = size * elt->len;
            if (space & 3) space += 4 - (space & 3);
            buffer += space;
          }
      }

      /* The following only works for Render version > 0.2 */

      /* All of the XGlyphElt* structure sizes are identical */
      elts = ALLOCATE_LOCAL(nelt * sizeof(XGlyphElt8));
      if (!elts)
          return BadAlloc;

      glyphs = ALLOCATE_LOCAL(nglyph * size);
      if (!glyphs) {
          DEALLOCATE_LOCAL(elts);
          return BadAlloc;
      }

      buffer = (CARD8 *)(stuff + 1);
      end = (CARD8 *)stuff + (stuff->length << 2);
      curGlyph = glyphs;
      curElt = elts;

      glyphSet = SecurityLookupIDByType(client, stuff->glyphset,
                                GlyphSetType, SecurityReadAccess);
      glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet);

      while (buffer + sizeof(xGlyphElt) < end) {
          elt = (xGlyphElt *)buffer;
          buffer += sizeof(xGlyphElt);

          if (elt->len == 0xff) {
            glyphSet = SecurityLookupIDByType(client,
                                      *((CARD32 *)buffer),
                                      GlyphSetType,
                                      SecurityReadAccess);
            glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet);
            buffer += 4;
          } else {
            curElt->glyphset = glyphPriv->glyphSets[scrnNum];
            curElt->xOff = elt->deltax;
            curElt->yOff = elt->deltay;
            curElt->nchars = elt->len;
            curElt->chars = curGlyph;

            memcpy(curGlyph, buffer, size*elt->len);
            curGlyph += size * elt->len;

            curElt++;

            space = size * elt->len;
            if (space & 3) space += 4 - (space & 3);
            buffer += space;
          }
      }

      switch (stuff->renderReqType) {
      case X_RenderCompositeGlyphs8:
          XRenderCompositeText8(dmxScreen->beDisplay, stuff->op,
                          pSrcPriv->pict, pDstPriv->pict,
                          pFormat,
                          stuff->xSrc, stuff->ySrc,
                          0, 0, elts, nelt);
          break;
      case X_RenderCompositeGlyphs16:
          XRenderCompositeText16(dmxScreen->beDisplay, stuff->op,
                           pSrcPriv->pict, pDstPriv->pict,
                           pFormat,
                           stuff->xSrc, stuff->ySrc,
                           0, 0, (XGlyphElt16 *)elts, nelt);
          break;
      case X_RenderCompositeGlyphs32:
          XRenderCompositeText32(dmxScreen->beDisplay, stuff->op,
                           pSrcPriv->pict, pDstPriv->pict,
                           pFormat,
                           stuff->xSrc, stuff->ySrc,
                           0, 0, (XGlyphElt32 *)elts, nelt);
          break;
      }

      dmxSync(dmxScreen, FALSE);

      DEALLOCATE_LOCAL(elts);
      DEALLOCATE_LOCAL(glyphs);
    }

    return ret;
}

/** Set the picture transform on each screen. */
00616 static int dmxProcRenderSetPictureTransform(ClientPtr client)
{
    DMXScreenInfo  *dmxScreen;
    PicturePtr      pPicture;
    dmxPictPrivPtr  pPictPriv;
    XTransform      xform;
    REQUEST(xRenderSetPictureTransformReq);

    REQUEST_SIZE_MATCH(xRenderSetPictureTransformReq);
    VERIFY_PICTURE(pPicture, stuff->picture, client, SecurityWriteAccess,
               RenderErrBase + BadPicture);

    /* For the following to work with PanoramiX, it assumes that Render
     * wraps the ProcRenderVector after dmxRenderInit has been called.
     */
    dmxScreen = &dmxScreens[pPicture->pDrawable->pScreen->myNum];
    pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    if (pPictPriv->pict) {
      xform.matrix[0][0] = stuff->transform.matrix11;
      xform.matrix[0][1] = stuff->transform.matrix12;
      xform.matrix[0][2] = stuff->transform.matrix13;
      xform.matrix[1][0] = stuff->transform.matrix21;
      xform.matrix[1][1] = stuff->transform.matrix22;
      xform.matrix[1][2] = stuff->transform.matrix23;
      xform.matrix[2][0] = stuff->transform.matrix31;
      xform.matrix[2][1] = stuff->transform.matrix32;
      xform.matrix[2][2] = stuff->transform.matrix33;

      XRenderSetPictureTransform(dmxScreen->beDisplay,
                           pPictPriv->pict,
                           &xform);
      dmxSync(dmxScreen, FALSE);
    }

    return dmxSaveRenderVector[stuff->renderReqType](client);
}

/** Set the picture filter on each screen. */
00655 static int dmxProcRenderSetPictureFilter(ClientPtr client)
{
    DMXScreenInfo  *dmxScreen;
    PicturePtr      pPicture;
    dmxPictPrivPtr  pPictPriv;
    char           *filter;
    XFixed         *params;
    int             nparams;
    REQUEST(xRenderSetPictureFilterReq);

    REQUEST_SIZE_MATCH(xRenderSetPictureFilterReq);
    VERIFY_PICTURE(pPicture, stuff->picture, client, SecurityWriteAccess,
               RenderErrBase + BadPicture);

    /* For the following to work with PanoramiX, it assumes that Render
     * wraps the ProcRenderVector after dmxRenderInit has been called.
     */
    dmxScreen = &dmxScreens[pPicture->pDrawable->pScreen->myNum];
    pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    if (pPictPriv->pict) {
      filter  = (char *)(stuff + 1);
      params  = (XFixed *)(filter + ((stuff->nbytes + 3) & ~3));
      nparams = ((XFixed *)stuff + client->req_len) - params;

      XRenderSetPictureFilter(dmxScreen->beDisplay,
                        pPictPriv->pict,
                        filter,
                        params,
                        nparams);
      dmxSync(dmxScreen, FALSE);
    }

    return dmxSaveRenderVector[stuff->renderReqType](client);
}


/** Create a picture on the appropriate screen.  This is the actual
 *  function that creates the picture.  However, if the associated
 *  window has not yet been created due to lazy window creation, then
 *  delay the picture creation until the window is mapped. */
00696 static Picture dmxDoCreatePicture(PicturePtr pPicture)
{
    DrawablePtr               pDraw     = pPicture->pDrawable;
    ScreenPtr                 pScreen   = pDraw->pScreen;
    DMXScreenInfo            *dmxScreen = &dmxScreens[pScreen->myNum];
    XRenderPictFormat        *pFormat;
    Drawable                  draw;

    if (pPicture->pDrawable->type == DRAWABLE_WINDOW) {
      dmxWinPrivPtr  pWinPriv = DMX_GET_WINDOW_PRIV((WindowPtr)(pDraw));

      if (!(draw = pWinPriv->window)) {
          /* Window has not been created yet due to the window
           * optimization.  Delay picture creation until window is
           * mapped.
           */
          pWinPriv->hasPict = TRUE;
          return 0;
      }
    } else {
      dmxPixPrivPtr  pPixPriv = DMX_GET_PIXMAP_PRIV((PixmapPtr)(pDraw));

      if (!(draw = pPixPriv->pixmap)) {
          /* FIXME: Zero width/height pixmap?? */
          return 0;
      }
    }

    /* This should not be reached if the back-end display has been
     * detached because the pWinPriv->window or the pPixPriv->pixmap
     * will be NULL; however, we add it here for completeness
     */
    if (!dmxScreen->beDisplay)
      return 0;

    pFormat = dmxFindFormat(dmxScreen, pPicture->pFormat);

    return XRenderCreatePicture(dmxScreen->beDisplay, draw, pFormat, 0, 0);
}

/** Create a list of pictures.  This function is called by
 *  dmxCreateAndRealizeWindow() during the lazy window creation
 *  realization process.  It creates the entire list of pictures that
 *  are associated with the given window. */
00740 void dmxCreatePictureList(WindowPtr pWindow)
{
    PicturePtr  pPicture = GetPictureWindow(pWindow);

    while (pPicture) {
      dmxPictPrivPtr  pPictPriv = DMX_GET_PICT_PRIV(pPicture);

      /* Create the picture for this window */
      pPictPriv->pict = dmxDoCreatePicture(pPicture);

      /* ValidatePicture takes care of the state changes */

      pPicture = pPicture->pNext;
    }
}

/** Create a picture.  This function handles the CreatePicture
 *  unwrapping/wrapping and calls dmxDoCreatePicture to actually create
 *  the picture on the appropriate screen.  */
00759 int dmxCreatePicture(PicturePtr pPicture)
{
    ScreenPtr         pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pPictPriv = DMX_GET_PICT_PRIV(pPicture);
    int               ret       = Success;

    DMX_UNWRAP(CreatePicture, dmxScreen, ps);
#if 1
    if (ps->CreatePicture)
      ret = ps->CreatePicture(pPicture);
#endif

    /* Create picture on back-end server */
    pPictPriv->pict      = dmxDoCreatePicture(pPicture);
    pPictPriv->savedMask = 0;

    DMX_WRAP(CreatePicture, dmxCreatePicture, dmxScreen, ps);

    return ret;
}

/** Destroy \a pPicture on the back-end server. */
00783 Bool dmxBEFreePicture(PicturePtr pPicture)
{
    ScreenPtr      pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
    dmxPictPrivPtr pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    if (pPictPriv->pict) {
      XRenderFreePicture(dmxScreen->beDisplay, pPictPriv->pict);
      pPictPriv->pict = (Picture)0;
      return TRUE;
    }

    return FALSE;
}

/** Destroy a list of pictures that are associated with the window that
 *  is being destroyed.  This function is called by #dmxDestroyWindow().
 *  */
00801 Bool dmxDestroyPictureList(WindowPtr pWindow)
{
    PicturePtr  pPicture = GetPictureWindow(pWindow);
    Bool        ret      = FALSE;

    while (pPicture) {
      ret |= dmxBEFreePicture(pPicture);
      pPicture = pPicture->pNext;
    }

    return ret;
}

/** Destroy a picture.  This function calls the wrapped function that
 *  frees the resources in the DMX server associated with this
 *  picture. */
00817 void dmxDestroyPicture(PicturePtr pPicture)
{
    ScreenPtr         pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);

    DMX_UNWRAP(DestroyPicture, dmxScreen, ps);

    /* Destroy picture on back-end server */
    if (dmxBEFreePicture(pPicture))
      dmxSync(dmxScreen, FALSE);

#if 1
    if (ps->DestroyPicture)
      ps->DestroyPicture(pPicture);
#endif
    DMX_WRAP(DestroyPicture, dmxDestroyPicture, dmxScreen, ps);
}

/** Change the picture's list of clip rectangles. */
00837 int dmxChangePictureClip(PicturePtr pPicture, int clipType,
                   pointer value, int n)
{
    ScreenPtr         pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    DMX_UNWRAP(ChangePictureClip, dmxScreen, ps);
#if 1
    if (ps->ChangePictureClip)
      ps->ChangePictureClip(pPicture, clipType, value, n);
#endif

    /* Change picture clip rects on back-end server */
    if (pPictPriv->pict) {
      /* The clip has already been changed into a region by the mi
       * routine called above.
       */
      if (pPicture->clientClip) {
          RegionPtr   pClip = pPicture->clientClip;
          BoxPtr      pBox  = REGION_RECTS(pClip);
          int         nBox  = REGION_NUM_RECTS(pClip);
          XRectangle *pRects;
          XRectangle *pRect;
          int         nRects;

          nRects = nBox;
          pRects = pRect = xalloc(nRects * sizeof(*pRect));

          while (nBox--) {
            pRect->x      = pBox->x1;
            pRect->y      = pBox->y1;
            pRect->width  = pBox->x2 - pBox->x1;
            pRect->height = pBox->y2 - pBox->y1;
            pBox++;
            pRect++;
          }

          XRenderSetPictureClipRectangles(dmxScreen->beDisplay,
                                  pPictPriv->pict,
                                  0, 0,
                                  pRects,
                                  nRects);
          xfree(pRects);
      } else {
          XRenderSetPictureClipRectangles(dmxScreen->beDisplay,
                                  pPictPriv->pict,
                                  0, 0, NULL, 0);
      }
      dmxSync(dmxScreen, FALSE);
    } else {
      /* FIXME: Handle saving clip region when offscreen */
    }

    DMX_WRAP(ChangePictureClip, dmxChangePictureClip, dmxScreen, ps);
    
    return Success;
}

/** Destroy the picture's list of clip rectangles. */
00898 void dmxDestroyPictureClip(PicturePtr pPicture)
{
    ScreenPtr         pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    DMX_UNWRAP(DestroyPictureClip, dmxScreen, ps);
#if 1
    if (ps->DestroyPictureClip)
      ps->DestroyPictureClip(pPicture);
#endif

    /* Destroy picture clip rects on back-end server */
    if (pPictPriv->pict) {
      XRenderSetPictureClipRectangles(dmxScreen->beDisplay,
                              pPictPriv->pict,
                              0, 0, NULL, 0);
      dmxSync(dmxScreen, FALSE);
    } else {
      /* FIXME: Handle destroying clip region when offscreen */
    }

    DMX_WRAP(DestroyPictureClip, dmxDestroyPictureClip, dmxScreen, ps);
}

/** Change the attributes of the pictures.  If the picture has not yet
 *  been created due to lazy window creation, save the mask so that it
 *  can be used to appropriately initialize the picture's attributes
 *  when it is created later. */
00928 void dmxChangePicture(PicturePtr pPicture, Mask mask)
{
    ScreenPtr         pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    DMX_UNWRAP(ChangePicture, dmxScreen, ps);
#if 1
    if (ps->ChangePicture)
      ps->ChangePicture(pPicture, mask);
#endif

    /* Picture attribute changes are handled in ValidatePicture */
    pPictPriv->savedMask |= mask;

    DMX_WRAP(ChangePicture, dmxChangePicture, dmxScreen, ps);
}

/** Validate the picture's attributes before rendering to it.  Update
 *  any picture attributes that have been changed by one of the higher
 *  layers. */
00950 void dmxValidatePicture(PicturePtr pPicture, Mask mask)
{
    ScreenPtr         pScreen   = pPicture->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pPictPriv = DMX_GET_PICT_PRIV(pPicture);

    DMX_UNWRAP(ValidatePicture, dmxScreen, ps);

    /* Change picture attributes on back-end server */
    if (pPictPriv->pict) {
      XRenderPictureAttributes  attribs;

      if (mask & CPRepeat) {
          attribs.repeat = pPicture->repeatType;
      }
      if (mask & CPAlphaMap) {
          if (pPicture->alphaMap) {
            dmxPictPrivPtr  pAlphaPriv;
            pAlphaPriv = DMX_GET_PICT_PRIV(pPicture->alphaMap);
            if (pAlphaPriv->pict) {
                attribs.alpha_map = pAlphaPriv->pict;
            } else {
                /* FIXME: alpha picture drawable has not been created?? */
                return; /* or should this be: attribs.alpha_map = None; */
            }
          } else {
            attribs.alpha_map = None;
          }
      }
      if (mask & CPAlphaXOrigin)
          attribs.alpha_x_origin = pPicture->alphaOrigin.x;
      if (mask & CPAlphaYOrigin)
          attribs.alpha_y_origin = pPicture->alphaOrigin.y;
      if (mask & CPClipXOrigin)
          attribs.clip_x_origin = pPicture->clipOrigin.x;
      if (mask & CPClipYOrigin)
          attribs.clip_y_origin = pPicture->clipOrigin.y;
      if (mask & CPClipMask)
          mask &= ~CPClipMask; /* Handled in ChangePictureClip */
      if (mask & CPGraphicsExposure)
          attribs.graphics_exposures = pPicture->graphicsExposures;
      if (mask & CPSubwindowMode)
          attribs.subwindow_mode = pPicture->subWindowMode;
      if (mask & CPPolyEdge)
          attribs.poly_edge = pPicture->polyEdge;
      if (mask & CPPolyMode)
          attribs.poly_mode = pPicture->polyMode;
      if (mask & CPDither)
          attribs.dither = pPicture->dither;
      if (mask & CPComponentAlpha)
          attribs.component_alpha = pPicture->componentAlpha;

      XRenderChangePicture(dmxScreen->beDisplay, pPictPriv->pict,
                       mask, &attribs);
      dmxSync(dmxScreen, FALSE);
    } else {
      pPictPriv->savedMask |= mask;
    }

#if 1
    if (ps->ValidatePicture)
      ps->ValidatePicture(pPicture, mask);
#endif

    DMX_WRAP(ValidatePicture, dmxValidatePicture, dmxScreen, ps);
}

/** Composite a picture on the appropriate screen by combining the
 *  specified rectangle of the transformed src and mask operands with
 *  the specified rectangle of the dst using op as the compositing
 *  operator.  For a complete description see the protocol document of
 *  the RENDER library. */
01023 void dmxComposite(CARD8 op,
              PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst,
              INT16 xSrc, INT16 ySrc,
              INT16 xMask, INT16 yMask,
              INT16 xDst, INT16 yDst,
              CARD16 width, CARD16 height)
{
    ScreenPtr         pScreen   = pDst->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pSrcPriv  = DMX_GET_PICT_PRIV(pSrc);
    dmxPictPrivPtr    pMaskPriv = NULL;
    dmxPictPrivPtr    pDstPriv  = DMX_GET_PICT_PRIV(pDst);

    if (pMask) pMaskPriv = DMX_GET_PICT_PRIV(pMask);

    DMX_UNWRAP(Composite, dmxScreen, ps);
#if 0
    if (ps->Composite)
      ps->Composite(op, pSrc, pMask, pDst,
                  xSrc, ySrc, xMask, yMask, xDst, yDst,
                  width, height);
#endif

    /* Composite on back-end server */
    if (pSrcPriv->pict && pDstPriv->pict &&
      ((pMaskPriv && pMaskPriv->pict) || !pMaskPriv)) {
      XRenderComposite(dmxScreen->beDisplay,
                   op,
                   pSrcPriv->pict,
                   pMaskPriv ? pMaskPriv->pict : None,
                   pDstPriv->pict,
                   xSrc, ySrc,
                   xMask, yMask,
                   xDst, yDst,
                   width, height);
      dmxSync(dmxScreen, FALSE);
    }


    DMX_WRAP(Composite, dmxComposite, dmxScreen, ps);
}

/** Null function to catch when/if RENDER calls lower level mi hooks.
 *  Compositing glyphs is handled by dmxProcRenderCompositeGlyphs().
 *  This function should never be called. */
01069 void dmxGlyphs(CARD8 op,
             PicturePtr pSrc, PicturePtr pDst,
             PictFormatPtr maskFormat,
             INT16 xSrc, INT16 ySrc,
             int nlists, GlyphListPtr lists, GlyphPtr *glyphs)
{
    /* This won't work, so we need to wrap ProcRenderCompositeGlyphs */
}

/** Fill a rectangle on the appropriate screen by combining the color
 *  with the dest picture in the area specified by the list of
 *  rectangles.  For a complete description see the protocol document of
 *  the RENDER library. */
01082 void dmxCompositeRects(CARD8 op,
                   PicturePtr pDst,
                   xRenderColor *color,
                   int nRect, xRectangle *rects)
{
    ScreenPtr         pScreen   = pDst->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pPictPriv = DMX_GET_PICT_PRIV(pDst);

    DMX_UNWRAP(CompositeRects, dmxScreen, ps);
#if 0
    if (ps->CompositeRects)
      ps->CompositeRects(op, pDst, color, nRect, rects);
#endif

    /* CompositeRects on back-end server */
    if (pPictPriv->pict) {
      XRenderFillRectangles(dmxScreen->beDisplay,
                        op,
                        pPictPriv->pict,
                        (XRenderColor *)color,
                        (XRectangle *)rects,
                        nRect);
      dmxSync(dmxScreen, FALSE);
    }

    DMX_WRAP(CompositeRects, dmxCompositeRects, dmxScreen, ps);
}

/** Indexed color visuals are not yet supported. */
01113 Bool dmxInitIndexed(ScreenPtr pScreen, PictFormatPtr pFormat)
{
    return TRUE;
}

/** Indexed color visuals are not yet supported. */
01119 void dmxCloseIndexed(ScreenPtr pScreen, PictFormatPtr pFormat)
{
}

/** Indexed color visuals are not yet supported. */
01124 void dmxUpdateIndexed(ScreenPtr pScreen, PictFormatPtr pFormat,
                  int ndef, xColorItem *pdef)
{
}

/** Composite a list of trapezoids on the appropriate screen.  For a
 *  complete description see the protocol document of the RENDER
 *  library. */
01132 void dmxTrapezoids(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
               PictFormatPtr maskFormat,
               INT16 xSrc, INT16 ySrc,
               int ntrap, xTrapezoid *traps)
{
    ScreenPtr         pScreen   = pDst->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pSrcPriv  = DMX_GET_PICT_PRIV(pSrc);
    dmxPictPrivPtr    pDstPriv  = DMX_GET_PICT_PRIV(pDst);

    DMX_UNWRAP(Trapezoids, dmxScreen, ps);
#if 0
    if (ps->Trapezoids)
      ps->Trapezoids(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntrap, *traps);
#endif

    /* Draw trapezoids on back-end server */
    if (pDstPriv->pict) {
      XRenderPictFormat *pFormat;

      pFormat = dmxFindFormat(dmxScreen, maskFormat);
      if (!pFormat) {
          /* FIXME: Error! */
      }

      XRenderCompositeTrapezoids(dmxScreen->beDisplay,
                           op,
                           pSrcPriv->pict,
                           pDstPriv->pict,
                           pFormat,
                           xSrc, ySrc,
                           (XTrapezoid *)traps,
                           ntrap);
      dmxSync(dmxScreen, FALSE);
    }

    DMX_WRAP(Trapezoids, dmxTrapezoids, dmxScreen, ps);
}

/** Composite a list of triangles on the appropriate screen.  For a
 *  complete description see the protocol document of the RENDER
 *  library. */
01175 void dmxTriangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
              PictFormatPtr maskFormat,
              INT16 xSrc, INT16 ySrc,
              int ntri, xTriangle *tris)
{
    ScreenPtr         pScreen   = pDst->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pSrcPriv  = DMX_GET_PICT_PRIV(pSrc);
    dmxPictPrivPtr    pDstPriv  = DMX_GET_PICT_PRIV(pDst);

    DMX_UNWRAP(Triangles, dmxScreen, ps);
#if 0
    if (ps->Triangles)
      ps->Triangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, *tris);
#endif

    /* Draw trapezoids on back-end server */
    if (pDstPriv->pict) {
      XRenderPictFormat *pFormat;

      pFormat = dmxFindFormat(dmxScreen, maskFormat);
      if (!pFormat) {
          /* FIXME: Error! */
      }

      XRenderCompositeTriangles(dmxScreen->beDisplay,
                          op,
                          pSrcPriv->pict,
                          pDstPriv->pict,
                          pFormat,
                          xSrc, ySrc,
                          (XTriangle *)tris,
                          ntri);
      dmxSync(dmxScreen, FALSE);
    }

    DMX_WRAP(Triangles, dmxTriangles, dmxScreen, ps);
}

/** Composite a triangle strip on the appropriate screen.  For a
 *  complete description see the protocol document of the RENDER
 *  library. */
01218 void dmxTriStrip(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
             PictFormatPtr maskFormat,
             INT16 xSrc, INT16 ySrc,
             int npoint, xPointFixed *points)
{
    ScreenPtr         pScreen   = pDst->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pSrcPriv  = DMX_GET_PICT_PRIV(pSrc);
    dmxPictPrivPtr    pDstPriv  = DMX_GET_PICT_PRIV(pDst);

    DMX_UNWRAP(TriStrip, dmxScreen, ps);
#if 0
    if (ps->TriStrip)
      ps->TriStrip(op, pSrc, pDst, maskFormat, xSrc, ySrc, npoint, *points);
#endif

    /* Draw trapezoids on back-end server */
    if (pDstPriv->pict) {
      XRenderPictFormat *pFormat;

      pFormat = dmxFindFormat(dmxScreen, maskFormat);
      if (!pFormat) {
          /* FIXME: Error! */
      }

      XRenderCompositeTriStrip(dmxScreen->beDisplay,
                         op,
                         pSrcPriv->pict,
                         pDstPriv->pict,
                         pFormat,
                         xSrc, ySrc,
                         (XPointFixed *)points,
                         npoint);
      dmxSync(dmxScreen, FALSE);
    }

    DMX_WRAP(TriStrip, dmxTriStrip, dmxScreen, ps);
}

/** Composite a triangle fan on the appropriate screen.  For a complete
 *  description see the protocol document of the RENDER library. */
01260 void dmxTriFan(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
             PictFormatPtr maskFormat,
             INT16 xSrc, INT16 ySrc,
             int npoint, xPointFixed *points)
{
    ScreenPtr         pScreen   = pDst->pDrawable->pScreen;
    DMXScreenInfo    *dmxScreen = &dmxScreens[pScreen->myNum];
    PictureScreenPtr  ps        = GetPictureScreen(pScreen);
    dmxPictPrivPtr    pSrcPriv  = DMX_GET_PICT_PRIV(pSrc);
    dmxPictPrivPtr    pDstPriv  = DMX_GET_PICT_PRIV(pDst);

    DMX_UNWRAP(TriFan, dmxScreen, ps);
#if 0
    if (ps->TriFan)
      ps->TriFan(op, pSrc, pDst, maskFormat, xSrc, ySrc, npoint, *points);
#endif

    /* Draw trapezoids on back-end server */
    if (pDstPriv->pict) {
      XRenderPictFormat *pFormat;

      pFormat = dmxFindFormat(dmxScreen, maskFormat);
      if (!pFormat) {
          /* FIXME: Error! */
      }

      XRenderCompositeTriFan(dmxScreen->beDisplay,
                         op,
                         pSrcPriv->pict,
                         pDstPriv->pict,
                         pFormat,
                         xSrc, ySrc,
                         (XPointFixed *)points,
                         npoint);
      dmxSync(dmxScreen, FALSE);
    }

    DMX_WRAP(TriFan, dmxTriFan, dmxScreen, ps);
}

Generated by  Doxygen 1.6.0   Back to index