/* 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); } } /** * 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]; 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 winInfo->spuWindow = spuWin; crHashtableAdd(stub.windowTable, (unsigned int) spuWin, winInfo); return spuWin; } GLboolean stubIsWindowVisible( const WindowInfo *win ) { #if defined(WINDOWS) return GL_TRUE; #elif defined(Darwin) return GL_TRUE; #elif defined(GLX) if (win->dpy) { XWindowAttributes attr; XGetWindowAttributes(win->dpy, win->drawable, &attr); return (attr.map_state != IsUnmapped); } 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) { crError("Can't get HWND for given HDC(%x)", drawable); } 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; winInfo->mapped = -1; /* don't know */ winInfo->pOwner = NULL; #ifndef WINDOWS crHashtableAdd(stub.windowTable, (unsigned int) drawable, winInfo); #else crHashtableAdd(stub.windowTable, (unsigned int) hwnd, winInfo); #endif } return winInfo; } /** * 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 ) { GLint spuContext = -1, spuShareCtx = 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) { spuContext = stub.spu->dispatch_table.CreateContext(dpyName, visBits, spuShareCtx); if (spuContext < 0) return NULL; } context = crCalloc(sizeof(ContextInfo)); if (!context) { stub.spu->dispatch_table.DestroyContext(spuContext); 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(GLX) || defined(DARWIN) context->share = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) shareCtx); #endif #ifdef GLX context->pGLXPixmapsHash = crAllocHashtable(); context->damageInitFailed = GL_FALSE; context->damageDpy = NULL; 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; HWND hwnd; if (!window->drawable) { *w = *h = 0; return; } hwnd = WindowFromDC( window->drawable ); if (!hwnd) { *w = 0; *h = 0; } else { GetClientRect( hwnd, &rect ); *w = rect.right - rect.left; *h = rect.bottom - rect.top; ClientToScreen( hwnd, (LPPOINT) &rect ); *x = rect.left; *y = rect.top; } } static void GetWindowTitle( const WindowInfo *window, char *title ) { HWND hwnd; /* XXX - we don't handle recurseUp */ hwnd = WindowFromDC( window->drawable ); if (hwnd) GetWindowText(hwnd, title, 100); else title[0] = 0; } static void GetCursorPosition( const 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 = WindowFromDC( window->drawable ); // 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( const WindowInfo *window, int *x, int *y, unsigned int *w, unsigned int *h ) { Window root, child; unsigned int border, depth; //@todo: Performing those checks is expensive operation, especially for simple apps with high FPS. // Disabling those tripples glxgears fps, thus using xevens instead of per frame polling is much more preffered. //@todo: Check similiar on windows guests, though doubtfull as there're no XSync like calls on windows. if (!window || !window->dpy || !window->drawable || !XGetGeometry(window->dpy, window->drawable, &root, x, y, w, h, &border, &depth) || !XTranslateCoordinates(window->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; } } 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( const WindowInfo *window, int pos[2] ) { int rootX, rootY; Window root, child; unsigned int mask; int x, y; 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; } } #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. */ crWindowDestroy((GLint)pWindow->hWnd); #else crWindowDestroy((GLint)pWindow->drawable); #endif } } GLboolean stubMakeCurrent( WindowInfo *window, ContextInfo *context ) { GLboolean retVal; /* * Get WindowInfo and ContextInfo pointers. */ if (!context || !window) { if (stub.currentContext) stub.currentContext->currentDrawable = NULL; if (context) context->currentDrawable = NULL; stub.currentContext = 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)) { /* * Create a Chromium context. */ #if defined(GLX) || defined(DARWIN) GLint spuShareCtx = context->share ? context->share->spuContext : 0; #else GLint spuShareCtx = 0; #endif CRASSERT(stub.spu); CRASSERT(stub.spu->dispatch_table.CreateContext); context->type = CHROMIUM; context->spuContext = stub.spu->dispatch_table.CreateContext( context->dpyName, context->visBits, spuShareCtx ); 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.WindowCreate( window->dpyName, context->visBits ); } } 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.WindowCreate( window->dpyName, context->visBits ); if (context->currentDrawable && context->currentDrawable->type==CHROMIUM && context->currentDrawable->pOwner==context) { #ifdef WINDOWS if (!WindowFromDC(context->currentDrawable->drawable)) { crWindowDestroy((GLint)context->currentDrawable->hWnd); } #else Window root; int x, y; unsigned int border, depth, w, h; if (!XGetGeometry(context->currentDrawable->dpy, context->currentDrawable->drawable, &root, &x, &y, &w, &h, &border, &depth)) { crWindowDestroy((GLint)context->currentDrawable->drawable); } #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; stub.currentContext = 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 (stub.trackWindowSize) stub.spuDispatch.WindowSize( window->spuWindow, winW, winH ); if (winW > 0 && winH > 0) stub.spu->dispatch_table.Viewport( 0, 0, winW, winH ); } /* Update window mapping state. * Basically, this lets us hide render SPU windows which correspond * to unmapped application windows. Without this, perfly (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; } context = (ContextInfo *) crHashtableSearch(stub.contextTable, contextId); CRASSERT(context); 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 (stub.currentContext == context) { stub.currentContext = NULL; } #ifdef GLX crFreeHashtable(context->pGLXPixmapsHash, crFree); if (context->damageDpy) { XCloseDisplay(context->damageDpy); } #endif crMemZero(context, sizeof(ContextInfo)); /* just to be safe */ crHashtableDelete(stub.contextTable, contextId, crFree); } void stubSwapBuffers( const 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)."); } }