context.c revision 49b21ff01a84dcda84ea959b3d58117a803e3222
/* 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
}
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 recuire 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);
if (context->damageDpy)
{
XCloseDisplay(context->damageDpy);
}
#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->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;
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
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)) {
/*
* 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);
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
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).");
}
}