499 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			499 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * Copyright © 2008 George Sapountzis <gsap7@yahoo.gr>
 | 
						|
 * Copyright © 2008 Red Hat, Inc
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, distribute, and sell this software
 | 
						|
 * and its documentation for any purpose is hereby granted without
 | 
						|
 * fee, provided that the above copyright notice appear in all copies
 | 
						|
 * and that both that copyright notice and this permission notice
 | 
						|
 * appear in supporting documentation, and that the name of the
 | 
						|
 * copyright holders not be used in advertising or publicity
 | 
						|
 * pertaining to distribution of the software without specific,
 | 
						|
 * written prior permission.  The copyright holders make no
 | 
						|
 * representations about the suitability of this software for any
 | 
						|
 * purpose.  It is provided "as is" without express or implied
 | 
						|
 * warranty.
 | 
						|
 *
 | 
						|
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 | 
						|
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 | 
						|
 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 | 
						|
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 | 
						|
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 | 
						|
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 | 
						|
 * SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef HAVE_DIX_CONFIG_H
 | 
						|
#include <dix-config.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <sys/time.h>
 | 
						|
#include <dlfcn.h>
 | 
						|
 | 
						|
#include <GL/gl.h>
 | 
						|
#include <GL/internal/dri_interface.h>
 | 
						|
#include <GL/glxtokens.h>
 | 
						|
 | 
						|
#include "scrnintstr.h"
 | 
						|
#include "pixmapstr.h"
 | 
						|
#include "gcstruct.h"
 | 
						|
#include "os.h"
 | 
						|
 | 
						|
#include "glxserver.h"
 | 
						|
#include "glxutil.h"
 | 
						|
#include "glxdricommon.h"
 | 
						|
 | 
						|
#include "glapitable.h"
 | 
						|
#include "glapi.h"
 | 
						|
#include "glthread.h"
 | 
						|
#include "dispatch.h"
 | 
						|
#include "extension_string.h"
 | 
						|
 | 
						|
/* RTLD_LOCAL is not defined on Cygwin */
 | 
						|
#ifdef __CYGWIN__
 | 
						|
#ifndef RTLD_LOCAL
 | 
						|
#define RTLD_LOCAL 0
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
typedef struct __GLXDRIscreen __GLXDRIscreen;
 | 
						|
typedef struct __GLXDRIcontext __GLXDRIcontext;
 | 
						|
typedef struct __GLXDRIdrawable __GLXDRIdrawable;
 | 
						|
 | 
						|
struct __GLXDRIscreen {
 | 
						|
    __GLXscreen base;
 | 
						|
    __DRIscreen *driScreen;
 | 
						|
    void *driver;
 | 
						|
 | 
						|
    const __DRIcoreExtension *core;
 | 
						|
    const __DRIswrastExtension *swrast;
 | 
						|
    const __DRIcopySubBufferExtension *copySubBuffer;
 | 
						|
    const __DRItexBufferExtension *texBuffer;
 | 
						|
    const __DRIconfig **driConfigs;
 | 
						|
};
 | 
						|
 | 
						|
struct __GLXDRIcontext {
 | 
						|
    __GLXcontext base;
 | 
						|
    __DRIcontext *driContext;
 | 
						|
};
 | 
						|
 | 
						|
struct __GLXDRIdrawable {
 | 
						|
    __GLXdrawable base;
 | 
						|
    __DRIdrawable *driDrawable;
 | 
						|
    __GLXDRIscreen *screen;
 | 
						|
 | 
						|
    GCPtr gc;                   /* scratch GC for span drawing */
 | 
						|
    GCPtr swapgc;               /* GC for swapping the color buffers */
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
__glXDRIdrawableDestroy(__GLXdrawable * drawable)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
 | 
						|
    const __DRIcoreExtension *core = private->screen->core;
 | 
						|
 | 
						|
    (*core->destroyDrawable) (private->driDrawable);
 | 
						|
 | 
						|
    FreeGC(private->gc, (GContext) 0);
 | 
						|
    FreeGC(private->swapgc, (GContext) 0);
 | 
						|
 | 
						|
    __glXDrawableRelease(drawable);
 | 
						|
 | 
						|
    free(private);
 | 
						|
}
 | 
						|
 | 
						|
static GLboolean
 | 
						|
__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable * drawable)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
 | 
						|
    const __DRIcoreExtension *core = private->screen->core;
 | 
						|
 | 
						|
    (*core->swapBuffers) (private->driDrawable);
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
__glXDRIdrawableCopySubBuffer(__GLXdrawable * basePrivate,
 | 
						|
                              int x, int y, int w, int h)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
 | 
						|
    const __DRIcopySubBufferExtension *copySubBuffer =
 | 
						|
        private->screen->copySubBuffer;
 | 
						|
 | 
						|
    if (copySubBuffer)
 | 
						|
        (*copySubBuffer->copySubBuffer) (private->driDrawable, x, y, w, h);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
__glXDRIcontextDestroy(__GLXcontext * baseContext)
 | 
						|
{
 | 
						|
    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
 | 
						|
    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
 | 
						|
 | 
						|
    (*screen->core->destroyContext) (context->driContext);
 | 
						|
    __glXContextDestroy(&context->base);
 | 
						|
    free(context);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIcontextMakeCurrent(__GLXcontext * baseContext)
 | 
						|
{
 | 
						|
    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
 | 
						|
    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
 | 
						|
    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
 | 
						|
    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
 | 
						|
 | 
						|
    return (*screen->core->bindContext) (context->driContext,
 | 
						|
                                         draw->driDrawable, read->driDrawable);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIcontextLoseCurrent(__GLXcontext * baseContext)
 | 
						|
{
 | 
						|
    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
 | 
						|
    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
 | 
						|
 | 
						|
    return (*screen->core->unbindContext) (context->driContext);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIcontextCopy(__GLXcontext * baseDst, __GLXcontext * baseSrc,
 | 
						|
                    unsigned long mask)
 | 
						|
{
 | 
						|
    __GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst;
 | 
						|
    __GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc;
 | 
						|
    __GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen;
 | 
						|
 | 
						|
    return (*screen->core->copyContext) (dst->driContext,
 | 
						|
                                         src->driContext, mask);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef __DRI_TEX_BUFFER
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIbindTexImage(__GLXcontext * baseContext,
 | 
						|
                     int buffer, __GLXdrawable * glxPixmap)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *drawable = (__GLXDRIdrawable *) glxPixmap;
 | 
						|
    const __DRItexBufferExtension *texBuffer = drawable->screen->texBuffer;
 | 
						|
    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
 | 
						|
 | 
						|
    if (texBuffer == NULL)
 | 
						|
        return Success;
 | 
						|
 | 
						|
#if __DRI_TEX_BUFFER_VERSION >= 2
 | 
						|
    if (texBuffer->base.version >= 2 && texBuffer->setTexBuffer2 != NULL) {
 | 
						|
        (*texBuffer->setTexBuffer2) (context->driContext,
 | 
						|
                                     glxPixmap->target,
 | 
						|
                                     glxPixmap->format, drawable->driDrawable);
 | 
						|
    }
 | 
						|
    else
 | 
						|
#endif
 | 
						|
        texBuffer->setTexBuffer(context->driContext,
 | 
						|
                                glxPixmap->target, drawable->driDrawable);
 | 
						|
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIreleaseTexImage(__GLXcontext * baseContext,
 | 
						|
                        int buffer, __GLXdrawable * pixmap)
 | 
						|
{
 | 
						|
    /* FIXME: Just unbind the texture? */
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIbindTexImage(__GLXcontext * baseContext,
 | 
						|
                     int buffer, __GLXdrawable * glxPixmap)
 | 
						|
{
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
__glXDRIreleaseTexImage(__GLXcontext * baseContext,
 | 
						|
                        int buffer, __GLXdrawable * pixmap)
 | 
						|
{
 | 
						|
    return Success;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
static __GLXtextureFromPixmap __glXDRItextureFromPixmap = {
 | 
						|
    __glXDRIbindTexImage,
 | 
						|
    __glXDRIreleaseTexImage
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
__glXDRIscreenDestroy(__GLXscreen * baseScreen)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
 | 
						|
 | 
						|
    (*screen->core->destroyScreen) (screen->driScreen);
 | 
						|
 | 
						|
    dlclose(screen->driver);
 | 
						|
 | 
						|
    __glXScreenDestroy(baseScreen);
 | 
						|
 | 
						|
    if (screen->driConfigs) {
 | 
						|
        for (i = 0; screen->driConfigs[i] != NULL; i++)
 | 
						|
            free((__DRIconfig **) screen->driConfigs[i]);
 | 
						|
        free(screen->driConfigs);
 | 
						|
    }
 | 
						|
 | 
						|
    free(screen);
 | 
						|
}
 | 
						|
 | 
						|
static __GLXcontext *
 | 
						|
__glXDRIscreenCreateContext(__GLXscreen * baseScreen,
 | 
						|
                            __GLXconfig * glxConfig,
 | 
						|
                            __GLXcontext * baseShareContext,
 | 
						|
                            unsigned num_attribs,
 | 
						|
                            const uint32_t *attribs,
 | 
						|
                            int *error)
 | 
						|
{
 | 
						|
    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
 | 
						|
    __GLXDRIcontext *context, *shareContext;
 | 
						|
    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
 | 
						|
    const __DRIcoreExtension *core = screen->core;
 | 
						|
    __DRIcontext *driShare;
 | 
						|
 | 
						|
    /* DRISWRAST won't support createContextAttribs, so these parameters will
 | 
						|
     * never be used.
 | 
						|
     */
 | 
						|
    (void) num_attribs;
 | 
						|
    (void) attribs;
 | 
						|
    (void) error;
 | 
						|
 | 
						|
    shareContext = (__GLXDRIcontext *) baseShareContext;
 | 
						|
    if (shareContext)
 | 
						|
        driShare = shareContext->driContext;
 | 
						|
    else
 | 
						|
        driShare = NULL;
 | 
						|
 | 
						|
    context = calloc(1, sizeof *context);
 | 
						|
    if (context == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    context->base.destroy = __glXDRIcontextDestroy;
 | 
						|
    context->base.makeCurrent = __glXDRIcontextMakeCurrent;
 | 
						|
    context->base.loseCurrent = __glXDRIcontextLoseCurrent;
 | 
						|
    context->base.copy = __glXDRIcontextCopy;
 | 
						|
    context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
 | 
						|
 | 
						|
    context->driContext =
 | 
						|
        (*core->createNewContext) (screen->driScreen,
 | 
						|
                                   config->driConfig, driShare, context);
 | 
						|
 | 
						|
    return &context->base;
 | 
						|
}
 | 
						|
 | 
						|
static __GLXdrawable *
 | 
						|
__glXDRIscreenCreateDrawable(ClientPtr client,
 | 
						|
                             __GLXscreen * screen,
 | 
						|
                             DrawablePtr pDraw,
 | 
						|
                             XID drawId,
 | 
						|
                             int type, XID glxDrawId, __GLXconfig * glxConfig)
 | 
						|
{
 | 
						|
    XID gcvals[2];
 | 
						|
    int status;
 | 
						|
    __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
 | 
						|
    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
 | 
						|
    __GLXDRIdrawable *private;
 | 
						|
 | 
						|
    private = calloc(1, sizeof *private);
 | 
						|
    if (private == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    private->screen = driScreen;
 | 
						|
    if (!__glXDrawableInit(&private->base, screen,
 | 
						|
                           pDraw, type, glxDrawId, glxConfig)) {
 | 
						|
        free(private);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    private->base.destroy = __glXDRIdrawableDestroy;
 | 
						|
    private->base.swapBuffers = __glXDRIdrawableSwapBuffers;
 | 
						|
    private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer;
 | 
						|
 | 
						|
    gcvals[0] = GXcopy;
 | 
						|
    private->gc =
 | 
						|
        CreateGC(pDraw, GCFunction, gcvals, &status, (XID) 0, serverClient);
 | 
						|
    gcvals[1] = FALSE;
 | 
						|
    private->swapgc =
 | 
						|
        CreateGC(pDraw, GCFunction | GCGraphicsExposures, gcvals, &status,
 | 
						|
                 (XID) 0, serverClient);
 | 
						|
 | 
						|
    private->driDrawable =
 | 
						|
        (*driScreen->swrast->createNewDrawable) (driScreen->driScreen,
 | 
						|
                                                 config->driConfig, private);
 | 
						|
 | 
						|
    return &private->base;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
swrastGetDrawableInfo(__DRIdrawable * draw,
 | 
						|
                      int *x, int *y, int *w, int *h, void *loaderPrivate)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *drawable = loaderPrivate;
 | 
						|
    DrawablePtr pDraw = drawable->base.pDraw;
 | 
						|
 | 
						|
    *x = pDraw->x;
 | 
						|
    *y = pDraw->x;
 | 
						|
    *w = pDraw->width;
 | 
						|
    *h = pDraw->height;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
swrastPutImage(__DRIdrawable * draw, int op,
 | 
						|
               int x, int y, int w, int h, char *data, void *loaderPrivate)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *drawable = loaderPrivate;
 | 
						|
    DrawablePtr pDraw = drawable->base.pDraw;
 | 
						|
    GCPtr gc;
 | 
						|
 | 
						|
    switch (op) {
 | 
						|
    case __DRI_SWRAST_IMAGE_OP_DRAW:
 | 
						|
        gc = drawable->gc;
 | 
						|
        break;
 | 
						|
    case __DRI_SWRAST_IMAGE_OP_SWAP:
 | 
						|
        gc = drawable->swapgc;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    ValidateGC(pDraw, gc);
 | 
						|
 | 
						|
    gc->ops->PutImage(pDraw, gc, pDraw->depth, x, y, w, h, 0, ZPixmap, data);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
swrastGetImage(__DRIdrawable * draw,
 | 
						|
               int x, int y, int w, int h, char *data, void *loaderPrivate)
 | 
						|
{
 | 
						|
    __GLXDRIdrawable *drawable = loaderPrivate;
 | 
						|
    DrawablePtr pDraw = drawable->base.pDraw;
 | 
						|
    ScreenPtr pScreen = pDraw->pScreen;
 | 
						|
 | 
						|
    pScreen->GetImage(pDraw, x, y, w, h, ZPixmap, ~0L, data);
 | 
						|
}
 | 
						|
 | 
						|
static const __DRIswrastLoaderExtension swrastLoaderExtension = {
 | 
						|
    {__DRI_SWRAST_LOADER, __DRI_SWRAST_LOADER_VERSION},
 | 
						|
    swrastGetDrawableInfo,
 | 
						|
    swrastPutImage,
 | 
						|
    swrastGetImage
 | 
						|
};
 | 
						|
 | 
						|
static const __DRIextension *loader_extensions[] = {
 | 
						|
    &systemTimeExtension.base,
 | 
						|
    &swrastLoaderExtension.base,
 | 
						|
    NULL
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
initializeExtensions(__GLXDRIscreen * screen)
 | 
						|
{
 | 
						|
    const __DRIextension **extensions;
 | 
						|
    int i;
 | 
						|
 | 
						|
    extensions = screen->core->getExtensions(screen->driScreen);
 | 
						|
 | 
						|
    for (i = 0; extensions[i]; i++) {
 | 
						|
#ifdef __DRI_COPY_SUB_BUFFER
 | 
						|
        if (strcmp(extensions[i]->name, __DRI_COPY_SUB_BUFFER) == 0) {
 | 
						|
            screen->copySubBuffer =
 | 
						|
                (const __DRIcopySubBufferExtension *) extensions[i];
 | 
						|
            /* GLX_MESA_copy_sub_buffer is always enabled. */
 | 
						|
        }
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __DRI_TEX_BUFFER
 | 
						|
        if (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0) {
 | 
						|
            screen->texBuffer = (const __DRItexBufferExtension *) extensions[i];
 | 
						|
            /* GLX_EXT_texture_from_pixmap is always enabled. */
 | 
						|
        }
 | 
						|
#endif
 | 
						|
        /* Ignore unknown extensions */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static __GLXscreen *
 | 
						|
__glXDRIscreenProbe(ScreenPtr pScreen)
 | 
						|
{
 | 
						|
    const char *driverName = "swrast";
 | 
						|
    __GLXDRIscreen *screen;
 | 
						|
 | 
						|
    screen = calloc(1, sizeof *screen);
 | 
						|
    if (screen == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    screen->base.destroy = __glXDRIscreenDestroy;
 | 
						|
    screen->base.createContext = __glXDRIscreenCreateContext;
 | 
						|
    screen->base.createDrawable = __glXDRIscreenCreateDrawable;
 | 
						|
    screen->base.swapInterval = NULL;
 | 
						|
    screen->base.pScreen = pScreen;
 | 
						|
 | 
						|
    screen->driver = glxProbeDriver(driverName,
 | 
						|
                                    (void **) &screen->core,
 | 
						|
                                    __DRI_CORE, __DRI_CORE_VERSION,
 | 
						|
                                    (void **) &screen->swrast,
 | 
						|
                                    __DRI_SWRAST, __DRI_SWRAST_VERSION);
 | 
						|
    if (screen->driver == NULL) {
 | 
						|
        goto handle_error;
 | 
						|
    }
 | 
						|
 | 
						|
    screen->driScreen =
 | 
						|
        (*screen->swrast->createNewScreen) (pScreen->myNum,
 | 
						|
                                            loader_extensions,
 | 
						|
                                            &screen->driConfigs, screen);
 | 
						|
 | 
						|
    if (screen->driScreen == NULL) {
 | 
						|
        LogMessage(X_ERROR, "AIGLX error: Calling driver entry point failed\n");
 | 
						|
        goto handle_error;
 | 
						|
    }
 | 
						|
 | 
						|
    initializeExtensions(screen);
 | 
						|
 | 
						|
    screen->base.fbconfigs = glxConvertConfigs(screen->core, screen->driConfigs,
 | 
						|
                                               GLX_WINDOW_BIT |
 | 
						|
                                               GLX_PIXMAP_BIT |
 | 
						|
                                               GLX_PBUFFER_BIT);
 | 
						|
 | 
						|
    __glXScreenInit(&screen->base, pScreen);
 | 
						|
 | 
						|
    screen->base.GLXmajor = 1;
 | 
						|
    screen->base.GLXminor = 4;
 | 
						|
 | 
						|
    LogMessage(X_INFO, "AIGLX: Loaded and initialized %s\n", driverName);
 | 
						|
 | 
						|
    return &screen->base;
 | 
						|
 | 
						|
 handle_error:
 | 
						|
    if (screen->driver)
 | 
						|
        dlclose(screen->driver);
 | 
						|
 | 
						|
    free(screen);
 | 
						|
 | 
						|
    LogMessage(X_ERROR, "GLX: could not load software renderer\n");
 | 
						|
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
_X_EXPORT __GLXprovider __glXDRISWRastProvider = {
 | 
						|
    __glXDRIscreenProbe,
 | 
						|
    "DRISWRAST",
 | 
						|
    NULL
 | 
						|
};
 |