load.c revision d10b60270f0c0eeb87f45002a010cff8ba2126b1
/* Copyright (c) 2001, Stanford University
* All rights reserved
*
* See the file LICENSE.txt for information on redistributing this software.
*/
#include "cr_spu.h"
#include "cr_net.h"
#include "cr_error.h"
#include "cr_mem.h"
#include "cr_string.h"
#include "cr_net.h"
#include "cr_environment.h"
#include "cr_process.h"
#include "cr_rand.h"
#include "cr_netserver.h"
#include "stub.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <iprt/initterm.h>
#include <iprt/thread.h>
#include <iprt/err.h>
#include <iprt/asm.h>
#ifndef WINDOWS
# include <sys/types.h>
# include <unistd.h>
#endif
#ifdef VBOX_WITH_WDDM
#include <d3d9types.h>
#include <D3dumddi.h>
#include "../../WINNT/Graphics/Video/common/wddm/VBoxMPIf.h"
#include "../../WINNT/Graphics/Video/disp/wddm/VBoxDispMp.h"
#endif
/**
* If you change this, see the comments in tilesortspu_context.c
*/
#define MAGIC_CONTEXT_BASE 500
#define CONFIG_LOOKUP_FILE ".crconfigs"
#ifdef WINDOWS
#define PYTHON_EXE "python.exe"
#else
#define PYTHON_EXE "python"
#endif
#ifdef WINDOWS
static char* gsViewportHackApps[] = {"googleearth.exe", NULL};
#endif
static bool stub_initialized = 0;
#ifdef WINDOWS
static CRmutex stub_init_mutex;
#define STUB_INIT_LOCK() do { crLockMutex(&stub_init_mutex); } while (0)
#define STUB_INIT_UNLOCK() do { crUnlockMutex(&stub_init_mutex); } while (0)
#else
#define STUB_INIT_LOCK() do { } while (0)
#define STUB_INIT_UNLOCK() do { } while (0)
#endif
/* NOTE: 'SPUDispatchTable glim' is declared in NULLfuncs.py now */
/* NOTE: 'SPUDispatchTable stubThreadsafeDispatch' is declared in tsfuncs.c */
Stub stub;
#ifdef CHROMIUM_THREADSAFE
static bool g_stubIsCurrentContextTSDInited;
CRtsd g_stubCurrentContextTSD;
#endif
static void stubInitNativeDispatch( void )
{
#define MAX_FUNCS 1000
SPUNamedFunctionTable gl_funcs[MAX_FUNCS];
int numFuncs;
numFuncs = crLoadOpenGL( &stub.wsInterface, gl_funcs );
stub.haveNativeOpenGL = (numFuncs > 0);
/* XXX call this after context binding */
numFuncs += crLoadOpenGLExtensions( &stub.wsInterface, gl_funcs + numFuncs );
CRASSERT(numFuncs < MAX_FUNCS);
crSPUInitDispatchTable( &stub.nativeDispatch );
crSPUInitDispatch( &stub.nativeDispatch, gl_funcs );
crSPUInitDispatchNops( &stub.nativeDispatch );
#undef MAX_FUNCS
}
/** Pointer to the SPU's real glClear and glViewport functions */
static ClearFunc_t origClear;
static ViewportFunc_t origViewport;
static SwapBuffersFunc_t origSwapBuffers;
static DrawBufferFunc_t origDrawBuffer;
static ScissorFunc_t origScissor;
static void stubCheckWindowState(WindowInfo *window, GLboolean bFlushOnChange)
{
bool bForceUpdate = false;
bool bChanged = false;
#ifdef WINDOWS
/* @todo install hook and track for WM_DISPLAYCHANGE */
{
DEVMODE devMode;
devMode.dmSize = sizeof(DEVMODE);
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode);
if (devMode.dmPelsWidth!=window->dmPelsWidth || devMode.dmPelsHeight!=window->dmPelsHeight)
{
crDebug("Resolution changed(%d,%d), forcing window Pos/Size update", devMode.dmPelsWidth, devMode.dmPelsHeight);
window->dmPelsWidth = devMode.dmPelsWidth;
window->dmPelsHeight = devMode.dmPelsHeight;
bForceUpdate = true;
}
}
#endif
bChanged = stubUpdateWindowGeometry(window, bForceUpdate) || bForceUpdate;
#if defined(GLX) || defined (WINDOWS)
if (stub.trackWindowVisibleRgn)
{
bChanged = stubUpdateWindowVisibileRegions(window) || bChanged;
}
#endif
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;
bChanged = true;
}
}
if (bFlushOnChange && bChanged)
{
stub.spu->dispatch_table.Flush();
}
}
static bool stubSystemWindowExist(WindowInfo *pWindow)
{
#ifdef WINDOWS
if (pWindow->hWnd!=WindowFromDC(pWindow->drawable))
{
return false;
}
#else
Window root;
int x, y;
unsigned int border, depth, w, h;
Display *dpy;
dpy = stubGetWindowDisplay(pWindow);
XLOCK(dpy);
if (!XGetGeometry(dpy, pWindow->drawable, &root, &x, &y, &w, &h, &border, &depth))
{
XUNLOCK(dpy);
return false;
}
XUNLOCK(dpy);
#endif
return true;
}
static void stubCheckWindowsCB(unsigned long key, void *data1, void *data2)
{
WindowInfo *pWindow = (WindowInfo *) data1;
ContextInfo *pCtx = (ContextInfo *) data2;
if (pWindow == pCtx->currentDrawable
|| pWindow->type!=CHROMIUM
|| pWindow->pOwner!=pCtx)
{
return;
}
if (!stubSystemWindowExist(pWindow))
{
#ifdef WINDOWS
crWindowDestroy((GLint)pWindow->hWnd);
#else
crWindowDestroy((GLint)pWindow->drawable);
#endif
return;
}
stubCheckWindowState(pWindow, GL_FALSE);
}
static void stubCheckWindowsState(void)
{
ContextInfo *context = stubGetCurrentContext();
CRASSERT(stub.trackWindowSize || stub.trackWindowPos);
if (!context)
return;
#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
if (stub.bRunningUnderWDDM)
return;
#endif
#if defined(CR_NEWWINTRACK) && !defined(WINDOWS)
crLockMutex(&stub.mutex);
#endif
stubCheckWindowState(context->currentDrawable, GL_TRUE);
crHashtableWalk(stub.windowTable, stubCheckWindowsCB, context);
#if defined(CR_NEWWINTRACK) && !defined(WINDOWS)
crUnlockMutex(&stub.mutex);
#endif
}
/**
* Override the head SPU's glClear function.
* We're basically trapping this function so that we can poll the
* application window size at a regular interval.
*/
static void SPU_APIENTRY trapClear(GLbitfield mask)
{
stubCheckWindowsState();
/* call the original SPU glClear function */
origClear(mask);
}
/**
* As above, but for glViewport. Most apps call glViewport before
* glClear when a window is resized.
*/
static void SPU_APIENTRY trapViewport(GLint x, GLint y, GLsizei w, GLsizei h)
{
stubCheckWindowsState();
/* call the original SPU glViewport function */
if (!stub.viewportHack)
{
origViewport(x, y, w, h);
}
else
{
ContextInfo *context = stubGetCurrentContext();
int winX, winY;
unsigned int winW, winH;
WindowInfo *pWindow;
pWindow = context->currentDrawable;
stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH);
origViewport(0, 0, winW, winH);
}
}
static void SPU_APIENTRY trapSwapBuffers(GLint window, GLint flags)
{
stubCheckWindowsState();
origSwapBuffers(window, flags);
}
static void SPU_APIENTRY trapDrawBuffer(GLenum buf)
{
stubCheckWindowsState();
origDrawBuffer(buf);
}
static void SPU_APIENTRY trapScissor(GLint x, GLint y, GLsizei w, GLsizei h)
{
int winX, winY;
unsigned int winW, winH;
WindowInfo *pWindow;
ContextInfo *context = stubGetCurrentContext();
pWindow = context->currentDrawable;
stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH);
origScissor(0, 0, winW, winH);
}
/**
* Use the GL function pointers in <spu> to initialize the static glim
* dispatch table.
*/
static void stubInitSPUDispatch(SPU *spu)
{
crSPUInitDispatchTable( &stub.spuDispatch );
crSPUCopyDispatchTable( &stub.spuDispatch, &(spu->dispatch_table) );
if (stub.trackWindowSize || stub.trackWindowPos || stub.trackWindowVisibleRgn) {
/* patch-in special glClear/Viewport function to track window sizing */
origClear = stub.spuDispatch.Clear;
origViewport = stub.spuDispatch.Viewport;
origSwapBuffers = stub.spuDispatch.SwapBuffers;
origDrawBuffer = stub.spuDispatch.DrawBuffer;
origScissor = stub.spuDispatch.Scissor;
stub.spuDispatch.Clear = trapClear;
stub.spuDispatch.Viewport = trapViewport;
if (stub.viewportHack)
stub.spuDispatch.Scissor = trapScissor;
/*stub.spuDispatch.SwapBuffers = trapSwapBuffers;
stub.spuDispatch.DrawBuffer = trapDrawBuffer;*/
}
crSPUCopyDispatchTable( &glim, &stub.spuDispatch );
}
// Callback function, used to destroy all created contexts
static void hsWalkStubDestroyContexts(unsigned long key, void *data1, void *data2)
{
stubDestroyContext(key);
}
/**
* This is called when we exit.
* We call all the SPU's cleanup functions.
*/
static void stubSPUTearDownLocked(void)
{
crDebug("stubSPUTearDownLocked");
#ifdef WINDOWS
# ifndef CR_NEWWINTRACK
stubUninstallWindowMessageHook();
# endif
#endif
#ifdef CR_NEWWINTRACK
ASMAtomicWriteBool(&stub.bShutdownSyncThread, true);
#endif
//delete all created contexts
stubMakeCurrent( NULL, NULL);
/* 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);
crHashtableWalk(stub.contextTable, hsWalkStubDestroyContexts, NULL);
crHashtableUnlock(stub.windowTable);
/* shutdown, now trap any calls to a NULL dispatcher */
crSPUCopyDispatchTable(&glim, &stubNULLDispatch);
crSPUUnloadChain(stub.spu);
stub.spu = NULL;
#ifndef Linux
crUnloadOpenGL();
#endif
#ifndef WINDOWS
crNetTearDown();
#endif
#ifdef GLX
if (stub.xshmSI.shmid>=0)
{
shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
shmdt(stub.xshmSI.shmaddr);
}
crFreeHashtable(stub.pGLXPixmapsHash, crFree);
#endif
crFreeHashtable(stub.windowTable, crFree);
crFreeHashtable(stub.contextTable, NULL);
crMemset(&stub, 0, sizeof(stub));
}
/**
* This is called when we exit.
* We call all the SPU's cleanup functions.
*/
static void stubSPUTearDown(void)
{
STUB_INIT_LOCK();
if (stub_initialized)
{
stubSPUTearDownLocked();
stub_initialized = 0;
}
STUB_INIT_UNLOCK();
}
static void stubSPUSafeTearDown(void)
{
#ifdef CHROMIUM_THREADSAFE
CRmutex *mutex;
#endif
if (!stub_initialized) return;
stub_initialized = 0;
#ifdef CHROMIUM_THREADSAFE
mutex = &stub.mutex;
crLockMutex(mutex);
#endif
crDebug("stubSPUSafeTearDown");
#ifdef WINDOWS
# ifndef CR_NEWWINTRACK
stubUninstallWindowMessageHook();
# endif
#endif
#if defined(CR_NEWWINTRACK)
crUnlockMutex(mutex);
# if defined(WINDOWS)
if (RTThreadGetState(stub.hSyncThread)!=RTTHREADSTATE_TERMINATED)
{
HANDLE hNative;
DWORD ec=0;
hNative = OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION|THREAD_TERMINATE,
false, RTThreadGetNative(stub.hSyncThread));
if (!hNative)
{
crWarning("Failed to get handle for sync thread(%#x)", GetLastError());
}
else
{
crDebug("Got handle %p for thread %#x", hNative, RTThreadGetNative(stub.hSyncThread));
}
ASMAtomicWriteBool(&stub.bShutdownSyncThread, true);
if (PostThreadMessage(RTThreadGetNative(stub.hSyncThread), WM_QUIT, 0, 0))
{
RTThreadWait(stub.hSyncThread, 1000, NULL);
/*Same issue as on linux, RTThreadWait exits before system thread is terminated, which leads
* to issues as our dll goes to be unloaded.
*@todo
*We usually call this function from DllMain which seems to be holding some lock and thus we have to
* kill thread via TerminateThread.
*/
if (WaitForSingleObject(hNative, 100)==WAIT_TIMEOUT)
{
crDebug("Wait failed, terminating");
if (!TerminateThread(hNative, 1))
{
crDebug("TerminateThread failed");
}
}
if (GetExitCodeThread(hNative, &ec))
{
crDebug("Thread %p exited with ec=%i", hNative, ec);
}
else
{
crDebug("GetExitCodeThread failed(%#x)", GetLastError());
}
}
else
{
crDebug("Sync thread killed before DLL_PROCESS_DETACH");
}
if (hNative)
{
CloseHandle(hNative);
}
}
#else
if (stub.hSyncThread!=NIL_RTTHREAD)
{
ASMAtomicWriteBool(&stub.bShutdownSyncThread, true);
{
/*RTThreadWait might return too early, which cause our code being unloaded while RT thread wrapper is still running*/
int rc = pthread_join(RTThreadGetNative(stub.hSyncThread), NULL);
if (!rc)
{
crDebug("pthread_join failed %i", rc);
}
}
}
#endif
crLockMutex(mutex);
#endif
#ifndef WINDOWS
crNetTearDown();
#endif
#ifdef CHROMIUM_THREADSAFE
crUnlockMutex(mutex);
crFreeMutex(mutex);
#endif
crMemset(&stub, 0, sizeof(stub));
}
static void stubExitHandler(void)
{
stubSPUSafeTearDown();
}
/**
* Called when we receive a SIGTERM signal.
*/
static void stubSignalHandler(int signo)
{
stubSPUSafeTearDown();
exit(0); /* this causes stubExitHandler() to be called */
}
#ifndef RT_OS_WINDOWS
# ifdef CHROMIUM_THREADSAFE
static DECLCALLBACK(void) stubThreadTlsDtor(void *pvValue)
{
ContextInfo *pCtx = (ContextInfo*)pvValue;
VBoxTlsRefRelease(pCtx);
}
# endif
#endif
/**
* Init variables in the stub structure, install signal handler.
*/
static void stubInitVars(void)
{
WindowInfo *defaultWin;
#ifdef CHROMIUM_THREADSAFE
crInitMutex(&stub.mutex);
#endif
/* At the very least we want CR_RGB_BIT. */
stub.haveNativeOpenGL = GL_FALSE;
stub.spu = NULL;
stub.appDrawCursor = 0;
stub.minChromiumWindowWidth = 0;
stub.minChromiumWindowHeight = 0;
stub.maxChromiumWindowWidth = 0;
stub.maxChromiumWindowHeight = 0;
stub.matchChromiumWindowCount = 0;
stub.matchChromiumWindowID = NULL;
stub.matchWindowTitle = NULL;
stub.ignoreFreeglutMenus = 0;
stub.threadSafe = GL_FALSE;
stub.trackWindowSize = 0;
stub.trackWindowPos = 0;
stub.trackWindowVisibility = 0;
stub.trackWindowVisibleRgn = 0;
stub.mothershipPID = 0;
stub.spu_dir = NULL;
stub.freeContextNumber = MAGIC_CONTEXT_BASE;
stub.contextTable = crAllocHashtable();
#ifndef RT_OS_WINDOWS
# ifdef CHROMIUM_THREADSAFE
if (!g_stubIsCurrentContextTSDInited)
{
crInitTSDF(&g_stubCurrentContextTSD, stubThreadTlsDtor);
g_stubIsCurrentContextTSDInited = true;
}
# endif
#endif
stubSetCurrentContext(NULL);
stub.windowTable = crAllocHashtable();
#ifdef CR_NEWWINTRACK
stub.bShutdownSyncThread = false;
stub.hSyncThread = NIL_RTTHREAD;
#endif
defaultWin = (WindowInfo *) crCalloc(sizeof(WindowInfo));
defaultWin->type = CHROMIUM;
defaultWin->spuWindow = 0; /* window 0 always exists */
#ifdef WINDOWS
defaultWin->hVisibleRegion = INVALID_HANDLE_VALUE;
#elif defined(GLX)
defaultWin->pVisibleRegions = NULL;
defaultWin->cVisibleRegions = 0;
#endif
crHashtableAdd(stub.windowTable, 0, defaultWin);
#if 1
atexit(stubExitHandler);
signal(SIGTERM, stubSignalHandler);
signal(SIGINT, stubSignalHandler);
#ifndef WINDOWS
signal(SIGPIPE, SIG_IGN); /* the networking code should catch this */
#endif
#else
(void) stubExitHandler;
(void) stubSignalHandler;
#endif
}
/**
* Return a free port number for the mothership to use, or -1 if we
* can't find one.
*/
static int
GenerateMothershipPort(void)
{
const int MAX_PORT = 10100;
unsigned short port;
/* generate initial port number randomly */
crRandAutoSeed();
port = (unsigned short) crRandInt(10001, MAX_PORT);
#ifdef WINDOWS
/* XXX should implement a free port check here */
return port;
#else
/*
* See if this port number really is free, try another if needed.
*/
{
struct sockaddr_in servaddr;
int so_reuseaddr = 1;
int sock, k;
/* create socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
CRASSERT(sock > 2);
/* deallocate socket/port when we exit */
k = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *) &so_reuseaddr, sizeof(so_reuseaddr));
CRASSERT(k == 0);
/* initialize the servaddr struct */
crMemset(&servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
while (port < MAX_PORT) {
/* Bind to the given port number, return -1 if we fail */
servaddr.sin_port = htons((unsigned short) port);
k = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (k) {
/* failed to create port. try next one. */
port++;
}
else {
/* free the socket/port now so mothership can make it */
close(sock);
return port;
}
}
}
#endif /* WINDOWS */
return -1;
}
/**
* Try to determine which mothership configuration to use for this program.
*/
static char **
LookupMothershipConfig(const char *procName)
{
const int procNameLen = crStrlen(procName);
FILE *f;
const char *home;
char configPath[1000];
/* first, check if the CR_CONFIG env var is set */
{
const char *conf = crGetenv("CR_CONFIG");
if (conf && crStrlen(conf) > 0)
return crStrSplit(conf, " ");
}
/* second, look up config name from config file */
home = crGetenv("HOME");
if (home)
sprintf(configPath, "%s/%s", home, CONFIG_LOOKUP_FILE);
else
crStrcpy(configPath, CONFIG_LOOKUP_FILE); /* from current dir */
/* Check if the CR_CONFIG_PATH env var is set. */
{
const char *conf = crGetenv("CR_CONFIG_PATH");
if (conf)
crStrcpy(configPath, conf); /* from env var */
}
f = fopen(configPath, "r");
if (!f) {
return NULL;
}
while (!feof(f)) {
char line[1000];
char **args;
fgets(line, 999, f);
line[crStrlen(line) - 1] = 0; /* remove trailing newline */
if (crStrncmp(line, procName, procNameLen) == 0 &&
(line[procNameLen] == ' ' || line[procNameLen] == '\t'))
{
crWarning("Using Chromium configuration for %s from %s",
procName, configPath);
args = crStrSplit(line + procNameLen + 1, " ");
return args;
}
}
fclose(f);
return NULL;
}
static int Mothership_Awake = 0;
/**
* Signal handler to determine when mothership is ready.
*/
static void
MothershipPhoneHome(int signo)
{
crDebug("Got signal %d: mothership is awake!", signo);
Mothership_Awake = 1;
}
void stubSetDefaultConfigurationOptions(void)
{
unsigned char key[16]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
stub.appDrawCursor = 0;
stub.minChromiumWindowWidth = 0;
stub.minChromiumWindowHeight = 0;
stub.maxChromiumWindowWidth = 0;
stub.maxChromiumWindowHeight = 0;
stub.matchChromiumWindowID = NULL;
stub.numIgnoreWindowID = 0;
stub.matchWindowTitle = NULL;
stub.ignoreFreeglutMenus = 0;
stub.trackWindowSize = 1;
stub.trackWindowPos = 1;
stub.trackWindowVisibility = 1;
stub.trackWindowVisibleRgn = 1;
stub.matchChromiumWindowCount = 0;
stub.spu_dir = NULL;
crNetSetRank(0);
crNetSetContextRange(32, 35);
crNetSetNodeRange("iam0", "iamvis20");
crNetSetKey(key,sizeof(key));
stub.force_pbuffers = 0;
stub.viewportHack = 0;
#ifdef WINDOWS
{
char name[1000];
int i;
# ifdef VBOX_WITH_WDDM
stub.bRunningUnderWDDM = false;
# endif
/* Apply viewport hack only if we're running under wine */
if (NULL!=GetModuleHandle("wined3d.dll") || NULL != GetModuleHandle("wined3dwddm.dll"))
{
crGetProcName(name, 1000);
for (i=0; gsViewportHackApps[i]; ++i)
{
if (!stricmp(name, gsViewportHackApps[i]))
{
stub.viewportHack = 1;
break;
}
}
}
}
#endif
}
#ifdef CR_NEWWINTRACK
# ifdef VBOX_WITH_WDDM
static stubDispatchVisibleRegions(WindowInfo *pWindow)
{
DWORD dwCount;
LPRGNDATA lpRgnData;
dwCount = GetRegionData(pWindow->hVisibleRegion, 0, NULL);
lpRgnData = crAlloc(dwCount);
if (lpRgnData)
{
GetRegionData(pWindow->hVisibleRegion, dwCount, lpRgnData);
crDebug("Dispatched WindowVisibleRegion (%i, cRects=%i)", pWindow->spuWindow, lpRgnData->rdh.nCount);
stub.spuDispatch.WindowVisibleRegion(pWindow->spuWindow, lpRgnData->rdh.nCount, (GLint*) lpRgnData->Buffer);
crFree(lpRgnData);
}
else crWarning("GetRegionData failed, VisibleRegions update failed");
}
static HRGN stubMakeRegionFromRects(PVBOXVIDEOCM_CMD_RECTS pRegions, uint32_t start)
{
HRGN hRgn, hTmpRgn;
uint32_t i;
if (pRegions->RectsInfo.cRects<=start)
{
return INVALID_HANDLE_VALUE;
}
hRgn = CreateRectRgn(0, 0, 0, 0);
for (i=start; i<pRegions->RectsInfo.cRects; ++i)
{
hTmpRgn = CreateRectRgnIndirect(&pRegions->RectsInfo.aRects[i]);
CombineRgn(hRgn, hRgn, hTmpRgn, RGN_OR);
DeleteObject(hTmpRgn);
}
return hRgn;
}
typedef struct VBOXCR_UPDATEWNDCB
{
VBOXDISPMP_REGIONS Regions;
bool fSendUpdateMsg;
} VBOXCR_UPDATEWNDCB, *PVBOXCR_UPDATEWNDCB;
static void stubSyncTrUpdateWindowCB(unsigned long key, void *data1, void *data2)
{
WindowInfo *pWindow = (WindowInfo *) data1;
PVBOXCR_UPDATEWNDCB pCbData = (PVBOXCR_UPDATEWNDCB) data2;
VBOXDISPMP_REGIONS *pRegions = &pCbData->Regions;
bool bChanged = false, bDoMap = false;
HRGN hNewRgn = INVALID_HANDLE_VALUE;
if (pRegions->hWnd != pWindow->hWnd)
{
return;
}
stub.spu->dispatch_table.VBoxPackSetInjectID(pWindow->u32ClientID);
if (!stubSystemWindowExist(pWindow))
{
crWindowDestroy((GLint)pWindow->hWnd);
return;
}
if (pRegions->pRegions->fFlags.bAddVisibleRects || pRegions->pRegions->fFlags.bSetViewRect)
{
if (!pWindow->mapped)
{
bDoMap = true;
}
/* ensure data integrity */
Assert(!pRegions->pRegions->fFlags.bAddHiddenRects);
if (pRegions->pRegions->fFlags.bSetViewRect)
{
int winX, winY;
unsigned int winW, winH;
BOOL bRc;
winX = pRegions->pRegions->RectsInfo.aRects[0].left;
winY = pRegions->pRegions->RectsInfo.aRects[0].top;
winW = pRegions->pRegions->RectsInfo.aRects[0].right - winX;
winH = pRegions->pRegions->RectsInfo.aRects[0].bottom - winY;
if (stub.trackWindowPos && (bDoMap || winX!=pWindow->x || winY!=pWindow->y))
{
crDebug("Dispatched WindowPosition (%i)", pWindow->spuWindow);
stub.spuDispatch.WindowPosition(pWindow->spuWindow, winX, winY);
pWindow->x = winX;
pWindow->y = winY;
bChanged = true;
}
if (stub.trackWindowSize && (bDoMap || winW!=pWindow->width || winH!=pWindow->height))
{
crDebug("Dispatched WindowSize (%i)", pWindow->spuWindow);
stub.spuDispatch.WindowSize(pWindow->spuWindow, winW, winH);
pWindow->width = winW;
pWindow->height = winH;
bChanged = true;
}
bRc = MoveWindow(pRegions->hWnd, winX, winY, winW, winH, FALSE /*BOOL bRepaint*/);
if (!bRc)
{
DWORD winEr = GetLastError();
crWarning("stubSyncTrUpdateWindowCB: MoveWindow failed winEr(%d)", winEr);
}
}
if (pRegions->pRegions->fFlags.bAddVisibleRects)
{
hNewRgn = stubMakeRegionFromRects(pRegions->pRegions, pRegions->pRegions->fFlags.bSetViewRect ? 1 : 0);
}
}
else if (!pRegions->pRegions->fFlags.bHide)
{
Assert(pRegions->pRegions->fFlags.bAddHiddenRects);
hNewRgn = stubMakeRegionFromRects(pRegions->pRegions, 0);
}
else
{
Assert(pRegions->pRegions->fFlags.bAddHiddenRects);
hNewRgn = CreateRectRgn(pWindow->x, pWindow->y, pWindow->x + pWindow->width, pWindow->y + pWindow->height);
}
if (hNewRgn!=INVALID_HANDLE_VALUE)
{
if (pRegions->pRegions->fFlags.bAddVisibleRects)
{
HRGN hEmptyRgn = CreateRectRgn(0, 0, 0, 0);
if (hEmptyRgn!=INVALID_HANDLE_VALUE)
{
if (pWindow->hVisibleRegion==INVALID_HANDLE_VALUE || EqualRgn(pWindow->hVisibleRegion, hEmptyRgn))
{
pCbData->fSendUpdateMsg = true;
}
DeleteObject(hEmptyRgn);
}
else
{
crWarning("Failed to created empty region!");
}
}
OffsetRgn(hNewRgn, -pWindow->x, -pWindow->y);
if (pWindow->hVisibleRegion!=INVALID_HANDLE_VALUE)
{
CombineRgn(hNewRgn, pWindow->hVisibleRegion, hNewRgn,
pRegions->pRegions->fFlags.bAddHiddenRects ? RGN_DIFF:RGN_OR);
if (!EqualRgn(pWindow->hVisibleRegion, hNewRgn))
{
DeleteObject(pWindow->hVisibleRegion);
pWindow->hVisibleRegion = hNewRgn;
stubDispatchVisibleRegions(pWindow);
bChanged = true;
}
else
{
DeleteObject(hNewRgn);
}
}
else
{
if (pRegions->pRegions->fFlags.bAddVisibleRects)
{
pWindow->hVisibleRegion = hNewRgn;
stubDispatchVisibleRegions(pWindow);
bChanged = true;
}
}
}
if (bDoMap)
{
pWindow->mapped = GL_TRUE;
bChanged = true;
crDebug("Dispatched: WindowShow(%i, %i)", pWindow->spuWindow, pWindow->mapped);
stub.spu->dispatch_table.WindowShow(pWindow->spuWindow, pWindow->mapped);
}
if (bChanged)
{
stub.spu->dispatch_table.Flush();
}
}
# endif
static void stubSyncTrCheckWindowsCB(unsigned long key, void *data1, void *data2)
{
WindowInfo *pWindow = (WindowInfo *) data1;
(void) data2;
if (pWindow->type!=CHROMIUM || pWindow->spuWindow==0)
{
return;
}
stub.spu->dispatch_table.VBoxPackSetInjectID(pWindow->u32ClientID);
if (!stubSystemWindowExist(pWindow))
{
#ifdef WINDOWS
crWindowDestroy((GLint)pWindow->hWnd);
#else
crWindowDestroy((GLint)pWindow->drawable);
#endif
/*No need to flush here as crWindowDestroy does it*/
return;
}
#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
if (stub.bRunningUnderWDDM)
return;
#endif
stubCheckWindowState(pWindow, GL_TRUE);
}
static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser)
{
#ifdef WINDOWS
MSG msg;
# ifdef VBOX_WITH_WDDM
static VBOXDISPMP_CALLBACKS VBoxDispMpTstCallbacks = {NULL, NULL, NULL};
HMODULE hVBoxD3D = NULL;
VBOXCR_UPDATEWNDCB RegionsData;
HRESULT hr;
GLint spuConnection = 0;
# endif
#endif
(void) pvUser;
crDebug("Sync thread started");
#ifdef WINDOWS
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
# ifdef VBOX_WITH_WDDM
hVBoxD3D = GetModuleHandle("VBoxDispD3D");
if (hVBoxD3D)
{
hVBoxD3D = LoadLibrary("VBoxDispD3D");
}
if (hVBoxD3D)
{
PFNVBOXDISPMP_GETCALLBACKS pfnVBoxDispMpGetCallbacks;
pfnVBoxDispMpGetCallbacks = (PFNVBOXDISPMP_GETCALLBACKS)GetProcAddress(hVBoxD3D, TEXT("VBoxDispMpGetCallbacks"));
if (pfnVBoxDispMpGetCallbacks)
{
hr = pfnVBoxDispMpGetCallbacks(VBOXDISPMP_VERSION, &VBoxDispMpTstCallbacks);
if (S_OK==hr)
{
CRASSERT(VBoxDispMpTstCallbacks.pfnEnableEvents);
CRASSERT(VBoxDispMpTstCallbacks.pfnDisableEvents);
CRASSERT(VBoxDispMpTstCallbacks.pfnGetRegions);
hr = VBoxDispMpTstCallbacks.pfnEnableEvents();
if (hr != S_OK)
{
crWarning("VBoxDispMpTstCallbacks.pfnEnableEvents failed");
}
else
{
crDebug("running with VBoxDispD3D");
stub.trackWindowVisibleRgn = 0;
stub.bRunningUnderWDDM = true;
}
}
else
{
crWarning("VBoxDispMpGetCallbacks failed");
}
}
}
# endif /* VBOX_WITH_WDDM */
#endif /* WINDOWS */
crLockMutex(&stub.mutex);
#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
spuConnection =
#endif
stub.spu->dispatch_table.VBoxPackSetInjectThread(NULL);
#if defined(WINDOWS) && defined(VBOX_WITH_WDDM)
if (!spuConnection)
{
crError("VBoxPackSetInjectThread failed!");
}
#endif
crUnlockMutex(&stub.mutex);
RTThreadUserSignal(ThreadSelf);
while(!stub.bShutdownSyncThread)
{
#ifdef WINDOWS
if (!PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
# ifdef VBOX_WITH_WDDM
if (VBoxDispMpTstCallbacks.pfnGetRegions)
{
hr = VBoxDispMpTstCallbacks.pfnGetRegions(&RegionsData.Regions, 50);
if (S_OK==hr)
{
RegionsData.fSendUpdateMsg = false;
# if 0
uint32_t i;
crDebug(">>>Regions for HWND(0x%x)>>>", RegionsData.Regions.hWnd);
crDebug("Flags(0x%x)", RegionsData.Regions.pRegions->fFlags.Value);
for (i = 0; i < RegionsData.Regions.pRegions->RectsInfo.cRects; ++i)
{
RECT *pRect = &RegionsData.Regions.pRegions->RectsInfo.aRects[i];
crDebug("Rect(%d): left(%d), top(%d), right(%d), bottom(%d)", i, pRect->left, pRect->top, pRect->right, pRect->bottom);
}
crDebug("<<<<<");
# endif
/*hacky way to make sure window wouldn't be deleted in another thread as we hold hashtable lock here*/
crHashtableWalk(stub.windowTable, stubSyncTrUpdateWindowCB, &RegionsData);
if (RegionsData.fSendUpdateMsg)
{
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0, SMTO_NORMAL, 1000, NULL);
}
}
else
{
if (WAIT_TIMEOUT!=hr)
{
crWarning("VBoxDispMpTstCallbacks.pfnGetRegions failed with 0x%x", hr);
}
crHashtableWalk(stub.windowTable, stubSyncTrCheckWindowsCB, NULL);
}
}
else
# endif
{
crHashtableWalk(stub.windowTable, stubSyncTrCheckWindowsCB, NULL);
RTThreadSleep(50);
}
}
else
{
if (WM_QUIT==msg.message)
{
crDebug("Sync thread got WM_QUIT");
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#else
crLockMutex(&stub.mutex);
crHashtableWalk(stub.windowTable, stubSyncTrCheckWindowsCB, NULL);
crUnlockMutex(&stub.mutex);
RTThreadSleep(50);
#endif
}
#ifdef VBOX_WITH_WDDM
if (VBoxDispMpTstCallbacks.pfnDisableEvents)
{
VBoxDispMpTstCallbacks.pfnDisableEvents();
}
if (spuConnection)
{
stub.spu->dispatch_table.VBoxConDestroy(spuConnection);
}
if (hVBoxD3D)
{
FreeLibrary(hVBoxD3D);
}
#endif
crDebug("Sync thread stopped");
return 0;
}
#endif
/**
* Do one-time initializations for the faker.
* Returns TRUE on success, FALSE otherwise.
*/
static bool
stubInitLocked(void)
{
/* Here is where we contact the mothership to find out what we're supposed
* to be doing. Networking code in a DLL initializer. I sure hope this
* works :)
*
* HOW can I pass the mothership address to this if I already know it?
*/
CRConnection *conn = NULL;
char response[1024];
char **spuchain;
int num_spus;
int *spu_ids;
char **spu_names;
const char *app_id;
int i;
int disable_sync = 0;
stubInitVars();
crGetProcName(response, 1024);
crDebug("Stub launched for %s", response);
#if defined(CR_NEWWINTRACK) && !defined(WINDOWS)
/*@todo when vm boots with compiz turned on, new code causes hang in xcb_wait_for_reply in the sync thread
* as at the start compiz runs our code under XGrabServer.
*/
if (!crStrcmp(response, "compiz") || !crStrcmp(response, "compiz_real") || !crStrcmp(response, "compiz.real")
|| !crStrcmp(response, "compiz-bin"))
{
disable_sync = 1;
}
#endif
/* @todo check if it'd be of any use on other than guests, no use for windows */
app_id = crGetenv( "CR_APPLICATION_ID_NUMBER" );
crNetInit( NULL, NULL );
#ifndef WINDOWS
{
CRNetServer ns;
ns.name = "vboxhgcm://host:0";
ns.buffer_size = 1024;
crNetServerConnect(&ns
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
, NULL
#endif
);
if (!ns.conn)
{
crWarning("Failed to connect to host. Make sure 3D acceleration is enabled for this VM.");
return false;
}
else
{
crNetFreeConnection(ns.conn);
}
#if 0 && defined(CR_NEWWINTRACK)
{
Status st = XInitThreads();
if (st==0)
{
crWarning("XInitThreads returned %i", (int)st);
}
}
#endif
}
#endif
strcpy(response, "2 0 feedback 1 pack");
spuchain = crStrSplit( response, " " );
num_spus = crStrToInt( spuchain[0] );
spu_ids = (int *) crAlloc( num_spus * sizeof( *spu_ids ) );
spu_names = (char **) crAlloc( num_spus * sizeof( *spu_names ) );
for (i = 0 ; i < num_spus ; i++)
{
spu_ids[i] = crStrToInt( spuchain[2*i+1] );
spu_names[i] = crStrdup( spuchain[2*i+2] );
crDebug( "SPU %d/%d: (%d) \"%s\"", i+1, num_spus, spu_ids[i], spu_names[i] );
}
stubSetDefaultConfigurationOptions();
stub.spu = crSPULoadChain( num_spus, spu_ids, spu_names, stub.spu_dir, NULL );
crFree( spuchain );
crFree( spu_ids );
for (i = 0; i < num_spus; ++i)
crFree(spu_names[i]);
crFree( spu_names );
// spu chain load failed somewhere
if (!stub.spu) {
return false;
}
crSPUInitDispatchTable( &glim );
/* This is unlikely to change -- We still want to initialize our dispatch
* table with the functions of the first SPU in the chain. */
stubInitSPUDispatch( stub.spu );
/* we need to plug one special stub function into the dispatch table */
glim.GetChromiumParametervCR = stub_GetChromiumParametervCR;
#if !defined(VBOX_NO_NATIVEGL)
/* Load pointers to native OpenGL functions into stub.nativeDispatch */
stubInitNativeDispatch();
#endif
/*crDebug("stub init");
raise(SIGINT);*/
#ifdef WINDOWS
# ifndef CR_NEWWINTRACK
stubInstallWindowMessageHook();
# endif
#endif
#ifdef CR_NEWWINTRACK
{
int rc;
RTR3InitDll(0);
if (!disable_sync)
{
crDebug("Starting sync thread");
rc = RTThreadCreate(&stub.hSyncThread, stubSyncThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Sync");
if (RT_FAILURE(rc))
{
crError("Failed to start sync thread! (%x)", rc);
}
RTThreadUserWait(stub.hSyncThread, 60 * 1000);
RTThreadUserReset(stub.hSyncThread);
crDebug("Going on");
}
}
#endif
#ifdef GLX
stub.xshmSI.shmid = -1;
stub.bShmInitFailed = GL_FALSE;
stub.pGLXPixmapsHash = crAllocHashtable();
stub.bXExtensionsChecked = GL_FALSE;
stub.bHaveXComposite = GL_FALSE;
stub.bHaveXFixes = GL_FALSE;
#endif
return true;
}
/**
* Do one-time initializations for the faker.
* Returns TRUE on success, FALSE otherwise.
*/
bool
stubInit(void)
{
bool bRc = true;
/* we need to serialize the initialization, otherwise racing is possible
* for XPDM-based d3d when a d3d switcher is testing the gl lib in two or more threads
* NOTE: the STUB_INIT_LOCK/UNLOCK is a NOP for non-win currently */
STUB_INIT_LOCK();
if (!stub_initialized)
bRc = stub_initialized = stubInitLocked();
STUB_INIT_UNLOCK();
return bRc;
}
/* Sigh -- we can't do initialization at load time, since Windows forbids
* the loading of other libraries from DLLMain. */
#ifdef LINUX
/* GCC crap
*void (*stub_init_ptr)(void) __attribute__((section(".ctors"))) = __stubInit; */
#endif
#ifdef WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef DEBUG_misha
/* debugging: this is to be able to catch first-chance notifications
* for exceptions other than EXCEPTION_BREAKPOINT in kernel debugger */
# define VDBG_VEHANDLER
#endif
#ifdef VDBG_VEHANDLER
static PVOID g_VBoxWDbgVEHandler = NULL;
LONG WINAPI vboxVDbgVectoredHandler(struct _EXCEPTION_POINTERS *pExceptionInfo)
{
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
PCONTEXT pContextRecord = pExceptionInfo->ContextRecord;
switch (pExceptionRecord->ExceptionCode)
{
case EXCEPTION_BREAKPOINT:
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_STACK_OVERFLOW:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_ILLEGAL_INSTRUCTION:
CRASSERT(0);
break;
default:
break;
}
return EXCEPTION_CONTINUE_SEARCH;
}
void vboxVDbgVEHandlerRegister()
{
CRASSERT(!g_VBoxWDbgVEHandler);
g_VBoxWDbgVEHandler = AddVectoredExceptionHandler(1,vboxVDbgVectoredHandler);
CRASSERT(g_VBoxWDbgVEHandler);
}
void vboxVDbgVEHandlerUnregister()
{
ULONG uResult;
if (g_VBoxWDbgVEHandler)
{
uResult = RemoveVectoredExceptionHandler(g_VBoxWDbgVEHandler);
CRASSERT(uResult);
g_VBoxWDbgVEHandler = NULL;
}
}
#endif
/* Windows crap */
BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
{
(void) lpvReserved;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
CRNetServer ns;
#ifdef CHROMIUM_THREADSAFE
crInitTSD(&g_stubCurrentContextTSD);
#endif
crInitMutex(&stub_init_mutex);
#ifdef VDBG_VEHANDLER
vboxVDbgVEHandlerRegister();
#endif
crNetInit(NULL, NULL);
ns.name = "vboxhgcm://host:0";
ns.buffer_size = 1024;
crNetServerConnect(&ns
#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
, NULL
#endif
);
if (!ns.conn)
{
crDebug("Failed to connect to host (is guest 3d acceleration enabled?), aborting ICD load.");
#ifdef VDBG_VEHANDLER
vboxVDbgVEHandlerUnregister();
#endif
return FALSE;
}
else
crNetFreeConnection(ns.conn);
break;
}
case DLL_PROCESS_DETACH:
{
/* do exactly the same thing as for DLL_THREAD_DETACH since
* DLL_THREAD_DETACH is not called for the thread doing DLL_PROCESS_DETACH according to msdn docs */
stubSetCurrentContext(NULL);
if (stub_initialized)
{
CRASSERT(stub.spu);
stub.spu->dispatch_table.VBoxDetachThread();
}
stubSPUSafeTearDown();
#ifdef CHROMIUM_THREADSAFE
crFreeTSD(&g_stubCurrentContextTSD);
#endif
#ifdef VDBG_VEHANDLER
vboxVDbgVEHandlerUnregister();
#endif
break;
}
case DLL_THREAD_ATTACH:
{
if (stub_initialized)
{
CRASSERT(stub.spu);
stub.spu->dispatch_table.VBoxAttachThread();
}
break;
}
case DLL_THREAD_DETACH:
{
stubSetCurrentContext(NULL);
if (stub_initialized)
{
CRASSERT(stub.spu);
stub.spu->dispatch_table.VBoxDetachThread();
}
break;
}
default:
break;
}
return TRUE;
}
#endif