/* Copyright (c) 2001, Stanford University
 * All rights reserved
 *
 * See the file LICENSE.txt for information on redistributing this software.
 */

#include "cr_environment.h"
#include "cr_string.h"
#include "cr_error.h"
#include "cr_mem.h"
#include "cr_spu.h"
#include "cr_environment.h"
#include "renderspu.h"
#include "cr_extstring.h"

#include <iprt/asm.h>

uint32_t renderspuContextRelease(ContextInfo *context);
uint32_t renderspuContextRetain(ContextInfo *context);

static void
DoSync(void)
{
    CRMessage *in, out;

    out.header.type = CR_MESSAGE_OOB;

    if (render_spu.is_swap_master)
    {
        int a;

        for (a = 0; a < render_spu.num_swap_clients; a++)
        {
            crNetGetMessage( render_spu.swap_conns[a], &in );
            crNetFree( render_spu.swap_conns[a], in);
        }

        for (a = 0; a < render_spu.num_swap_clients; a++)
            crNetSend( render_spu.swap_conns[a], NULL, &out, sizeof(CRMessage));
    }
    else
    {
        crNetSend( render_spu.swap_conns[0], NULL, &out, sizeof(CRMessage));

        crNetGetMessage( render_spu.swap_conns[0], &in );
        crNetFree( render_spu.swap_conns[0], in);
    }
}



/*
 * Visual functions
 */

/**
 * used for debugging and giving info to the user.
 */
void
renderspuMakeVisString( GLbitfield visAttribs, char *s )
{
    s[0] = 0;

    if (visAttribs & CR_RGB_BIT)
        crStrcat(s, "RGB");
    if (visAttribs & CR_ALPHA_BIT)
        crStrcat(s, "A");
    if (visAttribs & CR_DOUBLE_BIT)
        crStrcat(s, ", Doublebuffer");
    if (visAttribs & CR_STEREO_BIT)
        crStrcat(s, ", Stereo");
    if (visAttribs & CR_DEPTH_BIT)
        crStrcat(s, ", Z");
    if (visAttribs & CR_STENCIL_BIT)
        crStrcat(s, ", Stencil");
    if (visAttribs & CR_ACCUM_BIT)
        crStrcat(s, ", Accum");
    if (visAttribs & CR_MULTISAMPLE_BIT)
        crStrcat(s, ", Multisample");
    if (visAttribs & CR_OVERLAY_BIT)
        crStrcat(s, ", Overlay");
    if (visAttribs & CR_PBUFFER_BIT)
        crStrcat(s, ", PBuffer");
}

GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs)
{
    pVisInfo->displayName = crStrdup(displayName);
    pVisInfo->visAttribs = visAttribs;
    return renderspu_SystemInitVisual(pVisInfo);
}

/*
 * Find a VisualInfo which matches the given display name and attribute
 * bitmask, or return a pointer to a new visual.
 */
VisualInfo *
renderspuFindVisual(const char *displayName, GLbitfield visAttribs)
{
    int i;

    if (!displayName)
        displayName = "";

    /* first, try to find a match */
#if defined(WINDOWS) || defined(DARWIN)
    for (i = 0; i < render_spu.numVisuals; i++) {
        if (visAttribs == render_spu.visuals[i].visAttribs) {
            return &(render_spu.visuals[i]);
        }
    }
#elif defined(GLX)
    for (i = 0; i < render_spu.numVisuals; i++) {
        if (crStrcmp(displayName, render_spu.visuals[i].displayName) == 0
            && visAttribs == render_spu.visuals[i].visAttribs) {
            return &(render_spu.visuals[i]);
        }
    }
#endif

    if (render_spu.numVisuals >= MAX_VISUALS)
    {
        crWarning("Render SPU: Couldn't create a visual, too many visuals already");
        return NULL;
    }

    /* create a new visual */
    i = render_spu.numVisuals;
    if (renderspuInitVisual(&(render_spu.visuals[i]), displayName, visAttribs)) {
        render_spu.numVisuals++;
        return &(render_spu.visuals[i]);
    }
    else {
        crWarning("Render SPU: Couldn't get a visual, renderspu_SystemInitVisual failed");
        return NULL;
    }
}

static ContextInfo * renderspuCreateContextInternal(const char *dpyName, GLint visBits, GLint idCtx, ContextInfo * sharedContext)
{
    ContextInfo *context;
    VisualInfo *visual;

    if (idCtx <= 0)
    {
        idCtx = (GLint)crHashtableAllocKeys(render_spu.contextTable, 1);
        if (idCtx <= 0)
        {
            crWarning("failed to allocate context id");
            return NULL;
        }
    }
    else
    {
        if (crHashtableIsKeyUsed(render_spu.contextTable, idCtx))
        {
            crWarning("the specified ctx key %d is in use", idCtx);
            return NULL;
        }
    }


    if (!dpyName || crStrlen(render_spu.display_string)>0)
        dpyName = render_spu.display_string;

    visual = renderspuFindVisual(dpyName, visBits);
    if (!visual)
        return NULL;

    context = (ContextInfo *) crCalloc(sizeof(ContextInfo));
    if (!context)
        return NULL;
    context->BltInfo.Base.id = idCtx;
    context->shared = sharedContext;
    if (!renderspu_SystemCreateContext(visual, context, sharedContext))
        return NULL;

    crHashtableAdd(render_spu.contextTable, idCtx, context);

    context->BltInfo.Base.visualBits = visual->visAttribs;
    /*
    crDebug("Render SPU: CreateContext(%s, 0x%x) returning %d",
                    dpyName, visBits, context->BltInfo.Base.id);
    */

    if (sharedContext)
        renderspuContextRetain(sharedContext);

    context->cRefs = 1;

    return context;
}

GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx)
{
    ContextInfo *context, *sharedContext = NULL;

    if (shareCtx) {
        sharedContext
            = (ContextInfo *) crHashtableSearch(render_spu.contextTable, shareCtx);
        CRASSERT(sharedContext);
    }

    context = renderspuCreateContextInternal(dpyName, visBits, id, sharedContext);
    if (context)
        return context->BltInfo.Base.id;
    return -1;
}

/*
 * Context functions
 */

GLint RENDER_APIENTRY
renderspuCreateContext(const char *dpyName, GLint visBits, GLint shareCtx)
{
    return renderspuCreateContextEx(dpyName, visBits, 0, shareCtx);
}

static void renderspuDestroyContextTerminate( ContextInfo *context )
{
    CRASSERT(context->BltInfo.Base.id == -1);
    renderspu_SystemDestroyContext( context );
    if (context->extensionString) {
        crFree(context->extensionString);
        context->extensionString = NULL;
    }

    if (context->shared)
        renderspuContextRelease( context->shared );

    crFree(context);
}

uint32_t renderspuContextRetain( ContextInfo *context )
{
    Assert(context->cRefs);
    return ASMAtomicIncU32(&context->cRefs);
}

uint32_t renderspuContextRelease( ContextInfo *context )
{
    uint32_t cRefs = ASMAtomicDecU32(&context->cRefs);
    if (!cRefs)
        renderspuDestroyContextTerminate( context );
    else
        CRASSERT(cRefs < UINT32_MAX/2);
    return cRefs;
}

uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context )
{
    /* invalidate the context id to mark it as deleted */
    context->BltInfo.Base.id = -1;

    /* some drivers do not like when the base (shared) context is deleted before its referals,
     * this is why we keep a context refference counting the base (shared) context will be destroyed as soon as*/
    return renderspuContextRelease( context );
}

ContextInfo * renderspuDefaultSharedContextAcquire()
{
    ContextInfo * pCtx = render_spu.defaultSharedContext;
    if (!pCtx)
        return NULL;

    renderspuContextRetain(pCtx);
    return pCtx;
}

void renderspuDefaultSharedContextRelease(ContextInfo * pCtx)
{
    renderspuContextRelease(pCtx);
}


static void RENDER_APIENTRY
renderspuDestroyContext( GLint ctx )
{
    ContextInfo *context, *curCtx;

    CRASSERT(ctx);

    if (ctx == CR_RENDER_DEFAULT_CONTEXT_ID)
    {
        crWarning("request to destroy a default context, ignoring");
        return;
    }

    context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx);

    if (!context)
    {
        crWarning("request to delete inexistent context");
        return;
    }

    if (render_spu.defaultSharedContext == context)
    {
        renderspuSetDefaultSharedContext(NULL);
    }

    curCtx = GET_CONTEXT_VAL();
//    CRASSERT(curCtx);
    if (curCtx == context)
    {
        renderspuMakeCurrent( CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID );
        curCtx = GET_CONTEXT_VAL();
		Assert(curCtx);
		Assert(curCtx != context);
    }

    crHashtableDelete(render_spu.contextTable, ctx, NULL);

    renderspuContextMarkDeletedAndRelease(context);
}

WindowInfo* renderspuWinCreate(GLint visBits, GLint id)
{
    WindowInfo* window = (WindowInfo *)crAlloc(sizeof (*window));
    if (!window)
    {
        crWarning("crAlloc failed");
        return NULL;
    }

    if (!renderspuWinInit(window, NULL, visBits, id))
    {
        crWarning("renderspuWinInit failed");
        crFree(window);
        return NULL;
    }

    return window;
}

void renderspuWinTermOnShutdown(WindowInfo *window)
{
    renderspuVBoxCompositorSet(window, NULL);
    renderspuVBoxPresentBlitterCleanup(window);
    window->BltInfo.Base.id = -1;
    renderspu_SystemDestroyWindow( window );
}

static void renderspuCheckCurrentCtxWindowCB(unsigned long key, void *data1, void *data2)
{
    ContextInfo *pCtx = (ContextInfo *) data1;
    WindowInfo *pWindow = data2;
    (void) key;

    if (pCtx->currentWindow==pWindow)
    {
        WindowInfo* pDummy = renderspuGetDummyWindow(pCtx->BltInfo.Base.visualBits);
        if (pDummy)
        {
            renderspuPerformMakeCurrent(pDummy, 0, pCtx);
        }
        else
        {
            crWarning("failed to get dummy window");
            renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, pCtx->BltInfo.Base.id);
        }
    }
}

void renderspuWinTerm( WindowInfo *window )
{
    if (!renderspuWinIsTermed(window))
    {

    GET_CONTEXT(pOldCtx);
    WindowInfo * pOldWindow = pOldCtx ? pOldCtx->currentWindow : NULL;
    CRASSERT(!pOldCtx == !pOldWindow);
    /* ensure no concurrent draws can take place */
    renderspuWinTermOnShutdown(window);
    /* check if this window is bound to some ctx. Note: window pointer is already freed here */
    crHashtableWalk(render_spu.contextTable, renderspuCheckCurrentCtxWindowCB, window);
    /* restore current context */
    {
        GET_CONTEXT(pNewCtx);
        WindowInfo * pNewWindow = pNewCtx ? pNewCtx->currentWindow : NULL;
        CRASSERT(!pNewCtx == !pNewWindow);

        if (pOldWindow == window)
            renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID);
        else if (pNewCtx != pOldCtx || pOldWindow != pNewWindow)
        {
            if (pOldCtx)
                renderspuPerformMakeCurrent(pOldWindow, 0, pOldCtx);
            else
                renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID);
        }
    }

    }
}

void renderspuWinCleanup(WindowInfo *window)
{
    renderspuWinTerm( window );
    RTCritSectDelete(&window->CompositorLock);
}

void renderspuWinDestroy(WindowInfo *window)
{
    renderspuWinCleanup(window);
    crFree(window);
}

WindowInfo* renderspuGetDummyWindow(GLint visBits)
{
    WindowInfo *window = (WindowInfo *) crHashtableSearch(render_spu.dummyWindowTable, visBits);
    if (!window)
    {
        window = renderspuWinCreate(visBits, -1);
        if (!window)
        {
            WARN(("renderspuWinCreate failed"));
            return NULL;
        }

        crHashtableAdd(render_spu.dummyWindowTable, visBits, window);
    }

    return window;
}

void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context)
{
    if (window && context)
    {
#ifdef CHROMIUM_THREADSAFE
        crSetTSD(&_RenderTSD, context);
#else
        render_spu.currentContext = context;
#endif
        context->currentWindow = window;
        if (!window)
        {
            crDebug("Render SPU: MakeCurrent invalid window id: %d", window->BltInfo.Base.id);
            return;
        }
        if (!context)
        {
            crDebug("Render SPU: MakeCurrent invalid context id: %d", context->BltInfo.Base.id);
            return;
        }

        renderspu_SystemMakeCurrent( window, nativeWindow, context );
        if (!context->everCurrent) {
            /* print OpenGL info */
            const char *extString = (const char *) render_spu.ws.glGetString( GL_EXTENSIONS );
            /*
            crDebug( "Render SPU: GL_EXTENSIONS:   %s", render_spu.ws.glGetString( GL_EXTENSIONS ) );
            */
            crInfo( "Render SPU: GL_VENDOR:   %s", render_spu.ws.glGetString( GL_VENDOR ) );
            crInfo( "Render SPU: GL_RENDERER: %s", render_spu.ws.glGetString( GL_RENDERER ) );
            crInfo( "Render SPU: GL_VERSION:  %s", render_spu.ws.glGetString( GL_VERSION ) );
            crInfo( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) );
            if (crStrstr(extString, "GL_ARB_window_pos"))
                context->haveWindowPosARB = GL_TRUE;
            else
                context->haveWindowPosARB = GL_FALSE;
            context->everCurrent = GL_TRUE;
        }
        if (window->BltInfo.Base.id == CR_RENDER_DEFAULT_WINDOW_ID && window->mapPending &&
                !render_spu.render_to_app_window && !render_spu.render_to_crut_window) {
            /* Window[CR_RENDER_DEFAULT_CONTEXT_ID] is special, it's the default window and normally hidden.
             * If the mapPending flag is set, then we should now make the window
             * visible.
             */
            /*renderspu_SystemShowWindow( window, GL_TRUE );*/
            window->mapPending = GL_FALSE;
        }
        window->everCurrent = GL_TRUE;
    }
    else if (!window && !context)
    {
        renderspu_SystemMakeCurrent( NULL, 0, NULL );
#ifdef CHROMIUM_THREADSAFE
        crSetTSD(&_RenderTSD, NULL);
#else
        render_spu.currentContext = NULL;
#endif
    }
    else
    {
        crError("renderspuMakeCurrent invalid ids: crWindow(%d), ctx(%d)",
                window ? window->BltInfo.Base.id : 0,
                context ? context->BltInfo.Base.id : 0);
    }
}

void RENDER_APIENTRY
renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx)
{
    WindowInfo *window = NULL;
    ContextInfo *context = NULL;

    /*
    crDebug("%s win=%d native=0x%x ctx=%d", __FUNCTION__, crWindow, (int) nativeWindow, ctx);
    */

    if (crWindow)
    {
        window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow);
        if (!window)
        {
            crWarning("invalid window %d specified", crWindow);
            return;
        }
    }

    if (ctx)
    {
        context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx);
        if (!context)
        {
            crWarning("invalid context %d specified", ctx);
            return;
        }
    }

    if (!context != !window)
    {
        crWarning("either window %d or context %d are zero", crWindow, ctx);
        return;
    }

    renderspuPerformMakeCurrent(window, nativeWindow, context);
}

GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id )
{
    crMemset(window, 0, sizeof (*window));
    RTCritSectInit(&window->CompositorLock);
    window->pCompositor = NULL;

    window->BltInfo.Base.id = id;

    window->x = render_spu.defaultX;
    window->y = render_spu.defaultY;
    window->BltInfo.width  = render_spu.defaultWidth;
    window->BltInfo.height = render_spu.defaultHeight;

    /* Set window->title, replacing %i with the window ID number */
    {
        const char *s = crStrstr(render_spu.window_title, "%i");
        if (s) {
            int i, j, k;
            window->title = crAlloc(crStrlen(render_spu.window_title) + 10);
            for (i = 0; render_spu.window_title[i] != '%'; i++)
                window->title[i] = render_spu.window_title[i];
            k = sprintf(window->title + i, "%d", window->BltInfo.Base.id);
            CRASSERT(k < 10);
            i++; /* skip the 'i' after the '%' */
            j = i + k;
            for (; (window->title[j] = s[i]) != 0; i++, j++)
                ;
        }
        else {
            window->title = crStrdup(render_spu.window_title);
        }
    }
        
    window->BltInfo.Base.visualBits = visual->visAttribs;
    
    window->cRefs = 1;
    
    /*
    crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id);
    */
    /* Have GLX/WGL/AGL create the window */
    if (!renderspu_SystemVBoxCreateWindow( visual, showIt, window ))
    {
        crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" );
        return GL_FALSE;
    }
    
    window->visible = !!showIt;

    CRASSERT(window->visual == visual);
    return GL_TRUE;
}

/*
 * Window functions
 */
GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id)
{
    VisualInfo *visual;

    crMemset(pWindow, 0, sizeof (*pWindow));

    if (!dpyName || crStrlen(render_spu.display_string) > 0)
        dpyName = render_spu.display_string;

    visual = renderspuFindVisual( dpyName, visBits );
    if (!visual)
    {
        crWarning( "Render SPU: Couldn't create a window, renderspuFindVisual returned NULL" );
        return GL_FALSE;
    }

    /*
    crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id);
    */
    /* Have GLX/WGL/AGL create the window */
    if (!renderspuWinInitWithVisual( pWindow, visual, 0, id ))
    {
        crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" );
        return GL_FALSE;
    }

    return GL_TRUE;
}

GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id )
{
    WindowInfo *window;

    if (id <= 0)
    {
        id = (GLint)crHashtableAllocKeys(render_spu.windowTable, 1);
        if (id <= 0)
        {
            crWarning("failed to allocate window id");
            return -1;
        }
    }
    else
    {
        if (crHashtableIsKeyUsed(render_spu.windowTable, id))
        {
            crWarning("the specified window key %d is in use", id);
            return -1;
        }
    }

    /* Allocate WindowInfo */
    window = renderspuWinCreate(visBits, id);

    if (!window)
    {
        crWarning("renderspuWinCreate failed");
        crFree(window);
        return -1;
    }

    crHashtableAdd(render_spu.windowTable, id, window);
    return window->BltInfo.Base.id;
}

GLint RENDER_APIENTRY
renderspuWindowCreate( const char *dpyName, GLint visBits )
{
    return renderspuWindowCreateEx( dpyName, visBits, 0 );
}

void renderspuWinReleaseCb(void*pvWindow)
{
    renderspuWinRelease((WindowInfo*)pvWindow);
}

void
RENDER_APIENTRY renderspuWindowDestroy( GLint win )
{
    WindowInfo *window;

    CRASSERT(win >= 0);
    if (win == CR_RENDER_DEFAULT_WINDOW_ID)
    {
        crWarning("request to destroy a default mural, ignoring");
        return;
    }
    window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
    if (window) {
        crDebug("Render SPU: Destroy window (%d)", win);
        /* since os-specific backend can hold its own reference to the window object (e.g. on OSX),
         * we need to explicitly issue a window destroy command
         * this ensures the backend will eventually release the reference,
         * the window object itself will remain valid until its ref count reaches zero */
        renderspuWinTerm( window );

        /* remove window info from hash table, and free it */
        crHashtableDelete(render_spu.windowTable, win, renderspuWinReleaseCb);

    }
    else {
        crDebug("Render SPU: Attempt to destroy invalid window (%d)", win);
    }
}


static void RENDER_APIENTRY
renderspuWindowSize( GLint win, GLint w, GLint h )
{
    WindowInfo *window;
    CRASSERT(win >= 0);
    window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
    if (window) {
        if (w != window->BltInfo.width
                || h != window->BltInfo.height)
        {
            /* window is resized, compositor data is no longer valid
             * this set also ensures all redraw operations are done in the redraw thread
             * and that no redraw is started until new Present request comes containing a valid presentation data */
            renderspuVBoxCompositorSet( window, NULL);
            renderspu_SystemWindowSize( window, w, h );
            window->BltInfo.width  = w;
            window->BltInfo.height = h;
        }
    }
    else {
        WARN(("Render SPU: Attempt to resize invalid window (%d)", win));
    }
}


static void RENDER_APIENTRY
renderspuWindowPosition( GLint win, GLint x, GLint y )
{
    if (!render_spu.ignore_window_moves) {
        WindowInfo *window;
        CRASSERT(win >= 0);
        window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
        if (window) {
            renderspu_SystemWindowPosition( window, x, y );
            window->x = x;
            window->y = y;
        }
        else {
            crDebug("Render SPU: Attempt to move invalid window (%d)", win);
        }
    }
}

#ifdef DEBUG_misha
# define CR_DBG_DUMP_VISIBLE_REGIONS
#endif

#ifdef CR_DBG_DUMP_VISIBLE_REGIONS
static void renderspuDbgDumpVisibleRegion(GLint win, GLint cRects, const GLint *pRects)
{
    GLint i;
    const RTRECT *pRtRects = (const RTRECT *)((const void*)pRects);

    crInfo("Window %d, Vidible Regions%d", win, cRects);
    for (i = 0; i < cRects; ++i)
    {
        crInfo("%d: (%d,%d), (%d,%d)", i, pRtRects[i].xLeft, pRtRects[i].yTop, pRtRects[i].xRight, pRtRects[i].yBottom);
    }
    crInfo("======");
}
#endif

static void RENDER_APIENTRY
renderspuWindowVisibleRegion(GLint win, GLint cRects, const GLint *pRects)
{
    WindowInfo *window;
    CRASSERT(win >= 0);

#ifdef CR_DBG_DUMP_VISIBLE_REGIONS
    renderspuDbgDumpVisibleRegion(win, cRects, pRects);
#endif

    window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
    if (window) {
        renderspu_SystemWindowVisibleRegion( window, cRects, pRects );
    }
    else {
        crWarning("Render SPU: Attempt to set VisibleRegion for invalid window (%d)", win);
    }
}

static void RENDER_APIENTRY
renderspuWindowShow( GLint win, GLint flag )
{
    WindowInfo *window;
    CRASSERT(win >= 0);
    window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
    if (window) {
        GLboolean visible;
        if (window->nativeWindow) {
            /* We're rendering back to the native app window instead of the
             * new window which we (the Render SPU) created earlier.
             * So, we never want to show the Render SPU's window.
             */
            flag = 0;
        }
        
        visible = !!flag;
        
//        if (window->visible != visible)
        {
            renderspu_SystemShowWindow( window, visible );
            window->visible = visible;
        }
    }
    else {
        crDebug("Render SPU: Attempt to hide/show invalid window (%d)", win);
    }
}

static void RENDER_APIENTRY
renderspuVBoxPresentComposition( GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry )
{
    WindowInfo *window;
    CRASSERT(win >= 0);
    window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win);
    if (window) {
        if (renderspuVBoxCompositorSet(window, pCompositor))
        {
            renderspu_SystemVBoxPresentComposition(window, pChangedEntry);
        }
    }
    else {
        crDebug("Render SPU: Attempt to PresentComposition for invalid window (%d)", win);
    }
}

void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY)
{
    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter;
    const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
    CrVrScrCompositorConstIterInit(pCompositor, &CIter);
    while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL)
    {
        uint32_t cRegions;
        const RTRECT *paSrcRegions, *paDstRegions;
        int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL);
        uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry);
        if (RT_SUCCESS(rc))
        {
            uint32_t i;
            for (i = 0; i < cRegions; ++i)
            {
                RTRECT DstRect;
                const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
                DstRect.xLeft = paDstRegions[i].xLeft * scaleX;
                DstRect.yTop = paDstRegions[i].yTop * scaleY;
                DstRect.xRight = paDstRegions[i].xRight * scaleX;
                DstRect.yBottom = paDstRegions[i].yBottom * scaleY;
                CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), &paSrcRegions[i], &DstRect, 1, fFlags);
            }
        }
        else
        {
            crWarning("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d", rc);
        }
    }
}

void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter)
{
    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter;
    const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
    CrVrScrCompositorConstIterInit(pCompositor, &CIter);
    while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL)
    {
        uint32_t cRegions;
        const RTRECT *paSrcRegions, *paDstRegions;
        int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL);
        uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry);
        if (RT_SUCCESS(rc))
        {
            const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry);
            CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), paSrcRegions, paDstRegions, cRegions, fFlags);
        }
        else
        {
            crWarning("Blit: CrVrScrCompositorEntryRegionsGet failed rc %d", rc);
        }
    }
}

void renderspuVBoxPresentBlitterCleanup( WindowInfo *window )
{
    if (!window->pBlitter)
        return;

    if (render_spu.blitterTable)
    {
        const CR_BLITTER_WINDOW * pBltInfo = CrBltMuralGetCurrentInfo(window->pBlitter);
        if (pBltInfo && pBltInfo->Base.id == window->BltInfo.Base.id)
        {
            CrBltMuralSetCurrentInfo(window->pBlitter, NULL);
        }
    }
    else
    {
        CRASSERT(CrBltMuralGetCurrentInfo(window->pBlitter)->Base.id == window->BltInfo.Base.id);
        CrBltMuralSetCurrentInfo(window->pBlitter, NULL);
        CrBltTerm(window->pBlitter);
    }
    window->pBlitter = NULL;
}

PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window )
{
    PCR_BLITTER pBlitter = window->pBlitter;
    if (!pBlitter)
    {
        if (render_spu.blitterTable)
        {
            crHashtableLock(render_spu.blitterTable);
            pBlitter = (PCR_BLITTER)crHashtableSearch(render_spu.blitterTable, window->visual->visAttribs);
        }

        if (!pBlitter)
        {
            int rc;
            ContextInfo * pDefaultCtxInfo;

            pBlitter = (PCR_BLITTER)crCalloc(sizeof (*pBlitter));
            if (!pBlitter)
            {
                crWarning("failed to allocate blitter");
                return NULL;
            }

            pDefaultCtxInfo = renderspuDefaultSharedContextAcquire();
            if (!pDefaultCtxInfo)
            {
                crWarning("no default ctx info!");
                crFree(pBlitter);
                return NULL;
            }

            rc = CrBltInit(pBlitter, &pDefaultCtxInfo->BltInfo, true, true, NULL, &render_spu.blitterDispatch);

            /* we can release it either way, since it will be retained when used as a shared context */
            renderspuDefaultSharedContextRelease(pDefaultCtxInfo);

            if (!RT_SUCCESS(rc))
            {
                crWarning("CrBltInit failed, rc %d", rc);
                crFree(pBlitter);
                return NULL;
            }

            if (render_spu.blitterTable)
            {
                crHashtableAdd( render_spu.blitterTable, window->visual->visAttribs, pBlitter );
            }
        }

        if (render_spu.blitterTable)
            crHashtableUnlock(render_spu.blitterTable);

        Assert(pBlitter);
        window->pBlitter = pBlitter;
    }

    CrBltMuralSetCurrentInfo(pBlitter, &window->BltInfo);
    return pBlitter;
}

int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData)
{
    int rc;

    CrBltSetMakeCurrentUserData(pBlitter, i32MakeCurrentUserData);

    rc = CrBltEnter(pBlitter);
    if (!RT_SUCCESS(rc))
    {
        crWarning("CrBltEnter failed, rc %d", rc);
        return rc;
    }
    return VINF_SUCCESS;
}

PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw )
{
    PCR_BLITTER pBlitter = fRedraw ? window->pBlitter : renderspuVBoxPresentBlitterGet(window);
    if (pBlitter)
    {
        int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData);
        if (RT_SUCCESS(rc))
        {
            return pBlitter;
        }
    }
    return NULL;
}

PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData )
{
    if (!window->pBlitter)
    {
        const struct VBOXVR_SCR_COMPOSITOR * pTmpCompositor;
        /* just use compositor lock to synchronize */
        pTmpCompositor = renderspuVBoxCompositorAcquire(window);
        CRASSERT(pTmpCompositor);
        if (pTmpCompositor)
        {
            PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGet( window );
            if (pBlitter)
            {
                if (!CrBltIsEverEntered(pBlitter))
                {
                    int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData);
                    if (RT_SUCCESS(rc))
                    {
                        CrBltLeave(pBlitter);
                    }
                    else
                    {
                        crWarning("renderspuVBoxPresentBlitterEnter failed rc %d", rc);
                    }
                }
            }
            else
            {
                crWarning("renderspuVBoxPresentBlitterGet failed");
            }

            renderspuVBoxCompositorRelease(window);
        }
        else
        {
            crWarning("renderspuVBoxCompositorAcquire failed");
        }
    }
    return window->pBlitter;
}

void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor,
        const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData,
        bool fRedraw )
{
    PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGetAndEnter(window, i32MakeCurrentUserData, fRedraw);
    if (!pBlitter)
        return;

    renderspuVBoxCompositorBlit(pCompositor, pBlitter);

    renderspu_SystemSwapBuffers(window, 0);

    CrBltLeave(pBlitter);
}

GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor)
{
    int rc;
    GLboolean fEmpty = pCompositor && CrVrScrCompositorIsEmpty(pCompositor);
    GLboolean fNeedPresent;

    /* renderspuVBoxCompositorSet can be invoked from the chromium thread only and is not reentrant,
     * no need to synch here
     * the lock is actually needed to ensure we're in synch with the redraw thread */
    if (window->pCompositor == pCompositor && !fEmpty)
        return !!pCompositor;

    rc = RTCritSectEnter(&window->CompositorLock);
    if (RT_SUCCESS(rc))
    {
        if (!fEmpty)
            fNeedPresent = !!pCompositor;
        else
        {
            fNeedPresent = renderspu_SystemWindowNeedEmptyPresent(window);
            pCompositor = NULL;
        }

        window->pCompositor = !fEmpty ? pCompositor : NULL;
        RTCritSectLeave(&window->CompositorLock);
        return fNeedPresent;
    }
    else
    {
        WARN(("RTCritSectEnter failed rc %d", rc));
    }

    return GL_FALSE;
}

static void renderspuVBoxCompositorClearAllCB(unsigned long key, void *data1, void *data2)
{
    WindowInfo *window = (WindowInfo *) data1;
    renderspuVBoxCompositorSet(window, NULL);
}

void renderspuVBoxCompositorClearAll()
{
    /* we need to clear window compositor, which is not that trivial though,
     * since the lock order used in presentation thread is compositor lock() -> hash table lock (aquired for id->window resolution)
     * this is why, to prevent potential deadlocks, we use crHashtableWalkUnlocked that does not hold the table lock
     * we are can be sure noone will modify the table here since renderspuVBoxCompositorClearAll can be called in the command (hgcm) thread only,
     * and the table can be modified from that thread only as well */
    crHashtableWalkUnlocked(render_spu.windowTable, renderspuVBoxCompositorClearAllCB, NULL);
}

const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window)
{
    int rc = RTCritSectEnter(&window->CompositorLock);
    if (RT_SUCCESS(rc))
    {
        const VBOXVR_SCR_COMPOSITOR * pCompositor = window->pCompositor;
        if (pCompositor)
        {
            Assert(!CrVrScrCompositorIsEmpty(window->pCompositor));
            return pCompositor;
        }

        /* if no compositor is set, release the lock and return */
        RTCritSectLeave(&window->CompositorLock);
    }
    else
    {
        crWarning("RTCritSectEnter failed rc %d", rc);
    }
    return NULL;
}

int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor)
{
    int rc = RTCritSectEnter(&window->CompositorLock);
    if (RT_SUCCESS(rc))
    {
        if (ppCompositor)
            *ppCompositor = window->pCompositor;
    }
    else
        WARN(("RTCritSectEnter failed %d", rc));
    return rc;
}

int renderspuVBoxCompositorUnlock(WindowInfo *window)
{
    int rc = RTCritSectLeave(&window->CompositorLock);
    AssertRC(rc);
    return rc;
}

int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor)
{
    int rc = RTCritSectTryEnter(&window->CompositorLock);
    if (RT_SUCCESS(rc))
    {
        *ppCompositor = window->pCompositor;
        if (*ppCompositor)
        {
            Assert(!CrVrScrCompositorIsEmpty(window->pCompositor));
            return VINF_SUCCESS;
        }

        /* if no compositor is set, release the lock and return */
        RTCritSectLeave(&window->CompositorLock);
        rc = VERR_INVALID_STATE;
    }
    else
    {
        *ppCompositor = NULL;
    }
    return rc;
}

void renderspuVBoxCompositorRelease( WindowInfo *window)
{
    int rc;
    Assert(window->pCompositor);
    Assert(!CrVrScrCompositorIsEmpty(window->pCompositor));
    rc = RTCritSectLeave(&window->CompositorLock);
    if (!RT_SUCCESS(rc))
    {
        crWarning("RTCritSectLeave failed rc %d", rc);
    }
}


/*
 * Set the current raster position to the given window coordinate.
 */
static void
SetRasterPos( GLint winX, GLint winY )
{
    GLfloat fx, fy;

    /* Push current matrix mode and viewport attributes */
    render_spu.self.PushAttrib( GL_TRANSFORM_BIT | GL_VIEWPORT_BIT );

    /* Setup projection parameters */
    render_spu.self.MatrixMode( GL_PROJECTION );
    render_spu.self.PushMatrix();
    render_spu.self.LoadIdentity();
    render_spu.self.MatrixMode( GL_MODELVIEW );
    render_spu.self.PushMatrix();
    render_spu.self.LoadIdentity();

    render_spu.self.Viewport( winX - 1, winY - 1, 2, 2 );

    /* set the raster (window) position */
    /* huh ? */
    fx = (GLfloat) (winX - (int) winX);
    fy = (GLfloat) (winY - (int) winY);
    render_spu.self.RasterPos4f( fx, fy, 0.0, 1.0 );

    /* restore matrices, viewport and matrix mode */
    render_spu.self.PopMatrix();
    render_spu.self.MatrixMode( GL_PROJECTION );
    render_spu.self.PopMatrix();

    render_spu.self.PopAttrib();
}


/*
 * Draw the mouse pointer bitmap at (x,y) in window coords.
 */
static void DrawCursor( GLint x, GLint y )
{
#define POINTER_WIDTH   32
#define POINTER_HEIGHT  32
    /* Somebody artistic could probably do better here */
    static const char *pointerImage[POINTER_HEIGHT] =
    {
        "XX..............................",
        "XXXX............................",
        ".XXXXX..........................",
        ".XXXXXXX........................",
        "..XXXXXXXX......................",
        "..XXXXXXXXXX....................",
        "...XXXXXXXXXXX..................",
        "...XXXXXXXXXXXXX................",
        "....XXXXXXXXXXXXXX..............",
        "....XXXXXXXXXXXXXXXX............",
        ".....XXXXXXXXXXXXXXXXX..........",
        ".....XXXXXXXXXXXXXXXXXXX........",
        "......XXXXXXXXXXXXXXXXXXXX......",
        "......XXXXXXXXXXXXXXXXXXXXXX....",
        ".......XXXXXXXXXXXXXXXXXXXXXXX..",
        ".......XXXXXXXXXXXXXXXXXXXXXXXX.",
        "........XXXXXXXXXXXXX...........",
        "........XXXXXXXX.XXXXX..........",
        ".........XXXXXX...XXXXX.........",
        ".........XXXXX.....XXXXX........",
        "..........XXX.......XXXXX.......",
        "..........XX.........XXXXX......",
        "......................XXXXX.....",
        ".......................XXXXX....",
        "........................XXX.....",
        ".........................X......",
        "................................",
        "................................",
        "................................",
        "................................",
        "................................",
        "................................"

    };
    static GLubyte pointerBitmap[POINTER_HEIGHT][POINTER_WIDTH / 8];
    static GLboolean firstCall = GL_TRUE;
    GLboolean lighting, depthTest, scissorTest;

    if (firstCall) {
        /* Convert pointerImage into pointerBitmap */
        GLint i, j;
        for (i = 0; i < POINTER_HEIGHT; i++) {
            for (j = 0; j < POINTER_WIDTH; j++) {
                if (pointerImage[POINTER_HEIGHT - i - 1][j] == 'X') {
                    GLubyte bit = 128 >> (j & 0x7);
                    pointerBitmap[i][j / 8] |= bit;
                }
            }
        }
        firstCall = GL_FALSE;
    }

    render_spu.self.GetBooleanv(GL_LIGHTING, &lighting);
    render_spu.self.GetBooleanv(GL_DEPTH_TEST, &depthTest);
    render_spu.self.GetBooleanv(GL_SCISSOR_TEST, &scissorTest);
    render_spu.self.Disable(GL_LIGHTING);
    render_spu.self.Disable(GL_DEPTH_TEST);
    render_spu.self.Disable(GL_SCISSOR_TEST);
    render_spu.self.PixelStorei(GL_UNPACK_ALIGNMENT, 1);

    render_spu.self.Color3f(1, 1, 1);

    /* save current raster pos */
    render_spu.self.PushAttrib(GL_CURRENT_BIT);
    SetRasterPos(x, y);
    render_spu.self.Bitmap(POINTER_WIDTH, POINTER_HEIGHT, 1.0, 31.0, 0, 0,
                                (const GLubyte *) pointerBitmap);
    /* restore current raster pos */
    render_spu.self.PopAttrib();

    if (lighting)
       render_spu.self.Enable(GL_LIGHTING);
    if (depthTest)
       render_spu.self.Enable(GL_DEPTH_TEST);
    if (scissorTest)
        render_spu.self.Enable(GL_SCISSOR_TEST);
}

void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags )
{
    WindowInfo *w = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window);

    if (!w)
    {
        crDebug("Render SPU: SwapBuffers invalid window id: %d", window);
        return;
    }

    if (flags & CR_SUPPRESS_SWAP_BIT)
    {
        render_spu.self.Finish();
        return;
    }

    if (render_spu.drawCursor)
        DrawCursor( render_spu.cursorX, render_spu.cursorY );

    if (render_spu.swap_master_url)
        DoSync();

    renderspu_SystemSwapBuffers( w, flags );
}


/*
 * Barrier functions
 * Normally, we'll have a crserver somewhere that handles the barrier calls.
 * However, if we're running the render SPU on the client node, then we
 * should handle barriers here.  The threadtest demo illustrates this.
 * If we have N threads calling using this SPU we need these barrier
 * functions to synchronize them.
 */

static void RENDER_APIENTRY renderspuBarrierCreateCR( GLuint name, GLuint count )
{
    Barrier *b;

    if (render_spu.ignore_papi)
        return;

    b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name );
    if (b) {
        /* HACK -- this allows everybody to create a barrier, and all
           but the first creation are ignored, assuming the count
           match. */
        if ( b->count != count ) {
            crError( "Render SPU: Barrier name=%u created with count=%u, but already "
                     "exists with count=%u", name, count, b->count );
        }
    }
    else {
        b = (Barrier *) crAlloc( sizeof(Barrier) );
        b->count = count;
        crInitBarrier( &b->barrier, count );
        crHashtableAdd( render_spu.barrierHash, name, b );
    }
}

static void RENDER_APIENTRY renderspuBarrierDestroyCR( GLuint name )
{
    if (render_spu.ignore_papi)
        return;
    crHashtableDelete( render_spu.barrierHash, name, crFree );
}

static void RENDER_APIENTRY renderspuBarrierExecCR( GLuint name )
{
    Barrier *b;

    if (render_spu.ignore_papi)
        return;

    b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name );
    if (b) {
        crWaitBarrier( &(b->barrier) );
    }
    else {
        crWarning("Render SPU: Bad barrier name %d in BarrierExec()", name);
    }
}


/*
 * Semaphore functions
 * XXX we should probably implement these too, for the same reason as
 * barriers (see above).
 */

static void RENDER_APIENTRY renderspuSemaphoreCreateCR( GLuint name, GLuint count )
{
    (void) name;
    (void) count;
}

static void RENDER_APIENTRY renderspuSemaphoreDestroyCR( GLuint name )
{
    (void) name;
}

static void RENDER_APIENTRY renderspuSemaphorePCR( GLuint name )
{
    (void) name;
}

static void RENDER_APIENTRY renderspuSemaphoreVCR( GLuint name )
{
    (void) name;
}


/*
 * Misc functions
 */
void renderspuSetDefaultSharedContext(ContextInfo *pCtx)
{
    if (pCtx == render_spu.defaultSharedContext)
        return;

    renderspu_SystemDefaultSharedContextChanged(render_spu.defaultSharedContext, pCtx);

    if (render_spu.defaultSharedContext)
        renderspuContextRelease(render_spu.defaultSharedContext);

    if (pCtx)
        renderspuContextRetain(pCtx);
    render_spu.defaultSharedContext = pCtx;
}

static void RENDER_APIENTRY renderspuChromiumParameteriCR(GLenum target, GLint value)
{
    switch (target)
    {
        case GL_HH_SET_DEFAULT_SHARED_CTX:
        {
            ContextInfo * pCtx = NULL;
            if (value)
                pCtx = (ContextInfo *)crHashtableSearch(render_spu.contextTable, value);
            else
                crWarning("invalid default shared context id %d", value);

            renderspuSetDefaultSharedContext(pCtx);
            break;
        }
        case GL_HH_RENDERTHREAD_INFORM:
        {
            if (value)
            {
                int rc = renderspuDefaultCtxInit();
                if (RT_FAILURE(rc))
                {
                    WARN(("renderspuDefaultCtxInit failed"));
                    break;
                }
            }
            else
            {
                renderspuCleanupBase(false);
            }
            break;
        }
        default:
//            crWarning("Unhandled target in renderspuChromiumParameteriCR()");
            break;
    }
}

static void RENDER_APIENTRY
renderspuChromiumParameterfCR(GLenum target, GLfloat value)
{
    (void) target;
    (void) value;

#if 0
    switch (target) {
    default:
        crWarning("Unhandled target in renderspuChromiumParameterfCR()");
        break;
    }
#endif
}

bool renderspuCalloutAvailable()
{
    return render_spu.pfnClientCallout != NULL;
}

bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb)
{
    if (render_spu.pfnClientCallout)
    {
        render_spu.pfnClientCallout(pfnCb, pvCb);
        return true;
    }
    return false;
}

static void RENDER_APIENTRY
renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count,
                                                            const GLvoid *values)
{
    int client_num;
    unsigned short port;
    CRMessage *msg, pingback;
    unsigned char *privbuf = NULL;

    switch (target) {
        case GL_HH_SET_CLIENT_CALLOUT:
            render_spu.pfnClientCallout = (PFNVCRSERVER_CLIENT_CALLOUT)values;
            break;
        case GL_GATHER_CONNECT_CR:
            if (render_spu.gather_userbuf_size)
                privbuf = (unsigned char *)crAlloc(1024*768*4);

            port = ((GLint *) values)[0];

            if (render_spu.gather_conns == NULL)
                render_spu.gather_conns = crAlloc(render_spu.server->numClients*sizeof(CRConnection *));
            else
            {
                crError("Oh bother! duplicate GL_GATHER_CONNECT_CR getting through");
            }

            for (client_num=0; client_num< render_spu.server->numClients; client_num++)
            {
                switch (render_spu.server->clients[client_num]->conn->type)
                {
                    case CR_TCPIP:
                        crDebug("Render SPU: AcceptClient from %s on %d",
                            render_spu.server->clients[client_num]->conn->hostname, render_spu.gather_port);
                        render_spu.gather_conns[client_num] =
                                crNetAcceptClient("tcpip", NULL, port, 1024*1024,  1);
                        break;

                    case CR_GM:
                        render_spu.gather_conns[client_num] =
                                crNetAcceptClient("gm", NULL, port, 1024*1024,  1);
                        break;

                    default:
                        crError("Render SPU: Unknown Network Type to Open Gather Connection");
                }


                if (render_spu.gather_userbuf_size)
                {
                    render_spu.gather_conns[client_num]->userbuf = privbuf;
                    render_spu.gather_conns[client_num]->userbuf_len = render_spu.gather_userbuf_size;
                }
                else
                {
                    render_spu.gather_conns[client_num]->userbuf = NULL;
                    render_spu.gather_conns[client_num]->userbuf_len = 0;
                }

                if (render_spu.gather_conns[client_num])
                {
                    crDebug("Render SPU: success! from %s", render_spu.gather_conns[client_num]->hostname);
                }
            }

            break;

    case GL_GATHER_DRAWPIXELS_CR:
        pingback.header.type = CR_MESSAGE_OOB;

        for (client_num=0; client_num< render_spu.server->numClients; client_num++)
        {
            crNetGetMessage(render_spu.gather_conns[client_num], &msg);
            if (msg->header.type == CR_MESSAGE_GATHER)
            {
                crNetFree(render_spu.gather_conns[client_num], msg);
            }
            else
            {
                crError("Render SPU: expecting MESSAGE_GATHER. got crap! (%d of %d)",
                                client_num, render_spu.server->numClients-1);
            }
        }

        /*
         * We're only hitting the case if we're not actually calling
         * child.SwapBuffers from readback, so a switch about which
         * call to DoSync() we really want [this one, or the one
         * in SwapBuffers above] is not necessary -- karl
         */

        if (render_spu.swap_master_url)
            DoSync();

        for (client_num=0; client_num< render_spu.server->numClients; client_num++)
            crNetSend(render_spu.gather_conns[client_num], NULL, &pingback,
                                        sizeof(CRMessageHeader));

        render_spu.self.RasterPos2i(((GLint *)values)[0], ((GLint *)values)[1]);
        render_spu.self.DrawPixels(  ((GLint *)values)[2], ((GLint *)values)[3],
                                        ((GLint *)values)[4], ((GLint *)values)[5],
                                    render_spu.gather_conns[0]->userbuf);


        render_spu.self.SwapBuffers(((GLint *)values)[6], 0);
        break;

    case GL_CURSOR_POSITION_CR:
        if (type == GL_INT && count == 2) {
            render_spu.cursorX = ((GLint *) values)[0];
            render_spu.cursorY = ((GLint *) values)[1];
            crDebug("Render SPU: GL_CURSOR_POSITION_CR (%d, %d)", render_spu.cursorX, render_spu.cursorY);
        }
        else {
            crWarning("Render SPU: Bad type or count for ChromiumParametervCR(GL_CURSOR_POSITION_CR)");
        }
        break;

    case GL_WINDOW_SIZE_CR:
        /* XXX this is old code that should be removed.
         * NOTE: we can only resize the default (id=CR_RENDER_DEFAULT_WINDOW_ID) window!!!
         */
        {
            GLint w, h;
            WindowInfo *window;
            CRASSERT(type == GL_INT);
            CRASSERT(count == 2);
            CRASSERT(values);
            w = ((GLint*)values)[0];
            h = ((GLint*)values)[1];
            window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID);
            if (window)
            {
                renderspu_SystemWindowSize(window, w, h);
            }
        }
        break;

    case GL_HH_SET_TMPCTX_MAKE_CURRENT:
    	if (type == GL_BYTE && count == sizeof (void*))
    		memcpy(&render_spu.blitterDispatch.MakeCurrent, values, count);
    	else
    		WARN(("unexpected type(%#x) - count(%d) pair", type, count));
    	break;

    default:
#if 0
        WARN(("Unhandled target in renderspuChromiumParametervCR(0x%x)", (int) target));
#endif
        break;
    }
}


static void RENDER_APIENTRY
renderspuGetChromiumParametervCR(GLenum target, GLuint index, GLenum type,
                                                                 GLsizei count, GLvoid *values)
{
    switch (target) {
    case GL_WINDOW_SIZE_CR:
        {
            GLint x, y, w, h, *size = (GLint *) values;
            WindowInfo *window;
            CRASSERT(type == GL_INT);
            CRASSERT(count == 2);
            CRASSERT(values);
            size[0] = size[1] = 0;  /* default */
            window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
            if (window)
            {
                renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h);
                size[0] = w;
                size[1] = h;
            }
        }
        break;
    case GL_WINDOW_POSITION_CR:
        /* return window position, as a screen coordinate */
        {
            GLint *pos = (GLint *) values;
            GLint x, y, w, h;
            WindowInfo *window;
            CRASSERT(type == GL_INT);
            CRASSERT(count == 2);
            CRASSERT(values);
            pos[0] = pos[1] = 0;  /* default */
            window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
            if (window)
            {
                renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h);
                pos[0] = x;/*window->x;*/
                pos[1] = y;/*window->y;*/
            }
        }
        break;
    case GL_MAX_WINDOW_SIZE_CR:
        {
            GLint *maxSize = (GLint *) values;
            WindowInfo *window;
            CRASSERT(type == GL_INT);
            CRASSERT(count == 2);
            CRASSERT(values);
            window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
            if (window)
            {
                renderspu_SystemGetMaxWindowSize(window, maxSize + 0, maxSize + 1);
            }
        }
        break;
    case GL_WINDOW_VISIBILITY_CR:
        {
            GLint *vis = (GLint *) values;
            WindowInfo *window;
            CRASSERT(type == GL_INT);
            CRASSERT(count == 1);
            CRASSERT(values);
            vis[0] = 0;  /* default */
            window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index);
            if (window)
            {
                vis[0] = window->visible;
            }
        }
        break;
    default:
        ; /* nothing - silence compiler */
    }
}


static void RENDER_APIENTRY
renderspuBoundsInfoCR( CRrecti *bounds, GLbyte *payload, GLint len,
                                             GLint num_opcodes )
{
    (void) bounds;
    (void) payload;
    (void) len;
    (void) num_opcodes;
    /* draw the bounding box */
    if (render_spu.draw_bbox) {
        GET_CONTEXT(context);
        WindowInfo *window = context->currentWindow;
        GLint x, y, w, h;

        renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h);

        render_spu.self.PushMatrix();
        render_spu.self.LoadIdentity();
        render_spu.self.MatrixMode(GL_PROJECTION);
        render_spu.self.PushMatrix();
        render_spu.self.LoadIdentity();
        render_spu.self.Ortho(0, w, 0, h, -1, 1);
        render_spu.self.Color3f(1, 1, 1);
        render_spu.self.Begin(GL_LINE_LOOP);
        render_spu.self.Vertex2i(bounds->x1, bounds->y1);
        render_spu.self.Vertex2i(bounds->x2, bounds->y1);
        render_spu.self.Vertex2i(bounds->x2, bounds->y2);
        render_spu.self.Vertex2i(bounds->x1, bounds->y2);
        render_spu.self.End();
        render_spu.self.PopMatrix();
        render_spu.self.MatrixMode(GL_MODELVIEW);
        render_spu.self.PopMatrix();
    }
}


static void RENDER_APIENTRY
renderspuWriteback( GLint *writeback )
{
    (void) writeback;
}


static void
remove_trailing_space(char *s)
{
    int k = crStrlen(s);
    while (k > 0 && s[k-1] == ' ')
        k--;
    s[k] = 0;
}

static const GLubyte * RENDER_APIENTRY
renderspuGetString(GLenum pname)
{
    static char tempStr[1000];
    GET_CONTEXT(context);

    if (pname == GL_EXTENSIONS)
    {
        const char *nativeExt;
        char *crExt, *s1, *s2;

        if (!render_spu.ws.glGetString)
            return NULL;

        nativeExt = (const char *) render_spu.ws.glGetString(GL_EXTENSIONS);
        if (!nativeExt) {
            /* maybe called w/out current context. */
            return NULL;
        }

        if (!context)
            return (const GLubyte *)nativeExt;

        crExt = crStrjoin3(crExtensions, " ", crAppOnlyExtensions);
        s1 = crStrIntersect(nativeExt, crExt);
        remove_trailing_space(s1);
        s2 = crStrjoin3(s1, " ", crChromiumExtensions);
        remove_trailing_space(s2);
        crFree(crExt);
        crFree(s1);
        if (context->extensionString)
            crFree(context->extensionString);
        context->extensionString = s2;
        return (const GLubyte *) s2;
    }
    else if (pname == GL_VENDOR)
        return (const GLubyte *) CR_VENDOR;
    else if (pname == GL_VERSION)
        return render_spu.ws.glGetString(GL_VERSION);
    else if (pname == GL_RENDERER) {
#ifdef VBOX
        snprintf(tempStr, sizeof(tempStr), "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER));
#else
        sprintf(tempStr, "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER));
#endif
        return (const GLubyte *) tempStr;
    }
#ifdef CR_OPENGL_VERSION_2_0
    else if (pname == GL_SHADING_LANGUAGE_VERSION)
        return render_spu.ws.glGetString(GL_SHADING_LANGUAGE_VERSION);
#endif
#ifdef GL_CR_real_vendor_strings
    else if (pname == GL_REAL_VENDOR)
        return render_spu.ws.glGetString(GL_VENDOR);
    else if (pname == GL_REAL_VERSION)
        return render_spu.ws.glGetString(GL_VERSION);
    else if (pname == GL_REAL_RENDERER)
        return render_spu.ws.glGetString(GL_RENDERER);
    else if (pname == GL_REAL_EXTENSIONS)
        return render_spu.ws.glGetString(GL_EXTENSIONS);
#endif
    else
        return NULL;
}

static void renderspuReparentWindowCB(unsigned long key, void *data1, void *data2)
{
    WindowInfo *pWindow = (WindowInfo *)data1;

    renderspu_SystemReparentWindow(pWindow);
}

DECLEXPORT(void) renderspuReparentWindow(GLint window)
{
    WindowInfo *pWindow;
    CRASSERT(window >= 0);

    pWindow = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window);

    if (!pWindow)
    {
        crDebug("Render SPU: Attempt to reparent invalid window (%d)", window);
        return;
    }

    renderspu_SystemReparentWindow(pWindow);

    /* special case: reparent all internal windows as well */
    if (window == CR_RENDER_DEFAULT_WINDOW_ID)
    {
        crHashtableWalk(render_spu.dummyWindowTable, renderspuReparentWindowCB, NULL);
    }
}

#define FILLIN( NAME, FUNC ) \
  table[i].name = crStrdup(NAME); \
  table[i].fn = (SPUGenericFunction) FUNC; \
  i++;


/* These are the functions which the render SPU implements, not OpenGL.
 */
int
renderspuCreateFunctions(SPUNamedFunctionTable table[])
{
    int i = 0;
    FILLIN( "SwapBuffers", renderspuSwapBuffers );
    FILLIN( "CreateContext", renderspuCreateContext );
    FILLIN( "DestroyContext", renderspuDestroyContext );
    FILLIN( "MakeCurrent", renderspuMakeCurrent );
    FILLIN( "WindowCreate", renderspuWindowCreate );
    FILLIN( "WindowDestroy", renderspuWindowDestroy );
    FILLIN( "WindowSize", renderspuWindowSize );
    FILLIN( "WindowPosition", renderspuWindowPosition );
    FILLIN( "WindowVisibleRegion", renderspuWindowVisibleRegion );
    FILLIN( "WindowShow", renderspuWindowShow );
    FILLIN( "BarrierCreateCR", renderspuBarrierCreateCR );
    FILLIN( "BarrierDestroyCR", renderspuBarrierDestroyCR );
    FILLIN( "BarrierExecCR", renderspuBarrierExecCR );
    FILLIN( "BoundsInfoCR", renderspuBoundsInfoCR );
    FILLIN( "SemaphoreCreateCR", renderspuSemaphoreCreateCR );
    FILLIN( "SemaphoreDestroyCR", renderspuSemaphoreDestroyCR );
    FILLIN( "SemaphorePCR", renderspuSemaphorePCR );
    FILLIN( "SemaphoreVCR", renderspuSemaphoreVCR );
    FILLIN( "Writeback", renderspuWriteback );
    FILLIN( "ChromiumParameteriCR", renderspuChromiumParameteriCR );
    FILLIN( "ChromiumParameterfCR", renderspuChromiumParameterfCR );
    FILLIN( "ChromiumParametervCR", renderspuChromiumParametervCR );
    FILLIN( "GetChromiumParametervCR", renderspuGetChromiumParametervCR );
    FILLIN( "GetString", renderspuGetString );
    FILLIN( "VBoxPresentComposition", renderspuVBoxPresentComposition );
    return i;
}
