diff options
Diffstat (limited to 'src/VBox/Additions/common/crOpenGL/context.c')
-rw-r--r-- | src/VBox/Additions/common/crOpenGL/context.c | 1463 |
1 files changed, 1463 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/crOpenGL/context.c b/src/VBox/Additions/common/crOpenGL/context.c new file mode 100644 index 00000000..29b93aa1 --- /dev/null +++ b/src/VBox/Additions/common/crOpenGL/context.c @@ -0,0 +1,1463 @@ +/* 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; + RT_NOREF(key); + + 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 +# if 0 /* unused */ +static XErrorHandler oldErrorHandler; +static unsigned char lastXError = Success; + +static int +errorHandler (Display *dpy, XErrorEvent *e) +{ + RT_NOREF(dpy); + + lastXError = e->error_code; + return 0; +} +# endif /* unused */ +#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(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); // kCGLPFAWindow deprecated starting from OSX 10.7 + SET_ATTR_V(attribs, i, kCGLPFADisplayMask, ctx->disp_mask); + + SET_ATTR(attribs, i, 0); + + *num = i; +} + +#endif + +#ifndef GLX +/** + * 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 +} +#endif /* !GLX */ + + +/** + * Utility functions to get window size and titlebar text. + */ +#ifdef WINDOWS + +void +stubGetWindowGeometry(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; + + RT_NOREF(key); + + + 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) + { + crError("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 = GL_FALSE; + + /* + * 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 + } + } +#ifndef GLX + else { + /* + * Create a native OpenGL context. + */ + if (!InstantiateNativeContext(window, context)) + { +# ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&stub.mutex); +# endif + return 0; /* false */ + } + context->type = NATIVE; + } +#endif /* !GLX */ + +#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); + if (context) + stubDestroyContextLocked(context); + else + crError("No 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)."); + } +} |