/****************************/
/*   MAC SPECIFIC.C			*/
/****************************/

/*	PangeaVR - 3D accelerated plug-in for viewing panoramas.

	İ2005 Pangea Software, Inc.  All rights reserved.
	Pangea Software, PangeaVR and various logos are trademarks of Pangea Software, Inc.
	
	Written by Brian Greenstone,   brian@pangeasoft.net	
	
	This program and source code is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
	
	IMPORTANT:  The purpose in releasing this source code is to encourage others
				To port this program to other platforms, and to make improvments to it.
				However, to prevent things from getting out of control with lots of
				different versions of PangeaVR floating around the internet,
				Pangea Software, Inc. prohibits the public distribution of any versions
				of PangeaVR without prior approval from Pangea Software, Inc.
				
				Should you make any improvements to this source code and wish to
				have those changes rolled into an official release then you'll
				need to contact Pangea Software to have your modifications adapted.				
*/


#ifdef TARGET_OS_MAC


/****************************/
/*    EXTERNALS             */
/****************************/

extern	MyInstanceType	gMyInstances[];
extern	int				gVideoSampleSize[];
extern	Ptr				gTempImageBuffer;
extern	Rect			gTempImageRect;
extern	short	gPrefsFolderVRefNum;
extern	long	gPrefsFolderDirID;


/****************************/
/*    PROTOTYPES            */
/****************************/

#pragma export on
// For compatibility with CFM browsers.
int main(NPNetscapeFuncs *browserFuncs, NPPluginFuncs *pluginFuncs, NPP_ShutdownProcPtr *shutdown);
#pragma export off



/****************************/
/*    CONSTANTS             */
/****************************/

#define kMyNibFileName					"OurResources"			// name of the Mac OS X resource file


/*********************/
/*    VARIABLES      */
/*********************/

GWorldPtr	gTempImageGWorld = nil;


static GDHandle 		gGDevice;

			/* MAC FILE VARIABLES */
			
CFBundleRef 		gBundle = nil;
IBNibRef 			gNibs = nil;

FSRef				gMyResourcesFolderFSRef;
FSSpec				gMyResourcesFolderFSSpec;






//******************************************************************************************
//
// THE FOLLOWING GIBBERISH IS THE CRAP THAT'S NEED TO MAKE THIS THING WORK IN MESSED UP CFM BROWSERS 
// LIKE FIREFOX.   FOR SAFARI, main() ISNT NEEDED, NOR IS ANY OF THIS MESS.
//


typedef void (* FunctionPointer) (void);
typedef void (* TransitionVector) (void);
static FunctionPointer functionPointerForTVector(TransitionVector);
static TransitionVector tVectorForFunctionPointer(FunctionPointer);

NPNetscapeFuncs* browser;


/***************************** MAIN **********************************/
//
// For compatibility with CFM browsers.
//

int main(NPNetscapeFuncs *browserFuncs, NPPluginFuncs *pluginFuncs, NPP_ShutdownProcPtr *shutdown)
{
    browser = malloc(sizeof(NPNetscapeFuncs));
    bzero(browser, sizeof(NPNetscapeFuncs));
    
    browser->size = browserFuncs->size;
    browser->version = browserFuncs->version;
    
    // Since this is a mach-o plug-in and the browser is CFM because it is calling main, translate
    // our function points into TVectors so the browser can call them.
    browser->geturl = (NPN_GetURLProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->geturl);
    browser->posturl = (NPN_PostURLProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->posturl);
    browser->requestread = (NPN_RequestReadProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->requestread);
    browser->newstream = (NPN_NewStreamProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->newstream);
    browser->write = (NPN_WriteProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->write);
    browser->destroystream = (NPN_DestroyStreamProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->destroystream);
    browser->status = (NPN_StatusProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->status);
    browser->uagent = (NPN_UserAgentProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->uagent);
    browser->memalloc = (NPN_MemAllocProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->memalloc);
    browser->memfree = (NPN_MemFreeProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->memfree);
    browser->memflush = (NPN_MemFlushProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->memflush);
    browser->reloadplugins = (NPN_ReloadPluginsProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->reloadplugins);
    browser->geturlnotify = (NPN_GetURLNotifyProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->geturlnotify);
    browser->posturlnotify = (NPN_PostURLNotifyProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->posturlnotify);
    browser->getvalue = (NPN_GetValueProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->getvalue);
    browser->setvalue = (NPN_SetValueProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->setvalue);
    browser->invalidaterect = (NPN_InvalidateRectProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->invalidaterect);
    browser->invalidateregion = (NPN_InvalidateRegionProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->invalidateregion);
    browser->forceredraw = (NPN_ForceRedrawProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->forceredraw);
    browser->getJavaEnv = (NPN_GetJavaEnvProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->getJavaEnv);
    browser->getJavaPeer = (NPN_GetJavaPeerProcPtr)functionPointerForTVector((TransitionVector)browserFuncs->getJavaPeer);
    
    /*
    // These functions are not yet supported in CFM browers like Netscape.
    // In the future, the versions of the vectors should be checked before accessing symbols.
    browser->releasevariantvalue = functionPointerForTVector((TransitionVector)browserFuncs->releasevariantvalue);
    browser->getstringidentifier = functionPointerForTVector((TransitionVector)browserFuncs->getstringidentifier);
    browser->getstringidentifiers = functionPointerForTVector((TransitionVector)browserFuncs->getstringidentifiers);
    browser->getintidentifier = functionPointerForTVector((TransitionVector)browserFuncs->getintidentifier);
    browser->identifierisstring = functionPointerForTVector((TransitionVector)browserFuncs->identifierisstring);
    browser->utf8fromidentifier = functionPointerForTVector((TransitionVector)browserFuncs->utf8fromidentifier);
    browser->createobject = functionPointerForTVector((TransitionVector)browserFuncs->createobject);
    browser->retainobject = functionPointerForTVector((TransitionVector)browserFuncs->retainobject);
    browser->releaseobject = functionPointerForTVector((TransitionVector)browserFuncs->releaseobject);
    browser->call = functionPointerForTVector((TransitionVector)browserFuncs->call);
    browser->evalute = functionPointerForTVector((TransitionVector)browserFuncs->evalute);
    browser->getproperty = functionPointerForTVector((TransitionVector)browserFuncs->getproperty);
    browser->setproperty = functionPointerForTVector((TransitionVector)browserFuncs->setproperty);
    browser->removeproperty = functionPointerForTVector((TransitionVector)browserFuncs->removeproperty);
    browser->setexception = functionPointerForTVector((TransitionVector)browserFuncs->setexception);
    */
    
    pluginFuncs->version = 11;
    pluginFuncs->size = sizeof(pluginFuncs);
    pluginFuncs->newp = (NPP_NewProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_New);
    pluginFuncs->destroy = (NPP_DestroyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_Destroy);
    pluginFuncs->setwindow = (NPP_SetWindowProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_SetWindow);
    pluginFuncs->newstream = (NPP_NewStreamProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_NewStream);
    pluginFuncs->destroystream = (NPP_DestroyStreamProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_DestroyStream);
    pluginFuncs->asfile = (NPP_StreamAsFileProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_StreamAsFile);
    pluginFuncs->writeready = (NPP_WriteReadyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_WriteReady);
    pluginFuncs->write = (NPP_WriteProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_Write);
    pluginFuncs->print = (NPP_PrintProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_Print);
    pluginFuncs->event = (NPP_HandleEventProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_HandleEvent);
    pluginFuncs->urlnotify = (NPP_URLNotifyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_URLNotify);
    pluginFuncs->getvalue = (NPP_GetValueProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_GetValue);
    pluginFuncs->setvalue = (NPP_SetValueProcPtr)tVectorForFunctionPointer((FunctionPointer)NPP_SetValue);
    
    *shutdown = (NPP_ShutdownProcPtr)tVectorForFunctionPointer((FunctionPointer)NP_Shutdown);
    

				/* CALL THE REAL INIT FUNCTION */
				
	NP_Initialize(browser);
	
	
    return NPERR_NO_ERROR;
}


// function pointer converters

FunctionPointer functionPointerForTVector(TransitionVector tvp)
{
    const uint32 temp[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420};
    uint32 *newGlue = NULL;
    
    if (tvp != NULL) {
        newGlue = (uint32 *)malloc(sizeof(temp));
        if (newGlue != NULL) {
            unsigned i;
            for (i = 0; i < 6; i++) newGlue[i] = temp[i];
            newGlue[0] |= ((UInt32)tvp >> 16);
            newGlue[1] |= ((UInt32)tvp & 0xFFFF);
            MakeDataExecutable(newGlue, sizeof(temp));
        }
    }
    
    return (FunctionPointer)newGlue;
}

TransitionVector tVectorForFunctionPointer(FunctionPointer fp)
{
    FunctionPointer *newGlue = NULL;
    if (fp != NULL) {
        newGlue = (FunctionPointer *)malloc(2 * sizeof(FunctionPointer));
        if (newGlue != NULL) {
            newGlue[0] = fp;
            newGlue[1] = NULL;
        }
    }
    return (TransitionVector)newGlue;
}





#pragma mark -







/************ DO PLATFORM SPECIFIC INIT : MAC *****************/

NPError DoPlatformSpecificInit_Mac(void)
{
OSErr			iErr;
CFURLRef		url;
long			createdDirID;

			/**************************************/
			/* FIND OUR MAC OS X BUNDLE RESOURCES */
			/**************************************/
			//
			// The following is all the code we need on the Mac in order
			// to get paths to files we need.
			//
			
    gBundle = CFBundleGetBundleWithIdentifier ( CFSTR( "com.pangea.vr" ) );
	iErr = CreateNibReferenceWithCFBundle(gBundle, CFSTR(kMyNibFileName), &gNibs);
	if (iErr)
	{
		printf("NP_Initialize: CreateNibReferenceWithCFBundle failed, %d\n", iErr);
		return(NPERR_GENERIC_ERROR);
	}


			/* GET MAC OS X PREFERENCES FOLDER */
			
	iErr = FindFolder(kOnSystemDisk,kPreferencesFolderType,kDontCreateFolder,				// locate the folder
					&gPrefsFolderVRefNum,&gPrefsFolderDirID);
	if (iErr != noErr)
		printf("Warning: Cannot locate the Preferences folder.\n");

	iErr = DirCreate(gPrefsFolderVRefNum,gPrefsFolderDirID,"\pPangeaVR",&createdDirID);		// make folder in there
	
	InitDefaultPrefs();
	LoadPrefs();	



				/* GET URL TO THE RESOURCES FOLDER */
				
	url = CFBundleCopyResourcesDirectoryURL (gBundle);
	if (url == nil)
	{
		printf("NP_Initialize: CFBundleCopyResourcesDirectoryURL failed, %d\n", iErr);
		return(NPERR_GENERIC_ERROR);
	}
	
			/* CONVERT THE URL TO AN FSREF */

	if (!CFURLGetFSRef(url, &gMyResourcesFolderFSRef))
	{
		printf("NP_Initialize: CFURLGetFSRef failed, %d\n", iErr);
	}

	
		/* ALSO GET THE FSSPEC WHILE WEĠRE HERE */
		//
		// FSGetCatalogInfo() will return a FSSpec for the Resources folder,
		// but the volume and directory ID's are for the parent folder, not the
		// actual Resources folder.  So, we do a trick by calling FSMakeFSSpec()
		// and we pass in the parent volume and directory ID along with a hardcoded
		// path to a file that we know is in our Resources folder:  OurResources.nib.
		// The result is a new FSSpec with volume and directory ID's that point
		// directly to the Resources folder.
		//

	iErr = FSGetCatalogInfo(&gMyResourcesFolderFSRef,
					kFSCatInfoNone,				// no catinfo needed
					nil, 
					nil,
					&gMyResourcesFolderFSSpec,	// fsspec to save into
					nil);	

	if (iErr != noErr)
		printf("DoPlatformSpecificInit_Mac: FSGetCatalogInfo failed! %d\n", iErr);



	iErr = FSMakeFSSpec(gMyResourcesFolderFSSpec.vRefNum,			// parent volume/directory info
						gMyResourcesFolderFSSpec.parID,
						"\p:Resources:logo.tif",					// path from parent to resource file
						&gMyResourcesFolderFSSpec);



	if (iErr != noErr)
		printf("DoPlatformSpecificInit_Mac: FSMakeFSSpec failed! %d\n", iErr);

	gMyResourcesFolderFSSpec.name[0] = 0;					// clear the name out of the FSSpec

    CFRelease(url);
	
	return(NPERR_NO_ERROR);
}


/******************** DO PLATFORM SPECIFIC SHUTDOWN : MAC *********************/

void DoPlatformSpecificShutdown_Mac(void)
{
	if (gNibs != nil)
	{
		DisposeNibReference(gNibs);
		gNibs = nil;
	}
	
#if 0	
	if (gBundle != nil)							// for some reason, Firefox and Netscape crash when we call CFRelease.  WTF?
	{
		CFRelease(gBundle);
		gBundle= nil;
	}
#endif

}

#pragma mark -



/********************** OGL:  CREATE DRAW CONTEXT : MAC ***********************/
//
// This is the Mac-specific code for creating an OpenGL draw context.
//

OSErr	OGL_CreateDrawContext_Mac(int instanceNum)
{
AGLPixelFormat 	fmt;
GLboolean      mkc, ok;
const GLint    attribWindow[]	= {AGL_RGBA, AGL_DOUBLEBUFFER, AGL_ALL_RENDERERS, AGL_ACCELERATED, AGL_NONE};
AGLDrawable		aglWin;


		/* IF THERE'S ALREADY A CONTEXT THEN JUST UPDATE IT */
		
	if (gMyInstances[instanceNum].drawContext)
	{
		aglUpdateContext(gMyInstances[instanceNum].drawContext);
		SetPaneClipRect_Mac(instanceNum); 
		OGL_DrawScene(instanceNum);
		return(1);
	}


			/***********************/
			/* CHOOSE PIXEL FORMAT */
			/***********************/
								
	gGDevice = GetMainDevice();										// Get the main device	
	fmt = aglChoosePixelFormat(&gGDevice, 1, attribWindow);			// create a pixel format object for this device

	if ((fmt == NULL) || (aglGetError() != AGL_NO_ERROR))
	{
		printf("OGL_InitDrawContext: aglChoosePixelFormat failed!\n");
		return(1);
	}

			/*****************************************/
			/* CREATE AGL CONTEXT & ATTACH TO WINDOW */
			/*****************************************/
			
	gMyInstances[instanceNum].drawContext = aglCreateContext(fmt, nil);						// create draw context
	if ((gMyInstances[instanceNum].drawContext == nil) || (aglGetError() != AGL_NO_ERROR))
	{
		printf("OGL_InitDrawContext: aglCreateContext failed!\n");
		return(1);
	}
				
	aglWin = (AGLDrawable)gMyInstances[instanceNum].grafPtr;
	ok = aglSetDrawable(gMyInstances[instanceNum].drawContext, aglWin);						// set the context's drawable to the grafPort
	if ((!ok) || (aglGetError() != AGL_NO_ERROR))
	{
		if (aglGetError() == AGL_BAD_ALLOC)
		{
			printf("Not enough VRAM to initialize the draw context.\n");	
			return(1);
		}
		else
		{
			printf("OGL_InitDrawContext: aglSetDrawable failed!\n");
			return(1);
		}
	}
	

			/* ACTIVATE CONTEXT */

	mkc = aglSetCurrentContext(gMyInstances[instanceNum].drawContext);
	if ((mkc == NULL) || (aglGetError() != AGL_NO_ERROR))
	{
		printf("OGL_InitDrawContext: aglSetCurrentContext failed!\n");
		return(1);
	}

			/* NO LONGER NEED PIXEL FORMAT */
			
	aglDestroyPixelFormat(fmt);




	return(noErr);
}


/***************** OGL:  DISPOSE DRAW CONTEXT : MAC ***********************/
//
// Disposes of all data created by OGL_InitDrawContext for a Mac draw context
//

void OGL_DisposeDrawContext_Mac(int instanceNum)
{
	aglSetCurrentContext(nil);								// make context not current
	aglSetDrawable(gMyInstances[instanceNum].drawContext, nil);
	aglDestroyContext(gMyInstances[instanceNum].drawContext);					// nuke the AGL context
}



#pragma mark -



/********************** SET PANE CLIP RECT : MAC ************************/
//
// Sets a buffer rect to clip just to the actual pane that our plug-in is supposed to draw into
// in this window.
//
// This code was tricky as hell.  Seems to work correctly tho :)
//

void SetPaneClipRect_Mac(int instanceNum)
{
GLint		bufferRect[4];
Rect		r;
int			x,y, cp;
int			clipHeight;
int			windowHeight;
NPWindow	*npWindow = gMyInstances[instanceNum].npWindow;
	
	if (gMyInstances[instanceNum].drawContext == nil)
		return;
	
	aglSetCurrentContext(gMyInstances[instanceNum].drawContext);
	
	
	x = npWindow->x;

			/* CALCULATING Y IS FUNKY */
			//
			// The BUFFER_RECT has y = 0 at the bottom of the window, not the top.
			//
			
	GetWindowBounds(gMyInstances[instanceNum].macWindow, kWindowContentRgn, &r);		// get size of actual Mac window

	windowHeight = r.bottom - r.top;

	cp = gMyInstances[instanceNum].paneClip.bottom - npWindow->y;

	y = windowHeight -													// calc y coord of top of window
		npWindow->y -													// offset y to browser's pane
		cp;																// offset again by the clipping factor

	clipHeight = gMyInstances[instanceNum].paneClip.bottom - gMyInstances[instanceNum].paneClip.top;


			/* SET CLIPPING RECT */
			//
			// This makes it so that OpenGL doesn't draw into the entire window.
			// It will only draw into the rect pane that we want.
			//
			
	bufferRect[0] = x;			
	bufferRect[1] = y;
	bufferRect[2] = gMyInstances[instanceNum].paneClip.right - x;
	bufferRect[3] = clipHeight;
	
	aglSetInteger (gMyInstances[instanceNum].drawContext, AGL_BUFFER_RECT, bufferRect);
	
	aglEnable (gMyInstances[instanceNum].drawContext, AGL_BUFFER_RECT);

			/* THIS SETS THE OPENGL VIEWPORT TO MATCH THE PANE */
						
	cp = y - npWindow->y;					// this corrects the y for y-scrolling that doesn't clip the top
	cp += gMyInstances[instanceNum].paneClip.bottom - 
			gMyInstances[instanceNum].paneHeight;

	cp -= (r.bottom - gMyInstances[instanceNum].paneClip.bottom); 
	cp += r.top;

			
	glViewport(0, cp, gMyInstances[instanceNum].paneWidth, gMyInstances[instanceNum].paneHeight);


	gMyInstances[instanceNum].aspectRatio = (float)gMyInstances[instanceNum].paneWidth/(float)gMyInstances[instanceNum].paneHeight;	
	
}


#pragma mark -


/************** CALC FRAMES PER SECOND : MAC *****************/
//
// This version uses UpTime() which is only available on PCI Macs.
//

void CalcFramesPerSecond_Mac(int instanceNum)
{
AbsoluteTime		currTime,deltaTime;
static AbsoluteTime time = {0,0};
Nanoseconds			nano;

	currTime = UpTime();

	deltaTime = SubAbsoluteFromAbsolute(currTime, time);
	nano = AbsoluteToNanoseconds(deltaTime);

	gMyInstances[instanceNum].fps = (float)kSecondScale / (float)nano.lo;

	if (gMyInstances[instanceNum].fps < 6)			// (avoid divide by 0's later)
		gMyInstances[instanceNum].fps = 6;
		
	gMyInstances[instanceNum].oneOverFPS = 1.0f/gMyInstances[instanceNum].fps;		// calc fractional for multiplication


	time = currTime;	// reset for next time interval
}


#pragma mark -


/************************** OGL_INIT FONT : MAC **************************/

void OGL_InitFont_Mac(int instanceNum)
{
  	aglSetCurrentContext(gMyInstances[instanceNum].drawContext);				// make context active
	
	gMyInstances[instanceNum].fontList = glGenLists(256);
 
    if (!aglUseFont(gMyInstances[instanceNum].drawContext, kFontIDHelvetica, bold,
		gMyInstances[instanceNum].fontSize, 0, 256, gMyInstances[instanceNum].fontList))
	{
		printf("OGL_InitFont: aglUseFont failed. OpenGL could not locate the font 'Helvetica' which is one of the default MacOS system fonts.  You should reinstall OS X to get this font back since it is needed by the operating system.");
	}
	else
		gMyInstances[instanceNum].fontLoaded = true;
	
}


/******************* OGL_FREE FONT : MAC ***********************/

void OGL_FreeFont_Mac(int instanceNum)
{
	if (gMyInstances[instanceNum].fontLoaded)
	{
		aglSetCurrentContext(gMyInstances[instanceNum].drawContext);				// make context active
	
		glDeleteLists(gMyInstances[instanceNum].fontList, 256);
		
		gMyInstances[instanceNum].fontLoaded = false;
	}
}


/**************** OGL_DRAW STRING : MAC ********************/

void OGL_DrawString_Mac(int instanceNum, Str255 s, GLint x, GLint y)
{

  	aglSetCurrentContext(gMyInstances[instanceNum].drawContext);				// make context active

	OGL_PushState();

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, 640, 0, 480, -10.0, 10.0);	
	
	glRasterPos2i(x, SPRITE_WINDOW_RANGE-y);	

	glColor4f(1,1,1,1);

	glListBase(gMyInstances[instanceNum].fontList);
	glCallLists(s[0], GL_UNSIGNED_BYTE, &s[1]);
	
	OGL_PopState();

}

#pragma mark -

/**************** JPEG TO TEXTURE : MAC ***********************/
//
// Draws the JPEG pointed to by jpegData, and then converts the pixel buffer
// into an OpenGL texture.
//
// INPUT:	instanceNum = plug-in instance index
//			i			= cube face # 0..5
//			jpegData	= ptr to jpeg data
//

OSErr	JPEGToTexture_Mac(int instanceNum, int	i, Ptr jpegData)
{
Rect	r;
OSErr	iErr;

				/* DECODE IT INTO TEMP GWORLD */
	
	iErr = DrawJPEGFromRAMIntoBuffer_Mac(jpegData, gVideoSampleSize[i], &gTempImageGWorld, &gTempImageBuffer);
	if (iErr)
	{
		printf("SeekAndExtractImages:  DrawJPEGFromRAMIntoBuffer failed!\n"); 
		return(1);
	}
	
	CalcAllowedTextureSize(instanceNum, &gTempImageRect, &r);


				/* ALLOCATE BUFFER & GWORLD FOR TEXTURE */
				//
				// The image is loaded above, but that's not necessarily the right size
				// so, we'll copybits it into this...
				//
				
	gMyInstances[instanceNum].textureBuffers[i] = malloc((int)r.right * (int)r.bottom * 4);		// alloc buffer to hold image with no padding
	if (gMyInstances[instanceNum].textureBuffers[i] == nil)
	{
		printf("SeekAndExtractImages: malloc failed!\n");
		return(1);
	}
	
	iErr = QTNewGWorldFromPtr(&gMyInstances[instanceNum].textureGWorlds[i], k32ARGBPixelFormat, &r, nil, nil, 0,
							gMyInstances[instanceNum].textureBuffers[i], (int)r.right * 4);
	if (iErr)
	{
		printf("SeekAndExtractImages: NewGWorld failed.  %d\n", iErr);
		return(1);
	}

			/* SCALED THE LOADED TEMP IMAGE DOWN INTO THE TEXTURE GWORLD & CREATE TEXTURE */
				
	gMyInstances[instanceNum].textureNames[i] = ScaleTempImageToTexture_Mac(instanceNum,
																		gMyInstances[instanceNum].textureGWorlds[i],
																		gMyInstances[instanceNum].textureBuffers[i],
																		&r,
																		gMyInstances[instanceNum].compressTextures); 


	return(noErr);
}



/**************** DRAW JPEG FROM RAM INTO BUFFER : MAC ***********************/
//
// Decompresses a JPEG who's compressed data is in RAM.
//
// This function uses OS X's Core Graphics calls to decode the JPEG pretty quickly.
//

OSErr DrawJPEGFromRAMIntoBuffer_Mac(Ptr jpegData, int size, GWorldPtr *theGWorld, Ptr *buffer)
{
OSErr						iErr;
short						width, height;
CGDataProviderRef			cgDataRef;
CGImageRef					cgImage;
CGRect						cgRect;
CGContextRef				cgContext;

			/*****************************************/
			/* CREATE A CG DATA REF TO THE JPEG DATA */
			/*****************************************/
			//
			// This basically creates a reference to Core Graphic's JPEG decoder
			//
			
	cgDataRef = CGDataProviderCreateWithData(jpegData, jpegData, size, nil);
	if (cgDataRef == nil)
	{
		printf("DrawJPEGFromRAMIntoBuffer:  CGDataProviderCreateWithData failed!\n");
		return(1);
	}
	
    cgImage = CGImageCreateWithJPEGDataProvider (cgDataRef, NULL, true, kCGRenderingIntentDefault);	
	if (cgImage == nil)
	{
		printf("DrawJPEGFromRAMIntoBuffer:  CGImageCreateWithJPEGDataProvider failed!\n");
		return(1);
	}
				

				/****************************/
				/* GET IMAGE WIDTH / HEIGHT */
				/****************************/
				//
				// Before decoding, let's get the size of the image so we can
				// create our buffers to draw it into.
				//
				
	width = CGImageGetWidth(cgImage);
	height = CGImageGetHeight(cgImage);

	cgRect.origin.x = 
	cgRect.origin.y = 0;
	cgRect.size.width = width;
	cgRect.size.height = height;

	gTempImageRect.top = gTempImageRect.left = 0;
	gTempImageRect.right = width;
	gTempImageRect.bottom = height;


	printf("JPEG data:  width/height  %d / %d\n", width, height);


				/**************************/
				/* CREATE CG DRAW CONTEXT */
				/**************************/
				//
				// First we allocate a buffer that will be our image.
				// Then we make a GrafPtr/GWorld for that buffer.
				// And the we tell Core Graphics to draw the JPEG into that GWorld.
				//

				/* ALLOCATE THE PIXEL BUFFER & GWORLD */
				
	if (*buffer)														// dispose any existing image (just to be sure)
		free(*buffer);
		
	*buffer = malloc((int)width * (int)height * 4);						// alloc buffer to hold image with no padding
	if (*buffer == nil)
	{
		printf("malloc failed!\n");
		return(1);	
	}
	
	iErr = QTNewGWorldFromPtr(theGWorld, k32ARGBPixelFormat, &gTempImageRect, nil, nil, 0, *buffer, width * 4);
	if (iErr)
	{
		printf("DrawJPEGFromRAMIntoBuffer: NewGWorld failed\n");
		return(1);
	}


				/* CONVERT GWORLD'S GRAFPTR TO A CGCONTEXT */
				
	iErr = QDBeginCGContext ((CGrafPtr)*theGWorld,  &cgContext);
	if (iErr)
	{
		printf("DrawJPEGFromRAMIntoBuffer: QDBeginCGContext failed.  %d\n", iErr);
		return(1);
	} 
								


				/* DRAW THE IMAGE */
					
    CGContextDrawImage (cgContext, cgRect, cgImage); 
    CGImageRelease (cgImage); 

	iErr = QDEndCGContext((CGrafPtr)*theGWorld, &cgContext);
	if (iErr)
	{
		printf("DrawJPEGFromRAMIntoBuffer: QDEndCGContext failed.  %d\n", iErr);
		return(1);
	}


			/* CLEAN UP */
		
    CGDataProviderRelease (cgDataRef); 
	CGContextRelease(cgContext);
		
	return(noErr);
}



/******************** SCALE TEMP IMAGE TO TEXTURE **************************/
//
// Draws the image in gTempImageGWorld into the input buffer while resizing it.
//
// INPUT:  gworld / buffer = buffer to draw into.
//			textureRect = buffer's rect
//			compressTextures = flag to use hardware compression or not.
//

GLint ScaleTempImageToTexture_Mac(int instanceNum, GWorldPtr	gworld, Ptr	buffer, Rect *textureRect, Boolean compressTextures)
{
GLint		textureName;

		/*******************************************/
		/* SCALE IMAGE FROM TEMP BUFFER TO TEXTURE */
		/*******************************************/
	
	
	CopyPixelsFromBufferToBuffer_Mac(gTempImageGWorld, gworld, &gTempImageRect, textureRect);

			
			/* FREE THE TEMP BUFFER */
			
	if (gTempImageBuffer)
	{
		free(gTempImageBuffer);
		gTempImageBuffer = nil;
	}
	if (gTempImageGWorld)
	{
		DisposeGWorld(gTempImageGWorld);
		gTempImageGWorld = nil;
	}
			
	
	textureName = MakeTextureFromBuffer(gMyInstances[instanceNum].nonPower2, buffer,
										textureRect->right, textureRect->bottom, false,
										compressTextures);

	return(textureName);
}


#pragma mark -


/**************** DRAW TIFF INTO GWORLD : MAC ***********************/
//
// Uses Quicktime to load any kind of picture format file and draws
// it into the GWorld.  In this app we're using it to load and draw TIFF's.
//
//
// INPUT: myFSSpec = spec of image file
//
// OUTPUT:	theGWorld = gworld contining the drawn image.
//

OSErr DrawTIFFIntoBuffer_Mac(FSSpec *myFSSpec, Ptr *buffer)
{
OSErr						iErr;
GraphicsImportComponent		gi;
ComponentResult				result;
PixMapHandle 				hPixMap;
GWorldPtr					theGWorld;

			/* PREP IMPORTER COMPONENT */
			
	result = GetGraphicsImporterForFile(myFSSpec, &gi);		// load importer for this image file
	if (result != noErr)
	{
		printf("DrawTIFFIntoBuffer: GetGraphicsImporterForFile failed! %d\n", result);
		return(result);
	}
	if (GraphicsImportGetBoundsRect(gi, &gTempImageRect) != noErr)		// get dimensions of image
		printf("DrawTIFFIntoBuffer: GraphicsImportGetBoundsRect failed!");

	printf("image size = %d x %d\n", gTempImageRect.right, gTempImageRect.bottom);


			/************************/
			/* MAKE BUFFER & GWORLD */
			/************************/
			
	if (*buffer)												// dispose any existing image (just to be sure)
		free(*buffer);
		
	*buffer = malloc((int)gTempImageRect.right * 
						(int)gTempImageRect.bottom * 4);		// alloc buffer to hold image with no padding
	if (*buffer == nil)
	{
		printf("malloc failed!\n");
		return(1);	
	}
	
	iErr = QTNewGWorldFromPtr(&theGWorld, k32ARGBPixelFormat, &gTempImageRect, nil, nil, 0,
							*buffer, (int)gTempImageRect.right * 4);
	if (iErr)
	{
		printf("DrawTIFFIntoBuffer: NewGWorld failed\n");
		return(1);
	}
	
		hPixMap = GetGWorldPixMap(theGWorld);				// get gworld's pixmap
		(**hPixMap).cmpCount = 4;							// we want full 4-component argb (defaults to only rgb)
	


			/* DRAW INTO THE GWORLD */
	
	GraphicsImportSetGWorld(gi, theGWorld, nil);			// set the gworld to draw image into
	GraphicsImportSetQuality(gi,codecLosslessQuality);		// set import quality

	result = GraphicsImportDraw(gi);						// draw into gworld
	CloseComponent(gi);										// cleanup
	
	if (result != noErr)
	{
		printf("DrawTIFFIntoBuffer: GraphicsImportDraw failed!\n");
		DisposeGWorld (theGWorld);
		return(result);
	}
	
	
	return(noErr);
}

#pragma mark -

/******************** COPY PIXELS FROM BUFFER TO BUFFER ******************/
//
// Copy GWorld with designated Rect
//

void CopyPixelsFromBufferToBuffer_Mac(GWorldPtr fromWorld, GWorldPtr toWorld, const Rect *fromRect, const Rect *toRect)
{
PixMapHandle pmFrom,pmTo;
GDHandle	oldGD;
GWorldPtr	oldGW;
Rect		fromRect2, toRect2;
OSErr		iErr;

	GetGWorld (&oldGW,&oldGD);
	SetGWorld(fromWorld,nil);	

	pmFrom = GetGWorldPixMap(fromWorld);				// get pixel maps
	if ((pmFrom == nil) || (*pmFrom == nil) )
		printf("CopyPixelsFromBufferToBuffer_Mac: GetGWorldPixMap(fromWorld) failed!\n");

	pmTo = GetGWorldPixMap(toWorld);	
	if (pmTo == nil)
	{
		printf("CopyPixelsFromBufferToBuffer_Mac: GetGWorldPixMap(toWorld) failed!  pmTo == nil\n");
		goto bail;
	}
	if (*pmTo == nil)
	{
		printf("CopyPixelsFromBufferToBuffer_Mac: GetGWorldPixMap(toWorld) failed!  *pmTo == nil\n");
		goto bail;
	}

	ForeColor(blackColor);
	BackColor(whiteColor);


			/* MAKE SURE RECT IN BOUNDS */

	fromRect2 = *fromRect;
	toRect2 = *toRect;

#if 1

	if (fromRect2.bottom > (**pmFrom).bounds.bottom)
	{
		int	diff = fromRect2.bottom - (**pmFrom).bounds.bottom;
		fromRect2.bottom -= diff;
		toRect2.bottom -=diff;
	}

	if (fromRect2.right > (**pmFrom).bounds.right)
	{
		int	diff = fromRect2.right - (**pmFrom).bounds.right;
		fromRect2.right -= diff;
		toRect2.right -=diff;
	}
#endif



	CopyBits(GetPortBitMapForCopyBits(fromWorld), GetPortBitMapForCopyBits(toWorld),
			&fromRect2,&toRect2,srcCopy,0);

	iErr = QDError();
	if (iErr != noErr)
		printf("CopyBits failed!  %d\n", iErr);

bail:
	SetGWorld(oldGW,oldGD);
}



#pragma mark -

/**************** HANDLE BROWSER EVENT - MAC ******************/


void HandleBrowserEvent_Mac(int i, void *event)
{
EventRecord  *theEvent = event;
float		cubeDX, cubeDY;
Boolean		redraw = false;
Point		mouseCoordInPane;


	if (gMyInstances[i].browserType != BROWSER_SAFARI)			// only Safari works correctly.  Netscape and Firefox give us window coords, not plug-in pane coords like they're SUPPOSED to.
	{
		SetOrigin(gMyInstances[i].pluginPortX, gMyInstances[i].pluginPortY);
	}


	mouseCoordInPane = theEvent->where;											// get mouse coords in the plug-in's pane
	GlobalToLocal(&mouseCoordInPane);



	switch(theEvent->what)
	{
					/**************************************/
					/* THINGS TO DO DURING THE NULL EVENT */
					/**************************************/
					//
					// Safari seems to issue null events at a constant
					// rate of 50 times per second.  The main thing we do
					// here is determine if the cube needs updating - if so
					// then we draw it.
					//
					
		case	nullEvent:

						/* SEE IF CUBE STILL SPINNING */
						
				cubeDX = gMyInstances[i].cubeRotDX;							// get instance's cube deltas
				cubeDY = gMyInstances[i].cubeRotDY;
		
				if ((fabs(cubeDX) > 0.001f) || (fabs(cubeDY) > 0.001f))		// if there's motion, then keep updating
				{
					UpdateCubeDrift(i, true);
					redraw = true;
				}
				
				
						/* SEE IF ZOOM IN/OUT */
						
				if (IsMouseInPluginPane(i, mouseCoordInPane.h, mouseCoordInPane.v))		// is the mouse in the plug-in's window?
				{
					UInt32	modifiers = GetCurrentEventKeyModifiers();		// getting the modifiers this way insures that we don't get modifier hits if we're not the front process!
					
					if (modifiers & (1<<shiftKeyBit))						// shift == zoom in
					{
						gMyInstances[i].fov	-= gMyInstances[i].oneOverFPS * FOV_ZOOM_SPEED;
						if (gMyInstances[i].fov < MIN_FOV)
							gMyInstances[i].fov = MIN_FOV;
						redraw = true;
						gMyInstances[i].hiliteZoomIn = true;
					}
					else
					if (modifiers & (1<<controlKeyBit))							// ctrl == zoom out
					{
						gMyInstances[i].fov	+= gMyInstances[i].oneOverFPS * FOV_ZOOM_SPEED;
						if (gMyInstances[i].fov > MAX_FOV)
							gMyInstances[i].fov = MAX_FOV;
						redraw = true;
						gMyInstances[i].hiliteZoomOut = true;
					}
				}
								
				
						/* MISC TESTS */
				
				if (gMyInstances[i].cubeFade < 1.0f)						// see if still fading in
					redraw = true;
					
				if (gMyInstances[i].loadingFade	> 0.0f)						// see if still fading out loading sprite
					redraw = true;
						
				if (gMyInstances[i].controllerMoved)						// if controller moved then we need to refresh
					redraw = true;
				
						/* REDRAW */
						
				if (redraw)
				{
					OGL_DrawScene(i);
				}
				else
				{
					if (gMyInstances[i].hiliteZoomIn || gMyInstances[i].hiliteZoomOut)	// see if need to turn off any hilited buttons
					{
						gMyInstances[i].hiliteZoomIn = false;
						gMyInstances[i].hiliteZoomOut = false;
						OGL_DrawScene(i);					
					}
					else
					{
						CalcFramesPerSecond_Mac(i);						// keep calling this even tho not drawing
					}
				}				
				break;
	

		case	mouseDown:
		
//				printf("mouse x/y = %d / %d\n", mouseCoordInPane.h, mouseCoordInPane.v);
		
				if (!CheckClickOnControllerIcon(i, mouseCoordInPane.h, mouseCoordInPane.v))		// see if clicked on controller icon
					InteractivelyDragCube(i);													// no, so interactivly drag cube
				break;


		case	keyDown:
				if (IsMouseInPluginPane(i, mouseCoordInPane.h, mouseCoordInPane.v))									// is the mouse in the plug-in's window?
				{
//					printf("%d\n",theEvent->message);	//-------
					
					if (theEvent->message == 31248)				// F1 key
					{
						DoPrefsDialog(i);
					}
				}
				break;

			
    }


}


#endif


