load.c revision c4f234c3ddb2f5b0b5dd04ab0d0ab5d3a8190668
0N/A/* Copyright (c) 2001, Stanford University
0N/A * All rights reserved
0N/A *
0N/A * See the file LICENSE.txt for information on redistributing this software.
0N/A */
0N/A
0N/A#include "cr_spu.h"
0N/A#include "cr_net.h"
0N/A#include "cr_error.h"
0N/A#include "cr_mem.h"
0N/A#include "cr_string.h"
0N/A#include "cr_net.h"
0N/A#include "cr_environment.h"
0N/A#include "cr_process.h"
0N/A#include "cr_rand.h"
0N/A#include "stub.h"
0N/A#include <stdlib.h>
0N/A#include <string.h>
873N/A#include <signal.h>
0N/A#ifndef WINDOWS
0N/A#include <sys/types.h>
0N/A#include <unistd.h>
0N/A#else
0N/A#include "cr_netserver.h"
4501N/A#endif
0N/A#ifdef CHROMIUM_THREADSAFE
1182N/A#include "cr_threads.h"
0N/A#endif
2N/A
0N/A/**
0N/A * If you change this, see the comments in tilesortspu_context.c
2N/A */
0N/A#define MAGIC_CONTEXT_BASE 500
1182N/A
0N/A#define CONFIG_LOOKUP_FILE ".crconfigs"
0N/A
0N/A#ifdef WINDOWS
1191N/A#define PYTHON_EXE "python.exe"
0N/A#else
0N/A#define PYTHON_EXE "python"
3853N/A#endif
0N/A
2N/Astatic int stub_initialized = 0;
2N/A
0N/A/* NOTE: 'SPUDispatchTable glim' is declared in NULLfuncs.py now */
0N/A/* NOTE: 'SPUDispatchTable stubThreadsafeDispatch' is declared in tsfuncs.c */
0N/AStub stub;
0N/A
0N/A
535N/Astatic void stubInitNativeDispatch( void )
2N/A{
0N/A#define MAX_FUNCS 1000
0N/A SPUNamedFunctionTable gl_funcs[MAX_FUNCS];
1183N/A int numFuncs;
759N/A
759N/A numFuncs = crLoadOpenGL( &stub.wsInterface, gl_funcs );
759N/A
759N/A stub.haveNativeOpenGL = (numFuncs > 0);
759N/A
2270N/A /* XXX call this after context binding */
2270N/A numFuncs += crLoadOpenGLExtensions( &stub.wsInterface, gl_funcs + numFuncs );
2270N/A
2270N/A CRASSERT(numFuncs < MAX_FUNCS);
2270N/A
2270N/A crSPUInitDispatchTable( &stub.nativeDispatch );
3853N/A crSPUInitDispatch( &stub.nativeDispatch, gl_funcs );
2350N/A crSPUInitDispatchNops( &stub.nativeDispatch );
2350N/A#undef MAX_FUNCS
0N/A}
3853N/A
0N/A
0N/A/** Pointer to the SPU's real glClear and glViewport functions */
535N/Astatic ClearFunc_t origClear;
759N/Astatic ViewportFunc_t origViewport;
0N/Astatic SwapBuffersFunc_t origSwapBuffers;
1409N/Astatic DrawBufferFunc_t origDrawBuffer;
2350N/A
2270N/Astatic void stubCheckWindowState(void)
2270N/A{
3853N/A int winX, winY;
0N/A unsigned int winW, winH;
4532N/A WindowInfo *window;
759N/A bool bForceUpdate = false;
1409N/A
2270N/A CRASSERT(stub.trackWindowSize || stub.trackWindowPos);
2350N/A
3184N/A if (!stub.currentContext)
3853N/A return;
0N/A
2350N/A window = stub.currentContext->currentDrawable;
1409N/A
2N/A stubGetWindowGeometry( window, &winX, &winY, &winW, &winH );
3988N/A
4532N/A#ifdef WINDOWS
4532N/A /* @todo install hook and track for WM_DISPLAYCHANGE */
4532N/A {
4532N/A DEVMODE devMode;
759N/A
759N/A devMode.dmSize = sizeof(DEVMODE);
2270N/A EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode);
0N/A
3853N/A if (devMode.dmPelsWidth!=window->dmPelsWidth || devMode.dmPelsHeight!=window->dmPelsHeight)
0N/A {
0N/A crDebug("Resolution changed(%d,%d), forcing window Pos/Size update", devMode.dmPelsWidth, devMode.dmPelsHeight);
0N/A window->dmPelsWidth = devMode.dmPelsWidth;
0N/A window->dmPelsHeight = devMode.dmPelsHeight;
0N/A bForceUpdate = true;
0N/A }
0N/A }
0N/A#endif
0N/A
0N/A stubUpdateWindowGeometry(window, bForceUpdate);
0N/A
0N/A#if defined(GLX) || defined (WINDOWS)
3853N/A if (stub.trackWindowVisibleRgn)
2N/A {
2N/A stubUpdateWindowVisibileRegions(window);
3853N/A }
2N/A#endif
3853N/A
2N/A if (stub.trackWindowVisibility && window->type == CHROMIUM && window->drawable) {
3853N/A const int mapped = stubIsWindowVisible(window);
2N/A if (mapped != window->mapped) {
3853N/A crDebug("Dispatched: WindowShow(%i, %i)", window->spuWindow, mapped);
3853N/A stub.spu->dispatch_table.WindowShow(window->spuWindow, mapped);
3853N/A window->mapped = mapped;
1409N/A }
2N/A }
2N/A}
1409N/A
1409N/A
2N/A/**
2N/A * Override the head SPU's glClear function.
2N/A * We're basically trapping this function so that we can poll the
2N/A * application window size at a regular interval.
2N/A */
2N/Astatic void SPU_APIENTRY trapClear(GLbitfield mask)
2N/A{
2N/A stubCheckWindowState();
2N/A /* call the original SPU glClear function */
2N/A origClear(mask);
2N/A}
2N/A
2N/A/**
2N/A * As above, but for glViewport. Most apps call glViewport before
2N/A * glClear when a window is resized.
2N/A */
2N/Astatic void SPU_APIENTRY trapViewport(GLint x, GLint y, GLsizei w, GLsizei h)
2N/A{
2N/A stubCheckWindowState();
2N/A /* call the original SPU glViewport function */
2N/A origViewport(x, y, w, h);
2N/A}
2N/A
2N/Astatic void SPU_APIENTRY trapSwapBuffers(GLint window, GLint flags)
2N/A{
2N/A stubCheckWindowState();
2N/A origSwapBuffers(window, flags);
2N/A}
2N/A
2N/Astatic void SPU_APIENTRY trapDrawBuffer(GLenum buf)
2N/A{
2N/A stubCheckWindowState();
2N/A origDrawBuffer(buf);
2N/A}
2N/A
2N/A/**
2N/A * Use the GL function pointers in <spu> to initialize the static glim
2N/A * dispatch table.
2N/A */
2N/Astatic void stubInitSPUDispatch(SPU *spu)
2N/A{
2N/A crSPUInitDispatchTable( &stub.spuDispatch );
2N/A crSPUCopyDispatchTable( &stub.spuDispatch, &(spu->dispatch_table) );
2N/A
2N/A if (stub.trackWindowSize || stub.trackWindowPos || stub.trackWindowVisibleRgn) {
2N/A /* patch-in special glClear/Viewport function to track window sizing */
2N/A origClear = stub.spuDispatch.Clear;
2N/A origViewport = stub.spuDispatch.Viewport;
2N/A origSwapBuffers = stub.spuDispatch.SwapBuffers;
2N/A origDrawBuffer = stub.spuDispatch.DrawBuffer;
2N/A stub.spuDispatch.Clear = trapClear;
2N/A stub.spuDispatch.Viewport = trapViewport;
2N/A /*stub.spuDispatch.SwapBuffers = trapSwapBuffers;
535N/A stub.spuDispatch.DrawBuffer = trapDrawBuffer;*/
535N/A }
535N/A
535N/A crSPUCopyDispatchTable( &glim, &stub.spuDispatch );
535N/A}
535N/A
535N/A// Callback function, used to destroy all created contexts
759N/Astatic void hsWalkStubDestroyContexts(unsigned long key, void *data1, void *data2)
759N/A{
759N/A stubDestroyContext(key);
759N/A}
759N/A
759N/A/**
759N/A * This is called when we exit.
2270N/A * We call all the SPU's cleanup functions.
2270N/A */
2270N/Astatic void stubSPUTearDown(void)
2270N/A{
2270N/A crDebug("stubSPUTearDown");
2270N/A if (!stub_initialized) return;
4501N/A
4501N/A stub_initialized = 0;
4501N/A
4501N/A#ifdef WINDOWS
4501N/A stubUninstallWindowMessageHook();
4501N/A#endif
4501N/A
3853N/A //delete all created contexts
3184N/A stubMakeCurrent( NULL, NULL);
2N/A crHashtableWalk(stub.contextTable, hsWalkStubDestroyContexts, NULL);
2N/A
2N/A /* shutdown, now trap any calls to a NULL dispatcher */
2N/A crSPUCopyDispatchTable(&glim, &stubNULLDispatch);
2N/A
2N/A crSPUUnloadChain(stub.spu);
2N/A stub.spu = NULL;
0N/A
0N/A crUnloadOpenGL();
0N/A
0N/A crNetTearDown();
0N/A
2N/A#ifdef GLX
0N/A if (stub.xshmSI.shmid>=0)
0N/A {
0N/A shmctl(stub.xshmSI.shmid, IPC_RMID, 0);
0N/A shmdt(stub.xshmSI.shmaddr);
0N/A }
0N/A#endif
0N/A
0N/A crMemset(&stub, 0, sizeof(stub) );
0N/A}
0N/A
0N/Astatic void stubSPUSafeTearDown(void)
0N/A{
0N/A#ifdef CHROMIUM_THREADSAFE
0N/A CRmutex *mutex = &stub.mutex;
0N/A crLockMutex(mutex);
3988N/A#endif
0N/A crDebug("stubSPUSafeTearDown");
3988N/A crNetTearDown();
0N/A#ifdef WINDOWS
0N/A stubUninstallWindowMessageHook();
0N/A#endif
0N/A crMemset(&stub, 0, sizeof(stub));
0N/A#ifdef CHROMIUM_THREADSAFE
0N/A crUnlockMutex(mutex);
0N/A crFreeMutex(mutex);
0N/A#endif
0N/A}
0N/A
0N/A
0N/Astatic void stubExitHandler(void)
0N/A{
0N/A stubSPUSafeTearDown();
0N/A}
0N/A
0N/A/**
0N/A * Called when we receive a SIGTERM signal.
0N/A */
0N/Astatic void stubSignalHandler(int signo)
0N/A{
0N/A stubSPUSafeTearDown();
0N/A exit(0); /* this causes stubExitHandler() to be called */
0N/A}
0N/A
0N/A
0N/A/**
0N/A * Init variables in the stub structure, install signal handler.
0N/A */
0N/Astatic void stubInitVars(void)
0N/A{
0N/A WindowInfo *defaultWin;
0N/A
0N/A#ifdef CHROMIUM_THREADSAFE
0N/A crInitMutex(&stub.mutex);
0N/A#endif
0N/A
0N/A /* At the very least we want CR_RGB_BIT. */
0N/A stub.haveNativeOpenGL = GL_FALSE;
0N/A stub.spu = NULL;
0N/A stub.appDrawCursor = 0;
0N/A stub.minChromiumWindowWidth = 0;
0N/A stub.minChromiumWindowHeight = 0;
0N/A stub.maxChromiumWindowWidth = 0;
0N/A stub.maxChromiumWindowHeight = 0;
0N/A stub.matchChromiumWindowCount = 0;
0N/A stub.matchChromiumWindowID = NULL;
0N/A stub.matchWindowTitle = NULL;
0N/A stub.ignoreFreeglutMenus = 1;
0N/A stub.threadSafe = GL_FALSE;
0N/A stub.trackWindowSize = 0;
2N/A stub.trackWindowPos = 0;
2N/A stub.trackWindowVisibility = 0;
2N/A stub.trackWindowVisibleRgn = 0;
2N/A stub.mothershipPID = 0;
2N/A stub.spu_dir = NULL;
2N/A
2N/A stub.freeContextNumber = MAGIC_CONTEXT_BASE;
2N/A stub.contextTable = crAllocHashtable();
2N/A stub.currentContext = NULL;
2N/A
2N/A stub.windowTable = crAllocHashtable();
2N/A
2N/A defaultWin = (WindowInfo *) crCalloc(sizeof(WindowInfo));
2N/A defaultWin->type = CHROMIUM;
535N/A defaultWin->spuWindow = 0; /* window 0 always exists */
535N/A#ifdef WINDOWS
759N/A defaultWin->hVisibleRegion = INVALID_HANDLE_VALUE;
759N/A#elif defined(GLX)
2270N/A defaultWin->pVisibleRegions = NULL;
2270N/A defaultWin->cVisibleRegions = 0;
2N/A#endif
2N/A crHashtableAdd(stub.windowTable, 0, defaultWin);
1409N/A
2N/A#if 1
2N/A atexit(stubExitHandler);
2N/A signal(SIGTERM, stubSignalHandler);
2N/A signal(SIGINT, stubSignalHandler);
2N/A#ifndef WINDOWS
535N/A signal(SIGPIPE, SIG_IGN); /* the networking code should catch this */
759N/A#endif
2270N/A#else
2N/A (void) stubExitHandler;
2N/A (void) stubSignalHandler;
1409N/A#endif
1409N/A}
1409N/A
2N/A
2N/A/**
2N/A * Return a free port number for the mothership to use, or -1 if we
2N/A * can't find one.
2N/A */
2N/Astatic int
2N/AGenerateMothershipPort(void)
2N/A{
2N/A const int MAX_PORT = 10100;
2N/A unsigned short port;
2N/A
2N/A /* generate initial port number randomly */
2N/A crRandAutoSeed();
2N/A port = (unsigned short) crRandInt(10001, MAX_PORT);
2N/A
535N/A#ifdef WINDOWS
535N/A /* XXX should implement a free port check here */
759N/A return port;
759N/A#else
2270N/A /*
2270N/A * See if this port number really is free, try another if needed.
2N/A */
2N/A {
2N/A struct sockaddr_in servaddr;
2N/A int so_reuseaddr = 1;
2N/A int sock, k;
2N/A
2N/A /* create socket */
2N/A sock = socket(AF_INET, SOCK_STREAM, 0);
2N/A CRASSERT(sock > 2);
535N/A
535N/A /* deallocate socket/port when we exit */
535N/A k = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
535N/A (char *) &so_reuseaddr, sizeof(so_reuseaddr));
535N/A CRASSERT(k == 0);
535N/A
535N/A /* initialize the servaddr struct */
535N/A crMemset(&servaddr, 0, sizeof(servaddr) );
535N/A servaddr.sin_family = AF_INET;
535N/A servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
759N/A
759N/A while (port < MAX_PORT) {
759N/A /* Bind to the given port number, return -1 if we fail */
759N/A servaddr.sin_port = htons((unsigned short) port);
759N/A k = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
759N/A if (k) {
759N/A /* failed to create port. try next one. */
759N/A port++;
759N/A }
759N/A else {
759N/A /* free the socket/port now so mothership can make it */
759N/A close(sock);
2270N/A return port;
2270N/A }
2270N/A }
2270N/A }
2270N/A#endif /* WINDOWS */
2270N/A return -1;
2270N/A}
2270N/A
2270N/A
2270N/A/**
2270N/A * Try to determine which mothership configuration to use for this program.
2270N/A */
3184N/Astatic char **
3184N/ALookupMothershipConfig(const char *procName)
3853N/A{
3184N/A const int procNameLen = crStrlen(procName);
3853N/A FILE *f;
3853N/A const char *home;
3184N/A char configPath[1000];
3853N/A
3853N/A /* first, check if the CR_CONFIG env var is set */
3853N/A {
3853N/A const char *conf = crGetenv("CR_CONFIG");
3853N/A if (conf && crStrlen(conf) > 0)
3853N/A return crStrSplit(conf, " ");
3853N/A }
3853N/A
3853N/A /* second, look up config name from config file */
3853N/A home = crGetenv("HOME");
3853N/A if (home)
3853N/A sprintf(configPath, "%s/%s", home, CONFIG_LOOKUP_FILE);
3853N/A else
3853N/A crStrcpy(configPath, CONFIG_LOOKUP_FILE); /* from current dir */
3853N/A /* Check if the CR_CONFIG_PATH env var is set. */
3184N/A {
3853N/A 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 = 1;
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;
}
/**
* Do one-time initializations for the faker.
* Returns TRUE on success, FALSE otherwise.
*/
bool
stubInit(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;
if (stub_initialized)
return true;
stub_initialized = 1;
stubInitVars();
/* @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 );
strcpy(response, "3 0 array 1 feedback 2 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) {
stub_initialized = 0;
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;
/* Load pointers to native OpenGL functions into stub.nativeDispatch */
stubInitNativeDispatch();
/*crDebug("stub init");
raise(SIGINT);*/
#ifdef WINDOWS
stubInstallWindowMessageHook();
#endif
#ifdef GLX
stub.xshmSI.shmid = -1;
stub.bShmInitFailed = GL_FALSE;
#endif
return true;
}
/* 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>
/* Windows crap */
BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
{
(void) lpvReserved;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
CRNetServer ns;
crNetInit(NULL, NULL);
ns.name = "vboxhgcm://host:0";
ns.buffer_size = 1024;
crNetServerConnect(&ns);
if (!ns.conn)
{
crDebug("Failed to connect to host (is guest 3d acceleration enabled?), aborting ICD load.");
return FALSE;
}
else
crNetFreeConnection(ns.conn);
break;
}
case DLL_PROCESS_DETACH:
{
stubSPUSafeTearDown();
break;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
default:
break;
}
return TRUE;
}
#endif