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

/**
 * \mainpage OpenGL_stub 
 *
 * \section OpenGL_stubIntroduction Introduction
 *
 * Chromium consists of all the top-level files in the cr
 * directory.  The OpenGL_stub module basically takes care of API dispatch,
 * and OpenGL state management.
 *
 */

/**
 * This file manages OpenGL rendering contexts in the faker library.
 * The big issue is switching between Chromium and native GL context
 * management.  This is where we support multiple client OpenGL
 * windows.  Typically, one window is handled by Chromium while any
 * other windows are handled by the native OpenGL library.
 */

#include "chromium.h"
#include "cr_error.h"
#include "cr_spu.h"
#include "cr_mem.h"
#include "cr_string.h"
#include "cr_environment.h"
#include "stub.h"

/**
 * This function should be called from MakeCurrent().  It'll detect if
 * we're in a multi-thread situation, and do the right thing for dispatch.
 */
#ifdef CHROMIUM_THREADSAFE
    static void
stubCheckMultithread( void )
{
    static unsigned long knownID;
    static GLboolean firstCall = GL_TRUE;

    if (stub.threadSafe)
    return;  /* nothing new, nothing to do */

    if (firstCall) {
    knownID = crThreadID();
    firstCall = GL_FALSE;
    }
    else if (knownID != crThreadID()) {
    /* going thread-safe now! */
    stub.threadSafe = GL_TRUE;
    crSPUCopyDispatchTable(&glim, &stubThreadsafeDispatch);
    }
}
#endif


/**
 * Install the given dispatch table as the table used for all gl* calls.
 */
    static void
stubSetDispatch( SPUDispatchTable *table )
{
    CRASSERT(table);

#ifdef CHROMIUM_THREADSAFE
    /* always set the per-thread dispatch pointer */
    crSetTSD(&stub.dispatchTSD, (void *) table);
    if (stub.threadSafe) {
    /* Do nothing - the thread-safe dispatch functions will call GetTSD()
     * to get a pointer to the dispatch table, and jump through it.
     */
    }
    else 
#endif
    {
    /* Single thread mode - just install the caller's dispatch table */
    /* This conditional is an optimization to try to avoid unnecessary
     * copying.  It seems to work with atlantis, multiwin, etc. but
     * _could_ be a problem. (Brian)
     */
    if (glim.copy_of != table->copy_of)
        crSPUCopyDispatchTable(&glim, table);
    }
}

void stubForcedFlush(GLint con)
{
#if 0
    GLint buffer;
    stub.spu->dispatch_table.GetIntegerv(GL_DRAW_BUFFER, &buffer);
    stub.spu->dispatch_table.DrawBuffer(GL_FRONT);
    stub.spu->dispatch_table.Flush();
    stub.spu->dispatch_table.DrawBuffer(buffer);
#else
    if (con)
    {
        stub.spu->dispatch_table.VBoxConFlush(con);
    }
    else
    {
        stub.spu->dispatch_table.Flush();
    }
#endif
}

void stubConChromiumParameteriCR(GLint con, GLenum param, GLint value)
{
//    if (con)
        stub.spu->dispatch_table.VBoxConChromiumParameteriCR(con, param, value);
//    else
//        crError("VBoxConChromiumParameteriCR called with null connection");
}

void stubConChromiumParametervCR(GLint con, GLenum target, GLenum type, GLsizei count, const GLvoid *values)
{
//    if (con)
        stub.spu->dispatch_table.VBoxConChromiumParametervCR(con, target, type, count, values);
//    else
//        crError("VBoxConChromiumParameteriCR called with null connection");
}

void stubConFlush(GLint con)
{
    if (con)
        stub.spu->dispatch_table.VBoxConFlush(con);
    else
        crError("stubConFlush called with null connection");
}

static void stubWindowCleanupForContextsCB(unsigned long key, void *data1, void *data2)
{
    ContextInfo *context = (ContextInfo *) data1;

    CRASSERT(context);

    if (context->currentDrawable == data2)
        context->currentDrawable = NULL;
}

void stubDestroyWindow( GLint con, GLint window )
{
    WindowInfo *winInfo = (WindowInfo *)
        crHashtableSearch(stub.windowTable, (unsigned int) window);
    if (winInfo && winInfo->type == CHROMIUM && stub.spu)
    {
        crHashtableLock(stub.windowTable);

        stub.spu->dispatch_table.VBoxWindowDestroy(con, winInfo->spuWindow );

#ifdef WINDOWS
        if (winInfo->hVisibleRegion != INVALID_HANDLE_VALUE)
        {
            DeleteObject(winInfo->hVisibleRegion);
        }
#elif defined(GLX)
        if (winInfo->pVisibleRegions)
        {
            XFree(winInfo->pVisibleRegions);
        }
# ifdef CR_NEWWINTRACK
        if (winInfo->syncDpy)
        {
            XCloseDisplay(winInfo->syncDpy);
        }
# endif
#endif

        stubForcedFlush(con);

        crHashtableWalk(stub.contextTable, stubWindowCleanupForContextsCB, winInfo);

        crHashtableDelete(stub.windowTable, window, crFree);

        crHashtableUnlock(stub.windowTable);
    }
}

/**
 * Create a new _Chromium_ window, not GLX, WGL or CGL.
 * Called by crWindowCreate() only.
 */
    GLint
stubNewWindow( const char *dpyName, GLint visBits )
{
    WindowInfo *winInfo;
    GLint spuWin, size[2];

    spuWin = stub.spu->dispatch_table.WindowCreate( dpyName, visBits );
    if (spuWin < 0) {
    return -1;
    }

    winInfo = (WindowInfo *) crCalloc(sizeof(WindowInfo));
    if (!winInfo) {
    stub.spu->dispatch_table.WindowDestroy(spuWin);
    return -1;
    }

    winInfo->type = CHROMIUM;

    /* Ask the head SPU for the initial window size */
    size[0] = size[1] = 0;
    stub.spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, size);
    if (size[0] == 0 && size[1] == 0) {
    /* use some reasonable defaults */
    size[0] = size[1] = 512;
    }
    winInfo->width = size[0];
    winInfo->height = size[1];
#ifdef VBOX_WITH_WDDM
    if (stub.bRunningUnderWDDM)
    {
        crError("Should not be here: WindowCreate/Destroy & VBoxPackGetInjectID require connection id!");
        winInfo->mapped = 0;
    }
    else
#endif
    {
        winInfo->mapped = 1;
    }

    if (!dpyName)
    dpyName = "";

    crStrncpy(winInfo->dpyName, dpyName, MAX_DPY_NAME);
    winInfo->dpyName[MAX_DPY_NAME-1] = 0;

    /* Use spuWin as the hash table index and GLX/WGL handle */
#ifdef WINDOWS
    winInfo->drawable = (HDC) spuWin;
    winInfo->hVisibleRegion = INVALID_HANDLE_VALUE;
#elif defined(Darwin)
    winInfo->drawable = (CGSWindowID) spuWin;
#elif defined(GLX)
    winInfo->drawable = (GLXDrawable) spuWin;
    winInfo->pVisibleRegions = NULL;
    winInfo->cVisibleRegions = 0;
#endif
#ifdef CR_NEWWINTRACK
    winInfo->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(0);
#endif
    winInfo->spuWindow = spuWin;

    crHashtableAdd(stub.windowTable, (unsigned int) spuWin, winInfo);

    return spuWin;
}

#ifdef GLX
static XErrorHandler oldErrorHandler;
static unsigned char lastXError = Success;

static int 
errorHandler (Display *dpy, XErrorEvent *e)
{
    lastXError = e->error_code;
    return 0;
}
#endif

GLboolean
stubIsWindowVisible(WindowInfo *win)
{
#if defined(WINDOWS)
# ifdef VBOX_WITH_WDDM
    if (stub.bRunningUnderWDDM)
        return win->mapped;
# endif
    return GL_TRUE;
#elif defined(Darwin)
    return GL_TRUE;
#elif defined(GLX)
    Display *dpy = stubGetWindowDisplay(win);
    if (dpy) 
    {
        XWindowAttributes attr;
        XLOCK(dpy);
        XGetWindowAttributes(dpy, win->drawable, &attr);
        XUNLOCK(dpy);

        if (attr.map_state == IsUnmapped)
        {
            return GL_FALSE;
        }
# if 1
        return GL_TRUE;
# else
        if (attr.override_redirect)
        {
            return GL_TRUE;
        }

        if (!stub.bXExtensionsChecked)
        {
            stubCheckXExtensions(win);
        }

        if (!stub.bHaveXComposite)
        {
            return GL_TRUE;
        }
        else
        {
            Pixmap p;

            crLockMutex(&stub.mutex);

            XLOCK(dpy);
            XSync(dpy, false);
            oldErrorHandler = XSetErrorHandler(errorHandler);
            /*@todo this will create new pixmap for window every call*/
            p = XCompositeNameWindowPixmap(dpy, win->drawable);
            XSync(dpy, false);
            XSetErrorHandler(oldErrorHandler);
            XUNLOCK(dpy);

            switch (lastXError)
            {
                case Success:
                    XFreePixmap(dpy, p);
                    crUnlockMutex(&stub.mutex);
                    return GL_FALSE;
                    break;
                case BadMatch:
                    /*Window isn't redirected*/
                    lastXError = Success;
                    break;
                default:
                    crWarning("Unexpected XError %i", (int)lastXError);
                    lastXError = Success;
            }

            crUnlockMutex(&stub.mutex);

            return GL_TRUE;
        }
# endif
    }
    else {
        /* probably created by crWindowCreate() */
        return win->mapped;
    }
#endif
}


/**
 * Given a Windows HDC or GLX Drawable, return the corresponding
 * WindowInfo structure.  Create a new one if needed.
 */
WindowInfo *
#ifdef WINDOWS
    stubGetWindowInfo( HDC drawable )
#elif defined(Darwin)
    stubGetWindowInfo( CGSWindowID drawable )
#elif defined(GLX)
stubGetWindowInfo( Display *dpy, GLXDrawable drawable )
#endif
{
#ifndef WINDOWS
    WindowInfo *winInfo = (WindowInfo *) crHashtableSearch(stub.windowTable, (unsigned int) drawable);
#else
    WindowInfo *winInfo;
    HWND hwnd;
    hwnd = WindowFromDC(drawable);

    if (!hwnd)
    {
        return NULL;
    }

    winInfo = (WindowInfo *) crHashtableSearch(stub.windowTable, (unsigned int) hwnd);
#endif
    if (!winInfo) {
    winInfo = (WindowInfo *) crCalloc(sizeof(WindowInfo));
    if (!winInfo)
        return NULL;
#ifdef GLX
    crStrncpy(winInfo->dpyName, DisplayString(dpy), MAX_DPY_NAME);
    winInfo->dpyName[MAX_DPY_NAME-1] = 0;
    winInfo->dpy = dpy;
    winInfo->pVisibleRegions = NULL;
#elif defined(Darwin)
    winInfo->connection = _CGSDefaultConnection(); // store our connection as default
#elif defined(WINDOWS)
    winInfo->hVisibleRegion = INVALID_HANDLE_VALUE;
    winInfo->hWnd = hwnd;
#endif
    winInfo->drawable = drawable;
    winInfo->type = UNDECIDED;
    winInfo->spuWindow = -1;
#ifdef VBOX_WITH_WDDM
    if (stub.bRunningUnderWDDM)
        winInfo->mapped = 0;
    else
#endif
    {
        winInfo->mapped = -1; /* don't know */
    }
    winInfo->pOwner = NULL;
#ifdef CR_NEWWINTRACK
    winInfo->u32ClientID = -1;
#endif
#ifndef WINDOWS
    crHashtableAdd(stub.windowTable, (unsigned int) drawable, winInfo);
#else
    crHashtableAdd(stub.windowTable, (unsigned int) hwnd, winInfo);
#endif
    }
#ifdef WINDOWS
    else
    {
        winInfo->drawable = drawable;
    }
#endif
    return winInfo;
}

static void stubWindowCheckOwnerCB(unsigned long key, void *data1, void *data2);

static void
stubContextFree( ContextInfo *context )
{
    crMemZero(context, sizeof(ContextInfo));  /* just to be safe */
    crFree(context);
}

static void
stubDestroyContextLocked( ContextInfo *context )
{
    unsigned long contextId = context->id;
    if (context->type == NATIVE) {
#ifdef WINDOWS
        stub.wsInterface.wglDeleteContext( context->hglrc );
#elif defined(Darwin)
        stub.wsInterface.CGLDestroyContext( context->cglc );
#elif defined(GLX)
        stub.wsInterface.glXDestroyContext( context->dpy, context->glxContext );
#endif
    }
    else if (context->type == CHROMIUM) {
        /* Have pack SPU or tilesort SPU, etc. destroy the context */
        CRASSERT(context->spuContext >= 0);
        stub.spu->dispatch_table.DestroyContext( context->spuContext );
        crHashtableWalk(stub.windowTable, stubWindowCheckOwnerCB, context);
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
        if (context->spuConnection)
        {
            stub.spu->dispatch_table.VBoxConDestroy(context->spuConnection);
            context->spuConnection = 0;
        }
#endif
    }

#ifdef GLX
    crFreeHashtable(context->pGLXPixmapsHash, crFree);
#endif

    crHashtableDelete(stub.contextTable, contextId, NULL);
}

#ifdef CHROMIUM_THREADSAFE
static DECLCALLBACK(void) stubContextDtor(void*pvContext)
{
    stubContextFree((ContextInfo*)pvContext);
}
#endif

/**
 * Allocate a new ContextInfo object, initialize it, put it into the
 * context hash table.  If type==CHROMIUM, call the head SPU's
 * CreateContext() function too.
 */
    ContextInfo *
stubNewContext( const char *dpyName, GLint visBits, ContextType type,
    unsigned long shareCtx
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
        , struct VBOXUHGSMI *pHgsmi
#endif
    )
{
    GLint spuContext = -1, spuShareCtx = 0, spuConnection = 0;
    ContextInfo *context;

    if (shareCtx > 0) {
    /* translate shareCtx to a SPU context ID */
    context = (ContextInfo *)
        crHashtableSearch(stub.contextTable, shareCtx);
        if (context)
            spuShareCtx = context->spuContext;
    }

    if (type == CHROMIUM) {
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
        if (pHgsmi)
        {
            spuConnection = stub.spu->dispatch_table.VBoxConCreate(pHgsmi);
            if (!spuConnection)
            {
                crWarning("VBoxConCreate failed");
                return NULL;
            }
        }
#endif
        spuContext
            = stub.spu->dispatch_table.VBoxCreateContext(spuConnection, dpyName, visBits, spuShareCtx);
        if (spuContext < 0)
        {
            crWarning("VBoxCreateContext failed");
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
            if (spuConnection)
                stub.spu->dispatch_table.VBoxConDestroy(spuConnection);
#endif
            return NULL;
        }
    }

    context = crCalloc(sizeof(ContextInfo));
    if (!context) {
        stub.spu->dispatch_table.DestroyContext(spuContext);
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
        if (spuConnection)
            stub.spu->dispatch_table.VBoxConDestroy(spuConnection);
#endif
        return NULL;
    }

    if (!dpyName)
        dpyName = "";

    context->id = stub.freeContextNumber++;
    context->type = type;
    context->spuContext = spuContext;
    context->visBits = visBits;
    context->currentDrawable = NULL;
    crStrncpy(context->dpyName, dpyName, MAX_DPY_NAME);
    context->dpyName[MAX_DPY_NAME-1] = 0;

#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
    context->spuConnection = spuConnection;
    context->pHgsmi = pHgsmi;
#endif

#ifdef CHROMIUM_THREADSAFE
    VBoxTlsRefInit(context, stubContextDtor);
#endif

#if defined(GLX) || defined(DARWIN)
    context->share = (ContextInfo *)
        crHashtableSearch(stub.contextTable, (unsigned long) shareCtx);
#endif

#ifdef GLX
    context->pGLXPixmapsHash = crAllocHashtable();
    context->damageQueryFailed = GL_FALSE;
    context->damageEventsBase = 0;
#endif

    crHashtableAdd(stub.contextTable, context->id, (void *) context);

    return context;
}


#ifdef Darwin

#define SET_ATTR(l,i,a)     ( (l)[(i)++] = (a) )
#define SET_ATTR_V(l,i,a,v) ( SET_ATTR(l,i,a), SET_ATTR(l,i,v) )

void stubSetPFA( ContextInfo *ctx, CGLPixelFormatAttribute *attribs, int size, GLint *num ) {
    GLuint visual = ctx->visBits;
    int i = 0;

    CRASSERT(visual & CR_RGB_BIT);

    SET_ATTR_V(attribs, i, kCGLPFAColorSize, 8);

    if( visual & CR_DEPTH_BIT )
        SET_ATTR_V(attribs, i, kCGLPFADepthSize, 16);

    if( visual & CR_ACCUM_BIT )
        SET_ATTR_V(attribs, i, kCGLPFAAccumSize, 1);

    if( visual & CR_STENCIL_BIT )
        SET_ATTR_V(attribs, i, kCGLPFAStencilSize, 1);

    if( visual & CR_ALPHA_BIT )
        SET_ATTR_V(attribs, i, kCGLPFAAlphaSize, 1);

    if( visual & CR_DOUBLE_BIT )
        SET_ATTR(attribs, i, kCGLPFADoubleBuffer);

    if( visual & CR_STEREO_BIT )
        SET_ATTR(attribs, i, kCGLPFAStereo);

/*  SET_ATTR_V(attribs, i, kCGLPFASampleBuffers, 1);
    SET_ATTR_V(attribs, i, kCGLPFASamples, 0);
    SET_ATTR_V(attribs, i, kCGLPFADisplayMask, 0);  */
    SET_ATTR(attribs, i, kCGLPFABackingStore);
    SET_ATTR(attribs, i, kCGLPFAWindow);
    SET_ATTR_V(attribs, i, kCGLPFADisplayMask, ctx->disp_mask);

    SET_ATTR(attribs, i, 0);

    *num = i;
}

#endif

/**
 * This creates a native GLX/WGL context.
 */
static GLboolean
InstantiateNativeContext( WindowInfo *window, ContextInfo *context )
{
#ifdef WINDOWS
    context->hglrc = stub.wsInterface.wglCreateContext( window->drawable );
    return context->hglrc ? GL_TRUE : GL_FALSE;
#elif defined(Darwin)
    CGLContextObj shareCtx = NULL;
    CGLPixelFormatObj pix;
    long npix;

    CGLPixelFormatAttribute attribs[16];
    GLint ind = 0;

    if( context->share ) {
        if( context->cglc != context->share->cglc ) {
            crWarning("CGLCreateContext() is trying to share a non-existant "
                      "CGL context.  Setting share context to zero.");
            shareCtx = 0;
        }
        else
            shareCtx = context->cglc;
    }

    stubSetPFA( context, attribs, 16, &ind );

    stub.wsInterface.CGLChoosePixelFormat( attribs, &pix, &npix );
    stub.wsInterface.CGLCreateContext( pix, shareCtx, &context->cglc );
    if( !context->cglc )
        crError("InstantiateNativeContext: Couldn't Create the context!");

    stub.wsInterface.CGLDestroyPixelFormat( pix );

    if( context->parambits ) {
        /* Set the delayed parameters */
        if( context->parambits & VISBIT_SWAP_RECT )
            stub.wsInterface.CGLSetParameter( context->cglc, kCGLCPSwapRectangle, context->swap_rect );

        if( context->parambits & VISBIT_SWAP_INTERVAL )
            stub.wsInterface.CGLSetParameter( context->cglc, kCGLCPSwapInterval, &(context->swap_interval) );

        if( context->parambits & VISBIT_CLIENT_STORAGE )
            stub.wsInterface.CGLSetParameter( context->cglc, kCGLCPClientStorage, (long*)&(context->client_storage) );

        context->parambits = 0;
    }

    return context->cglc ? GL_TRUE : GL_FALSE;
#elif defined(GLX)
    GLXContext shareCtx = 0;

    /* sort out context sharing here */
    if (context->share) {
            if (context->glxContext != context->share->glxContext) {
                    crWarning("glXCreateContext() is trying to share a non-existant "
                                        "GLX context.  Setting share context to zero.");
                    shareCtx = 0;
            }
            else {
                    shareCtx = context->glxContext;
            }
    }

    context->glxContext = stub.wsInterface.glXCreateContext( window->dpy,
                 context->visual, shareCtx, context->direct );

    return context->glxContext ? GL_TRUE : GL_FALSE;
#endif
}


/**
 * Utility functions to get window size and titlebar text.
 */
#ifdef WINDOWS

void
stubGetWindowGeometry(const WindowInfo *window, int *x, int *y,
                      unsigned int *w, unsigned int *h )
{
    RECT rect;

    if (!window->drawable || !window->hWnd) {
        *w = *h = 0;
        return;
    }

    if (window->hWnd!=WindowFromDC(window->drawable))
    {
        crWarning("Window(%i) DC is no longer valid", window->spuWindow);
        return;
    }

    if (!GetClientRect(window->hWnd, &rect))
    {
        crWarning("GetClientRect failed for %p", window->hWnd);
        *w = *h = 0;
        return;
    }
    *w = rect.right - rect.left;
    *h = rect.bottom - rect.top;

    if (!ClientToScreen( window->hWnd, (LPPOINT) &rect ))
    {
        crWarning("ClientToScreen failed for %p", window->hWnd);
        *w = *h = 0;
        return;
    }
    *x = rect.left;
    *y = rect.top;
}

static void
GetWindowTitle( const WindowInfo *window, char *title )
{
    /* XXX - we don't handle recurseUp */
    if (window->hWnd)
        GetWindowText(window->hWnd, title, 100);
    else
        title[0] = 0;
}

static void
GetCursorPosition(WindowInfo *window, int pos[2])
{
    RECT rect;
    POINT point;
    GLint size[2], x, y;
    unsigned int NativeHeight, NativeWidth, ChromiumHeight, ChromiumWidth;
    float WidthRatio, HeightRatio;
    static int DebugFlag = 0;
    
    // apparently the "window" parameter passed to this 
    // function contains the native window information
    HWND NATIVEhwnd = window->hWnd;

    if (NATIVEhwnd!=WindowFromDC(window->drawable))
    {
        crWarning("Window(%i) DC is no longer valid", window->spuWindow);
        return;
    }

    // get the native window's height and width
    stubGetWindowGeometry(window, &x, &y, &NativeWidth, &NativeHeight);

    // get the spu window's height and width
    stub.spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, window->spuWindow, GL_INT, 2, size);
    ChromiumWidth = size[0];
    ChromiumHeight = size[1];

    // get the ratio of the size of the native window to the cr window
    WidthRatio = (float)ChromiumWidth / (float)NativeWidth;
    HeightRatio = (float)ChromiumHeight / (float)NativeHeight;
    
    // output some debug information at the beginning
    if(DebugFlag)
    {
        DebugFlag = 0;
        crDebug("Native Window Handle = %d", NATIVEhwnd);
        crDebug("Native Width = %i", NativeWidth);
        crDebug("Native Height = %i", NativeHeight);
        crDebug("Chromium Width = %i", ChromiumWidth);
        crDebug("Chromium Height = %i", ChromiumHeight);
    }
        
    if (NATIVEhwnd) 
    { 
        GetClientRect( NATIVEhwnd, &rect );
        GetCursorPos (&point);
        
        // make sure these coordinates are relative to the native window,
        // not the whole desktop
        ScreenToClient(NATIVEhwnd, &point);

        // calculate the new position of the virtual cursor
        pos[0] = (int)(point.x * WidthRatio);
        pos[1] = (int)((NativeHeight - point.y) * HeightRatio);
    }
    else 
    {
        pos[0] = 0;
        pos[1] = 0;
    }
}

#elif defined(Darwin)

extern OSStatus CGSGetScreenRectForWindow( CGSConnectionID cid, CGSWindowID wid, float *outRect );
extern OSStatus CGSGetWindowBounds( CGSConnectionID cid, CGSWindowID wid, float *bounds );

void
stubGetWindowGeometry( const WindowInfo *window, int *x, int *y, unsigned int *w, unsigned int *h )
{
    float rect[4];

    if( !window ||
        !window->connection ||
        !window->drawable ||
        CGSGetWindowBounds( window->connection, window->drawable, rect ) != noErr )
    {
        *x = *y = 0;
        *w = *h = 0;
    } else {
        *x = (int) rect[0];
        *y = (int) rect[1];
        *w = (int) rect[2];
        *h = (int) rect[3];
    }
}


static void
GetWindowTitle( const WindowInfo *window, char *title )
{
    /* XXX \todo Darwin window Title */
    title[0] = '\0';
}


static void
GetCursorPosition( const WindowInfo *window, int pos[2] )
{
    Point mouse_pos;
    float window_rect[4];

    GetMouse( &mouse_pos );
    CGSGetScreenRectForWindow( window->connection, window->drawable, window_rect );

    pos[0] = mouse_pos.h - (int) window_rect[0];
    pos[1] = (int) window_rect[3] - (mouse_pos.v - (int) window_rect[1]);

    /*crDebug( "%i %i", pos[0], pos[1] );*/
}

#elif defined(GLX)

void
stubGetWindowGeometry(WindowInfo *window, int *x, int *y, unsigned int *w, unsigned int *h)
{
    Window root, child;
    unsigned int border, depth;
    Display *dpy;

    dpy = stubGetWindowDisplay(window);

    //@todo: Performing those checks is expensive operation, especially for simple apps with high FPS.
    //       Disabling those triples glxgears fps, thus using xevents instead of per frame polling is much more preferred.
    //@todo: Check similar on windows guests, though doubtful as there're no XSync like calls on windows.
    if (window && dpy)
    {
        XLOCK(dpy);
    }

    if (!window
        || !dpy
        || !window->drawable
        || !XGetGeometry(dpy, window->drawable, &root, x, y, w, h, &border, &depth)
        || !XTranslateCoordinates(dpy, window->drawable, root, 0, 0, x, y, &child)) 
    {
        crWarning("Failed to get windows geometry for %p, try xwininfo", window);
        *x = *y = 0;
        *w = *h = 0;
    }

    if (window && dpy)
    {
        XUNLOCK(dpy);
    }
}

static char *
GetWindowTitleHelper( Display *dpy, Window window, GLboolean recurseUp )
{
    while (1) {
        char *name;
        if (!XFetchName(dpy, window, &name))
            return NULL;
        if (name[0]) {
            return name;
        }
        else if (recurseUp) {
            /* This window has no name, try the parent */
            Status stat;
            Window root, parent, *children;
            unsigned int numChildren;
            stat = XQueryTree( dpy, window, &root, &parent,
                                                 &children, &numChildren );
            if (!stat || window == root)
                return NULL;
            if (children)
                XFree(children);
            window = parent;
        }
        else {
            XFree(name);
            return NULL;
        }
    }
}

static void
GetWindowTitle( const WindowInfo *window, char *title )
{
    char *t = GetWindowTitleHelper(window->dpy, window->drawable, GL_TRUE);
    if (t) {
        crStrcpy(title, t);
        XFree(t);
    }
    else {
        title[0] = 0;
    }
}


/**
 *Return current cursor position in local window coords.
 */
static void
GetCursorPosition(WindowInfo *window, int pos[2] )
{
    int rootX, rootY;
    Window root, child;
    unsigned int mask;
    int x, y;

    XLOCK(window->dpy);

    Bool q = XQueryPointer(window->dpy, window->drawable, &root, &child,
                                                 &rootX, &rootY, &pos[0], &pos[1], &mask);
    if (q) {
        unsigned int w, h;
        stubGetWindowGeometry( window, &x, &y, &w, &h );
        /* invert Y */
        pos[1] = (int) h - pos[1] - 1;
    }
    else {
        pos[0] = pos[1] = 0;
    }

    XUNLOCK(window->dpy);
}

#endif


/**
 * This function is called by MakeCurrent() and determines whether or
 * not a new rendering context should be bound to Chromium or the native
 * OpenGL.
 * \return  GL_FALSE if native OpenGL should be used, or GL_TRUE if Chromium
 *          should be used.
 */
static GLboolean
stubCheckUseChromium( WindowInfo *window )
{
    int x, y;
    unsigned int w, h;

    /* If the provided window is CHROMIUM, we're clearly intended
     * to create a CHROMIUM context.
     */
    if (window->type == CHROMIUM)
        return GL_TRUE;

    if (stub.ignoreFreeglutMenus) {
        const char *glutMenuTitle = "freeglut menu";
        char title[1000];
        GetWindowTitle(window, title);
        if (crStrcmp(title, glutMenuTitle) == 0) {
            crDebug("GL faker: Ignoring freeglut menu window");
            return GL_FALSE;
        }
    }

    /*  If the user's specified a window count for Chromium, see if
        *  this window satisfies that criterium.
        */
    stub.matchChromiumWindowCounter++;
    if (stub.matchChromiumWindowCount > 0) {
        if (stub.matchChromiumWindowCounter != stub.matchChromiumWindowCount) {
            crDebug("Using native GL, app window doesn't meet match_window_count");
            return GL_FALSE;
        }
    }

    /* If the user's specified a window list to ignore, see if this
     * window satisfies that criterium.
     */
    if (stub.matchChromiumWindowID) {
        GLuint i;

        for (i = 0; i <= stub.numIgnoreWindowID; i++) {
            if (stub.matchChromiumWindowID[i] == stub.matchChromiumWindowCounter) {
                crDebug("Ignore window ID %d, using native GL", stub.matchChromiumWindowID[i]);
                return GL_FALSE;
            }
        }
    }

    /* If the user's specified a minimum window size for Chromium, see if
     * this window satisfies that criterium. 
     */
    if (stub.minChromiumWindowWidth > 0 && 
        stub.minChromiumWindowHeight > 0) {
        stubGetWindowGeometry( window, &x, &y, &w, &h );
        if (w >= stub.minChromiumWindowWidth && 
            h >= stub.minChromiumWindowHeight) {

            /* Check for maximum sized window now too */
            if (stub.maxChromiumWindowWidth && 
                stub.maxChromiumWindowHeight) {
                if (w < stub.maxChromiumWindowWidth &&
                    h < stub.maxChromiumWindowHeight)
                    return GL_TRUE;
                else 
                    return GL_FALSE;
            }

            return GL_TRUE;
        }
        crDebug("Using native GL, app window doesn't meet minimum_window_size");
        return GL_FALSE;
    }
    else if (stub.matchWindowTitle) {
        /* If the user's specified a window title for Chromium, see if this
         * window satisfies that criterium.
         */
        GLboolean wildcard = GL_FALSE;
        char title[1000];
        char *titlePattern;
        int len;
        /* check for leading '*' wildcard */
        if (stub.matchWindowTitle[0] == '*') {
            titlePattern = crStrdup( stub.matchWindowTitle + 1 );
            wildcard = GL_TRUE;
        }
        else {
            titlePattern = crStrdup( stub.matchWindowTitle );
        }
        /* check for trailing '*' wildcard */
        len = crStrlen(titlePattern);
        if (len > 0 && titlePattern[len - 1] == '*') {
            titlePattern[len - 1] = '\0'; /* terminate here */
            wildcard = GL_TRUE;
        }

        GetWindowTitle( window, title );
        if (title[0]) {
            if (wildcard) {
                if (crStrstr(title, titlePattern)) {
                    crFree(titlePattern);
                    return GL_TRUE;
                }
            }
            else if (crStrcmp(title, titlePattern) == 0) {
                crFree(titlePattern);
                return GL_TRUE;
            }
        }
        crFree(titlePattern);
        crDebug("Using native GL, app window title doesn't match match_window_title string (\"%s\" != \"%s\")", title, stub.matchWindowTitle);
        return GL_FALSE;
    }

    /* Window title and size don't matter */
    CRASSERT(stub.minChromiumWindowWidth == 0);
    CRASSERT(stub.minChromiumWindowHeight == 0);
    CRASSERT(stub.matchWindowTitle == NULL);

    /* User hasn't specified a width/height or window title.
     * We'll use chromium for this window (and context) if no other is.
     */

    return GL_TRUE;  /* use Chromium! */
}

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

    if (pWindow->pOwner == pCtx)
    {
#ifdef WINDOWS
            /* Note: can't use WindowFromDC(context->pOwnWindow->drawable) here
               because GL context is already released from DC and actual guest window
               could be destroyed.
             */
            stubDestroyWindow(CR_CTX_CON(pCtx), (GLint)pWindow->hWnd);
#else
            stubDestroyWindow(CR_CTX_CON(pCtx), (GLint)pWindow->drawable);
#endif
    }
}

GLboolean stubCtxCreate(ContextInfo *context)
{
    /*
     * Create a Chromium context.
     */
#if defined(GLX) || defined(DARWIN)
    GLint spuShareCtx = context->share ? context->share->spuContext : 0;
#else
    GLint spuShareCtx = 0;
#endif
    GLint spuConnection = 0;
    CRASSERT(stub.spu);
    CRASSERT(stub.spu->dispatch_table.CreateContext);
    context->type = CHROMIUM;

#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
    if (context->pHgsmi)
    {
        spuConnection = stub.spu->dispatch_table.VBoxConCreate(context->pHgsmi);
        if (!spuConnection)
        {
            crWarning("VBoxConCreate failed");
            return GL_FALSE;
        }
        context->spuConnection = spuConnection;
    }
#endif

    context->spuContext
        = stub.spu->dispatch_table.VBoxCreateContext(spuConnection, context->dpyName,
                                            context->visBits,
                                            spuShareCtx);

    return GL_TRUE;
}

GLboolean stubCtxCheckCreate(ContextInfo *context)
{
    if (context->type == UNDECIDED)
        return stubCtxCreate(context);
    return CHROMIUM == context->type;
}


GLboolean
stubMakeCurrent( WindowInfo *window, ContextInfo *context )
{
    GLboolean retVal;

    /*
     * Get WindowInfo and ContextInfo pointers.
     */

    if (!context || !window) {
        ContextInfo * currentContext = stubGetCurrentContext();
        if (currentContext)
            currentContext->currentDrawable = NULL;
        if (context)
            context->currentDrawable = NULL;
        stubSetCurrentContext(NULL);
        return GL_TRUE;  /* OK */
    }

#ifdef CHROMIUM_THREADSAFE
    stubCheckMultithread();
#endif

    if (context->type == UNDECIDED) {
        /* Here's where we really create contexts */
#ifdef CHROMIUM_THREADSAFE
        crLockMutex(&stub.mutex);
#endif

        if (stubCheckUseChromium(window)) {
            GLint spuConnection = 0;

            if (!stubCtxCreate(context))
            {
                crWarning("stubCtxCreate failed");
                return GL_FALSE;
            }

#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
            spuConnection = context->spuConnection;
#endif

            if (window->spuWindow == -1)
            {
                /*crDebug("(1)stubMakeCurrent ctx=%p(%i) window=%p(%i)", context, context->spuContext, window, window->spuWindow);*/
                window->spuWindow = stub.spu->dispatch_table.VBoxWindowCreate(spuConnection, window->dpyName, context->visBits );
#ifdef CR_NEWWINTRACK
                window->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(spuConnection);
#endif
            }
        }
        else {
            /*
             * Create a native OpenGL context.
             */
            if (!InstantiateNativeContext(window, context))
            {
#ifdef CHROMIUM_THREADSAFE
                crUnlockMutex(&stub.mutex);
#endif
                return 0; /* false */
            }
            context->type = NATIVE;
        }

#ifdef CHROMIUM_THREADSAFE
        crUnlockMutex(&stub.mutex);
#endif
    }


    if (context->type == NATIVE) {
        /*
         * Native OpenGL MakeCurrent().
         */
#ifdef WINDOWS
        retVal = (GLboolean) stub.wsInterface.wglMakeCurrent( window->drawable, context->hglrc );
#elif defined(Darwin)
        // XXX \todo We need to differentiate between these two..
        retVal = ( stub.wsInterface.CGLSetSurface(context->cglc, window->connection, window->drawable, window->surface) == noErr );
        retVal = ( stub.wsInterface.CGLSetCurrentContext(context->cglc) == noErr );
#elif defined(GLX)
        retVal = (GLboolean) stub.wsInterface.glXMakeCurrent( window->dpy, window->drawable, context->glxContext );
#endif
    }
    else {
        /*
         * SPU chain MakeCurrent().
         */
        CRASSERT(context->type == CHROMIUM);
        CRASSERT(context->spuContext >= 0);

        /*if (context->currentDrawable && context->currentDrawable != window)
            crDebug("Rebinding context %p to a different window", context);*/

        if (window->type == NATIVE) {
            crWarning("Can't rebind a chromium context to a native window\n");
            retVal = 0;
        }
        else {
            if (window->spuWindow == -1)
            {
                /*crDebug("(2)stubMakeCurrent ctx=%p(%i) window=%p(%i)", context, context->spuContext, window, window->spuWindow);*/
                window->spuWindow = stub.spu->dispatch_table.VBoxWindowCreate(
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
                        context->spuConnection,
#else
                        0,
#endif
                        window->dpyName, context->visBits );
#ifdef CR_NEWWINTRACK
                window->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(
# if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
                        context->spuConnection
# else
                        0
# endif
                        );
#endif
                if (context->currentDrawable && context->currentDrawable->type==CHROMIUM 
                    && context->currentDrawable->pOwner==context)
                {
#ifdef WINDOWS
                        if (context->currentDrawable->hWnd!=WindowFromDC(context->currentDrawable->drawable))
                        {
                            stubDestroyWindow(CR_CTX_CON(context), (GLint)context->currentDrawable->hWnd);
                        }
#else
                        Window root;
                        int x, y;
                        unsigned int border, depth, w, h;

                        XLOCK(context->currentDrawable->dpy);
                        if (!XGetGeometry(context->currentDrawable->dpy, context->currentDrawable->drawable, &root, &x, &y, &w, &h, &border, &depth))
                        {
                            stubDestroyWindow(CR_CTX_CON(context), (GLint)context->currentDrawable->drawable);
                        }
                        XUNLOCK(context->currentDrawable->dpy);
#endif
                    
                }
            }

            if (window->spuWindow != (GLint)window->drawable)
                 stub.spu->dispatch_table.MakeCurrent( window->spuWindow, (GLint) window->drawable, context->spuContext );
            else
                 stub.spu->dispatch_table.MakeCurrent( window->spuWindow, 0, /* native window handle */ context->spuContext );

            retVal = 1;
        }
    }

    window->type = context->type;
    window->pOwner = context;
    context->currentDrawable = window;
    stubSetCurrentContext(context);

    if (retVal) {
        /* Now, if we've transitions from Chromium to native rendering, or
         * vice versa, we have to change all the OpenGL entrypoint pointers.
         */
        if (context->type == NATIVE) {
            /* Switch to native API */
            /*printf("  Switching to native API\n");*/
            stubSetDispatch(&stub.nativeDispatch);
        }
        else if (context->type == CHROMIUM) {
            /* Switch to stub (SPU) API */
            /*printf("  Switching to spu API\n");*/
            stubSetDispatch(&stub.spuDispatch);
        }
        else {
            /* no API switch needed */
        }
    }

    if (!window->width && window->type == CHROMIUM) {
        /* One time window setup */
        int x, y;
        unsigned int winW, winH;

        stubGetWindowGeometry( window, &x, &y, &winW, &winH );

        /* If we're not using GLX/WGL (no app window) we'll always get
         * a width and height of zero here.  In that case, skip the viewport
         * call since we're probably using a tilesort SPU with fake_window_dims
         * which the tilesort SPU will use for the viewport.
         */
        window->width = winW;
        window->height = winH;
#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
        if (stubIsWindowVisible(window))
#endif
        {
            if (stub.trackWindowSize)
                stub.spuDispatch.WindowSize( window->spuWindow, winW, winH );
            if (stub.trackWindowPos)
                stub.spuDispatch.WindowPosition(window->spuWindow, x, y);
            if (winW > 0 && winH > 0)
                stub.spu->dispatch_table.Viewport( 0, 0, winW, winH );
        }
#ifdef VBOX_WITH_WDDM
        if (stub.trackWindowVisibleRgn)
            stub.spu->dispatch_table.WindowVisibleRegion(window->spuWindow, 0, NULL);
#endif
    }

    /* Update window mapping state.
     * Basically, this lets us hide render SPU windows which correspond
     * to unmapped application windows.  Without this, "pertly" (for example)
     * opens *lots* of temporary windows which otherwise clutter the screen.
     */
    if (stub.trackWindowVisibility && window->type == CHROMIUM && window->drawable) {
        const int mapped = stubIsWindowVisible(window);
        if (mapped != window->mapped) {
            crDebug("Dispatched: WindowShow(%i, %i)", window->spuWindow, mapped);
            stub.spu->dispatch_table.WindowShow(window->spuWindow, mapped);
            window->mapped = mapped;
        }
    }

    return retVal;
}

void
stubDestroyContext( unsigned long contextId )
{
    ContextInfo *context;

    if (!stub.contextTable) {
        return;
    }

    /* the lock order is windowTable->contextTable (see wglMakeCurrent_prox, glXMakeCurrent)
     * this is why we need to take a windowTable lock since we will later do stub.windowTable access & locking */
    crHashtableLock(stub.windowTable);
    crHashtableLock(stub.contextTable);

    context = (ContextInfo *) crHashtableSearch(stub.contextTable, contextId);

    CRASSERT(context);

    stubDestroyContextLocked(context);

#ifdef CHROMIUM_THREADSAFE
    if (stubGetCurrentContext() == context) {
        stubSetCurrentContext(NULL);
    }

    VBoxTlsRefMarkDestroy(context);
    VBoxTlsRefRelease(context);
#else
    if (stubGetCurrentContext() == context) {
        stubSetCurrentContext(NULL);
    }
    stubContextFree(context);
#endif
    crHashtableUnlock(stub.contextTable);
    crHashtableUnlock(stub.windowTable);
}

void
stubSwapBuffers(WindowInfo *window, GLint flags)
{
    if (!window)
        return;

    /* Determine if this window is being rendered natively or through
     * Chromium.
     */

    if (window->type == NATIVE) {
        /*printf("*** Swapping native window %d\n", (int) drawable);*/
#ifdef WINDOWS
        (void) stub.wsInterface.wglSwapBuffers( window->drawable );
#elif defined(Darwin)
        /* ...is this ok? */
/*      stub.wsInterface.CGLFlushDrawable( context->cglc ); */
        crDebug("stubSwapBuffers: unable to swap (no context!)");
#elif defined(GLX)
        stub.wsInterface.glXSwapBuffers( window->dpy, window->drawable );
#endif
    }
    else if (window->type == CHROMIUM) {
        /* Let the SPU do the buffer swap */
        /*printf("*** Swapping chromium window %d\n", (int) drawable);*/
        if (stub.appDrawCursor) {
            int pos[2];
            GetCursorPosition(window, pos);
            stub.spu->dispatch_table.ChromiumParametervCR(GL_CURSOR_POSITION_CR, GL_INT, 2, pos);
        }
        stub.spu->dispatch_table.SwapBuffers( window->spuWindow, flags );
    }
    else {
        crDebug("Calling SwapBuffers on a window we haven't seen before (no-op).");
    }
}
