
/*
     PLIB - A Suite of Portable Game Libraries
     Copyright (C) 1998,2002  Steve Baker

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
     version 2 of the License, or (at your option) any later version.

     This library 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.  See the GNU
     Library General Public License for more details.

     You should have received a copy of the GNU Library General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

     For further information visit http://plib.sourceforge.net

     $Id: pwMacOSX.cxx 1928 2004-04-06 23:45:19Z sjbaker $
*/


#include "ul.h"

/* ONLY COMPILE THIS FILE FOR MACINTOSH OR MACOSX SYSTEMS */

/* PLEASE DON'T REMOVE THIS LINE AGAIN!!! */
#if defined(UL_MACINTOSH) || defined(UL_MAC_OSX)
/* YES - THAT MEANS YOU! */


/*
*
*  - the fullscreen mode is indeed a "game mode";
*  - the fullscreen mode does not change the resolution of the screen:
*		it uses the resolution (width, height and depth) in use before entering pw;
*  - it is possible to toggle between modes by calling pwCleanup()
*		and calling again pwInit;
*  - the "border" parameter in pwInit is not used;
*  - the "about" menu shows a standard alert box with the current version of plib
*		and with the url to the plib official site.
*
*-----------------------------------------
*
*  - with Mac OS 8.6 to 9.2:
*		- must be linked with the following libraries:
*			InterfaceLib, accessors.o, AppearanceLib, DrawSprocketLib
*			and OpenGL (agl is in OpenGL)
*
*		- ACTIVE_SLEEPTIME	must be defined to 0 for the fastest execution; 
*			but it will not let other app to get events.
*
*  - with Mac OS X: must be linked with the following frameworks:
*			Carbon, AGL
*
*
*  - if not using Xcode, the following compiler falgs should be set to avoid errors or warnings
*		CPPFLAGS = -fpascal-strings -funsigned-char
*/

/*  version of 2004-04-03
*   - changes from previous version:
*		- fullscreen also implemented with mac OS 8.6 and 9;
*		- key up events are now reported in mac OS 8.6 to 9.2
*			(these events are disabled by default...)
*		- multisample added for mac OS X;
*		- no auto-key repeat event by default (can be set by pwSetAutoRepeatKey(bool on_off) );
*		- better error handling and use of ulSetError;
*/

#if defined (UL_MAC_OSX)
#  define TARGET_API_MAC_CARBON 1
#  include <Carbon/Carbon.h>
#  include <AGL/agl.h>
#elif defined (UL_MACINTOSH)
//#  define TARGET_API_MAC_CARBON 0
#  undef ACCESSOR_CALLS_ARE_FUNCTIONS		// already defined by ul.h !!!
#  define ACCESSOR_CALLS_ARE_FUNCTIONS 1
#  include <Events.h>
#  include <MacWindows.h>
#  include <Dialogs.h>
#  include <ToolUtils.h>
#  include <Quickdraw.h>
#  include <Menus.h>
#  include <Gestalt.h>
#  include <Appearance.h>
#  include <Devices.h>
#  include <CodeFragments.h>
#  include <agl.h>
#  include <DrawSprocket.h>
//#else
//#  error only for Mac OS (8.6 to OS X) operating system!
#endif


#include <stdlib.h>
#include <stdio.h>
#include "pw.h"


/* Apple menu: */
#define mApple	    128
#define	iAbout	    1
/* File menu: */
#ifdef UL_MAC_OSX
#  define ACTIVE_SLEEPTIME	0
#  define INACTIVE_SLEEPTIME  10
#  define mFile		0
#  define iQuit		0
#else   // UL_MACINTOSH
#  define ACTIVE_SLEEPTIME	1		// 0 is fastest, but does not let other app to catch events.
									// set to 0 if you want your app only to have CPU time
#  define INACTIVE_SLEEPTIME  10
#  define mFile	    129
#  define iQuit	    1
#endif

static bool			pwInitialized = false ;
static int          origin [2]   = {   0,   0 } ;
static int          size   [2]   = { 640, 480 } ;
static SInt16		horScrSze, verScrSze;			// screen dimensions
static bool			full_screen;
static bool			auto_repeat_key = false ;
#if TARGET_API_MAC_CARBON
static bool			pwQuitFlag ;
#endif
static int 			modifiers ;
static int			click_modifiers ;
static SInt32 		sleepTime = ACTIVE_SLEEPTIME;

static pwResizeCB     *resizeCB = NULL ;
static pwExitCB       *exitCB   = NULL ;
static pwKeybdFunc    *kbCB     = NULL ;
static pwMouseFunc    *msCB     = NULL ;
static pwMousePosFunc *mpCB     = NULL ;

#if TARGET_API_MAC_CARBON
  static WindowPtr 	pwWindow = NULL ;
# define SETPORT(w) SetPortWindowPort(w)
# define GRAFPTR
#else
  static CWindowPtr	pwWindow = NULL ;
# define SETPORT(w) SetPort((GrafPtr)w)
# define GRAFPTR (GrafPtr)
#endif

#ifdef UL_MACINTOSH
// DrawSprocket stuff:
static DSpContextReference		dspContext;
#endif

// generic OpenGL stuff
static AGLContext   	currContext = NULL ;

static void HandleEvents(void);
static void Initialize(void);
static bool MakeWindow(int x, int y, int w, int h, char* title);
static void CreateContext(int multisample, int num_samples, bool fullscreen);
static void MakeMenu(void);
static void handleMenuEvent(long menuResult);
static void handleKeyEvent(EventRecord* eventPtr, int updown);
static void handleMouseMoveEvt(EventRecord* eventPtr);
#if TARGET_API_MAC_CARBON
static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon);
#endif
static void pwAboutBox(void);
static void defaultExitFunc ();
#ifdef UL_MACINTOSH
static bool InitDSP();
#endif


static void CtoPcpy( Str255 pstr, char *cstr );	


#if TARGET_API_MAC_CARBON
static pascal OSErr 
QuitAppleEventHandler( const AppleEvent*, AppleEvent*, long )
{
	pwQuitFlag =  true;
	return(noErr);
}
#endif

static void Initialize()
{
	OSErr	err = noErr;
	BitMap  scrBits;

#if !TARGET_API_MAC_CARBON
	InitGraf(&qd.thePort);
	InitFonts();
	FlushEvents(everyEvent, 0);
	InitWindows();
	InitMenus();
	InitDialogs(nil);
	sleepTime = ACTIVE_SLEEPTIME;
	SetEventMask(everyEvent); 		// to enable key up events !!!
#else
	pwQuitFlag = false;
	err = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false );
  // this funtion is to handle the "cmd+Q" event. This key event is not send as a keyDown event with carbon!
	if (err != noErr)
	{
		ulSetError(UL_FATAL, "Error in initialization of the system." );
	}
#endif
	InitCursor();
	exitCB = defaultExitFunc;
	// get screen size:
	GetQDGlobalsScreenBits(&scrBits);
	horScrSze = scrBits.bounds.right -  scrBits.bounds.left;
	verScrSze = scrBits.bounds.bottom -  scrBits.bounds.top;
}

// Copy a C string into a Pascal string
static void CtoPcpy(  Str255 pstr, char* cstr )
{
	int		i = 1;	
	while( (*cstr) && (i < 255) )  pstr[i++] = *cstr++;
	pstr[0] = i - 1;
}

static bool MakeWindow(int x, int y, int w, int h, char* title)
{
	Rect		wRect;
	Str255		pTitle;
	
	CtoPcpy( pTitle, title );
	SetRect(&wRect, x, y, w+x, h+y);
#if TARGET_API_MAC_CARBON
	pwWindow = NewCWindow(nil, &wRect, pTitle, true, zoomNoGrow, (WindowPtr) -1, true, 0);
#else
	if (full_screen)		// use DrawSprocket
		pwWindow = (CWindowPtr) NewCWindow (nil, &wRect, pTitle, true, plainDBox, (WindowPtr)-1, 0, 0); 
	else
		pwWindow = (CWindowPtr) NewCWindow(nil, &wRect, pTitle, true, zoomNoGrow, (WindowPtr) -1, true, 0);
#endif
	if (pwWindow == NULL)
	{
		ulSetError(UL_WARNING, "pw: can not open a window; will exit");
		return false;
	}
	SETPORT(pwWindow);
	return true;
}

static void CreateContext(int multisample, int num_samples, bool fullscreen)
{
	AGLPixelFormat aglPixFmt;
	// Choose pixel format:
	GLint attrib[10];
	GLint i = 0;
	attrib[i++] = AGL_RGBA;
	attrib[i++] = AGL_DOUBLEBUFFER;
#ifdef UL_MAC_OSX
	if (fullscreen) attrib[i++] = AGL_FULLSCREEN;
#endif
	//attrib[i++] = AGL_DEPTH_SIZE;
	//attrib[i++] = 24;
	//attrib[i++] = AGL_ALL_RENDERERS;
	attrib[i++] = AGL_NONE;
	
	if (multisample)
	{
#ifdef UL_MAC_OSX
		i--;
		attrib[i++] = AGL_SAMPLE_BUFFERS_ARB ; 
		attrib[i++] = 1 ;
		attrib[i++] = AGL_SAMPLES_ARB ;
		attrib[i++] = num_samples;
		attrib[i++] = AGL_NONE;
		aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib);
		if (aglPixFmt == NULL)
		{
			multisample = 0;	// retry without
			ulSetError(UL_WARNING, "pw: multisample pixel format not found");
			i -= 5;
			attrib[i++] = AGL_NONE;
		}
#else
			multisample = 0;	// retry without
			ulSetError(UL_WARNING, "pw: multisample not implemented with MacOS 8 & 9"); 
#endif
	}
	if (!multisample)
	{
		aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib);
	}
	if (aglPixFmt == NULL)
	{
		ulSetError(UL_FATAL, "pw: can not find a pixel format");
	}
	
	// Create an AGL context
	currContext = aglCreateContext(aglPixFmt, NULL);
	aglDestroyPixelFormat ( aglPixFmt );
	if (currContext == NULL)
	{
		ulSetError(UL_FATAL, "pw: can not create an OpenGL context\n");
	}
	// swap buffers only every k vertical retrace:
	//long k = 100;
	//aglSetInteger(currContext, AGL_SWAP_INTERVAL, &k);
}

void MakeMenu()
{
	OSErr err;
	long response;
	MenuHandle menu = NewMenu(mApple, "\p\024");
	InsertMenu(menu, 0);

	InsertMenuItem(menu, "\pAbout Plib...", 0);

#if !TARGET_API_MAC_CARBON
	AppendResMenu(menu, 'DRVR');
#endif

	// If we not running on OS X then we need to add a File:Quit command: 
	err = Gestalt(gestaltSystemVersion, &response );

	if (err != noErr || response < 0x00001000)
	{
		menu = NewMenu (mFile, "\pFile");			// new menu
		InsertMenu (menu, 0);						// add menu to end
		AppendMenu (menu, "\pQuit/Q"); 				// add items
	}
	
	DrawMenuBar();
}

static void defaultExitFunc ()
{
  pwCleanup () ;
  exit(0);
}

void pwSetCallbacks( pwKeybdFunc    *kb, pwMouseFunc *ms,
					 pwMousePosFunc *mp, pwResizeCB *rcb,
					 pwExitCB      *ecb  )
{
	resizeCB = rcb;
	exitCB   = (ecb == NULL) ?  defaultExitFunc : ecb ;
	kbCB     = kb ;
	msCB     = ms ;
	mpCB     = mp ;
}

int pwGetModifiers()
{
	return modifiers;
}

void pwSetAutoRepeatKey( bool on_off )
{
	auto_repeat_key = on_off;
}

static void pwAboutBox()
{
	// show a dialog box with info on plib:
	SInt16 outItemHit;
	char version[32];
	Str255 Pversion;
	sprintf(version, "PLIB v %i.%i.%i", PLIB_MAJOR_VERSION, PLIB_MINOR_VERSION, PLIB_TINY_VERSION );
	CtoPcpy( Pversion, version );
	StandardAlert ( kAlertPlainAlert,
   					Pversion,
   					"\pfor more infos see <http://plib.sourceforge.net>",
   					NULL,
   					&outItemHit  );
}

static void handleMenuEvent(long menuCmd)
{
#if !TARGET_API_MAC_CARBON
	Str255		DAName;
#endif
	int menuNum = HiWord(menuCmd);
	int itemNum = LoWord(menuCmd);
	switch (menuNum)
	{
		case mApple:	// apple menu:
		{
			switch (itemNum)
			{
				case iAbout: pwAboutBox(); break;
				default:
#if !TARGET_API_MAC_CARBON
					GetMenuItemText(GetMenuHandle(mApple), itemNum, DAName);
					OpenDeskAcc(DAName);
#endif
					break;
			}
			break;
		}
		case mFile:		// File menu:
		{
			switch (itemNum)
			{
				case iQuit: (*exitCB)(); break;
				default: ;
			}
		}
	}
	HiliteMenu(0);  // remove. the highlight on the selected menu.
}

static int translateKey(int ch, int key)
{
	/*
	if ( modifiers & PW_ALT )
	{
		UInt32 state = 0;
		long keyResult = KeyTranslate ( (void *) GetScriptManagerVariable (smKCHRCache),
										(UInt16) key, &state );
		ch = 0xFF & keyResult;
	}
	*/
	// special characters:
	switch (ch)
	{
		case kLeftArrowCharCode:  	ch = PW_KEY_LEFT;    break;
		case kUpArrowCharCode:  	ch = PW_KEY_UP;      break;
		case kRightArrowCharCode:  	ch = PW_KEY_RIGHT;   break;
		case kDownArrowCharCode:  	ch = PW_KEY_DOWN;    break;
		case kPageUpCharCode:  		ch = PW_KEY_PAGE_UP; break;
		case kPageDownCharCode:  	ch = PW_KEY_PAGE_DOWN; break;
		case kHomeCharCode:  		ch = PW_KEY_HOME;    break;
		case kEndCharCode:  		ch = PW_KEY_END;     break;
		case kHelpCharCode:  		ch = PW_KEY_INSERT;  break;
		case kFunctionKeyCharCode:
			// fonction keys:
			switch(key)		
			{
				case 0x7A:  ch = PW_KEY_F1;   break;
				case 0x78:  ch = PW_KEY_F2;   break;
				case 0x63:  ch = PW_KEY_F3;   break;
				case 0x76:  ch = PW_KEY_F4;   break;
				case 0x60:  ch = PW_KEY_F5;   break;
				case 0x61:  ch = PW_KEY_F6;   break;
				case 0x62:  ch = PW_KEY_F7;   break;
				case 0x64:  ch = PW_KEY_F8;   break;
				case 0x65:  ch = PW_KEY_F9;   break;
				case 0x6D:  ch = PW_KEY_F10;  break;
				case 0x67:  ch = PW_KEY_F11;  break;
				case 0x6F:  ch = PW_KEY_F12;  break;
				default: break;
			}
			break;
		default: ; 
	}
	return ch;
}

static void handleKeyEvent(EventRecord* eventPtr, int updown)
{
	GrafPtr		origPort;
	
	if ( ! kbCB ) return;
	// handle key_events:
	int ch	= eventPtr->message & charCodeMask ;		// character pressed
	int key	= (eventPtr->message & keyCodeMask) >> 8 ;	// key pressed
	ch = translateKey(ch, key);
	GetPort(&origPort);
	SETPORT(pwWindow);
	GlobalToLocal(&eventPtr->where);
	(*kbCB) ( ch, updown, eventPtr->where.h, eventPtr->where.v ) ;
	SetPort(origPort);
	return;
}


static void handleDrag(WindowPtr window, Point mouseloc)
{
	Point 		loc;
	GrafPtr		origPort;
	Rect	    dragBounds, windowBounds;

	GetRegionBounds(GetGrayRgn(), &dragBounds);

	DragWindow(window, mouseloc, &dragBounds);
		
	GetPort (&origPort);			// save Port
	SETPORT(pwWindow);

	GetWindowPortBounds(window, &windowBounds);
	loc.h = windowBounds.left;
	loc.v = windowBounds.top;

	LocalToGlobal (&loc);
	origin[0] = loc.h;				// save new origin
	origin[1] = loc.v;
	
	MoveWindow(GRAFPTR pwWindow, loc.h, loc.v, false);
	// update OpenGL Context:
	aglUpdateContext(currContext);

	//SETPORT(pwWindow);	
	SetPort (origPort);				// restore Port
}

static void handleGoAwayBox(WindowPtr window, Point mouseloc)
{
	GrafPtr		origPort;
		
	GetPort(&origPort);
	SETPORT(window);
	
	if( TrackGoAway(window, mouseloc) )
	{ 
		(*exitCB)(); 	// quit...
	}
	
	SetPort(origPort);
}

static int last_m_h = 0;
static int last_m_v = 0;

static void handleMouseMoveEvt(EventRecord* eventPtr)
{
	GrafPtr		origPort;
	
	if ( ! mpCB ) return;
	
	int m_h = eventPtr->where.h;
	int m_v = eventPtr->where.v;
	if ( (m_v != last_m_v) || (m_h != last_m_h) )
	{
		last_m_v = m_v ;    last_m_h = m_h ;		// screen coord.
		GetPort(&origPort);
		SETPORT(pwWindow);
		GlobalToLocal(&eventPtr->where);
		(*mpCB) ( eventPtr->where.h, eventPtr->where.v ) ;
		SetPort(origPort);
	}
	return;
}

static void HandleEvents()
{
  EventRecord	event;
  GrafPtr		origPort;
//  bool			testNextEvent = true;

#if TARGET_API_MAC_CARBON
  if (pwQuitFlag) (*exitCB)();		// quit!
#endif

//  while ( testNextEvent )
//  {
	WaitNextEvent(everyEvent, &event, sleepTime, nil) ;
#ifdef UL_MACINTOSH
	if (full_screen)
	{
		Boolean dspEventProcessed;
		DSpProcessEvent(&event, &dspEventProcessed);
		if (dspEventProcessed) return;
	}
#endif
    int updown = PW_UP ;

    modifiers = 0;
    if ( event.modifiers & (shiftKey | rightShiftKey) )
    	modifiers |= PW_SHIFT ;
    if ( event.modifiers & (optionKey | rightOptionKey) )
    	modifiers |= PW_ALT ;
    if ( event.modifiers & (controlKey | rightControlKey) )
    	modifiers |= PW_CTRL ;

    switch ( event.what )
    {
    	case keyDown:
    		// test for a 'command' + key (menu):
    		if(event.modifiers & cmdKey)
    		{
    			char achar	= (char) (event.message & charCodeMask);
    			handleMenuEvent( MenuKey(achar) ) ;
    			return;
    		}
    		// FALLTHROUGH (not a command)
			handleKeyEvent(&event, PW_DOWN);
			return;
    	case autoKey:
			if ( ! auto_repeat_key )
			{   // report a mouse move event, if any:
				handleMouseMoveEvt(&event);
				return;
			}
			// else report a key down event:
    		updown = PW_DOWN ;
    	case keyUp:
			handleKeyEvent(&event, updown);
			return;

    	case mouseDown:	
		{
			short	    part;
			WindowPtr	window;
		
			part = FindWindow(event.where, &window);
			switch(part)
			{
				case inMenuBar:
					handleMenuEvent( MenuSelect(event.where) );
					return;
		#if !TARGET_API_MAC_CARBON
				case inSysWindow:
					SystemClick(&event, window);
					break;
		#endif
				case inDrag:
					handleDrag(window, event.where);
					return;
				case inGoAway:
					handleGoAwayBox(window, event.where);
					return;
				case inContent:
					if(!window) return;		
					if ( GRAFPTR pwWindow != FrontWindow() )
					{
						SelectWindow (GRAFPTR pwWindow);
						return;
					}
			}
			// FALLTHROUGH (not a system click)
			updown = PW_DOWN ;
			click_modifiers = modifiers;
		}
    	case mouseUp:
    		int button;
    		switch( click_modifiers )
    		{
            	case PW_ALT :  button = PW_RIGHT_BUTTON  ; break ;
            	case PW_CTRL : button = PW_MIDDLE_BUTTON ; break ;
    			default :      button = PW_LEFT_BUTTON   ; break ;
    		}
    		GetPort(&origPort);
    		SETPORT(pwWindow);
    		GlobalToLocal(&event.where);
    		modifiers = 0;		// reset modifiers with mouse events
    		if (msCB) (*msCB)( button, updown, event.where.h, event.where.v );
    		SetPort(origPort);
    		return;

		case osEvt:
		{
			unsigned char subcode = event.message >> 24;
			if (subcode == suspendResumeMessage)   	//	Suspend/resume event	
				if (resumeFlag & event.message)		//	Resume
				{
				#ifdef UL_MACINTOSH
					if (full_screen) sleepTime = 0;
					else
				#endif
					sleepTime = ACTIVE_SLEEPTIME;
					return;
				}
				else	// suspend
					sleepTime = INACTIVE_SLEEPTIME;
			break;
		}
		case activateEvt:
			if (event.modifiers & activeFlag)
			{
				#ifdef UL_MACINTOSH
					if (full_screen) sleepTime = 0;
					else
				#endif
					sleepTime = ACTIVE_SLEEPTIME;
					return;
			}
			else sleepTime = INACTIVE_SLEEPTIME;
			break;
		
		case updateEvt:
		case nullEvent:	// likely an idle event (i.e. no events)
					// report mouse location, only if mouse has moved:
					// (there is no mouse move events on Mac!)
			handleMouseMoveEvt(&event);
			return;
		
		default : /* printf ("event: %i \n", event.what); */ break;

    }		// switch(event.what)
//  }		// while ( testNextEvent )
}


#ifdef UL_MACINTOSH
bool InitDSP()
{
	DSpContextAttributes	dspAttributes;
	DisplayIDType			displayID;
	
	if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpStartup)
	{
		ulSetError(UL_WARNING, "DrawSprocket extension not found");
		return false;
	}
	// start DrawSprocket:
	if (noErr != DSpStartup ())
	{
		ulSetError(UL_WARNING, "Unable to start DrawSprocket");
		return false;
	}
	
	// initialise to zero all fields of DSpContextAttributes:
	BlockZero( &dspAttributes, sizeof (DSpContextAttributes) );
	dspAttributes.displayWidth			= horScrSze;
	dspAttributes.displayHeight			= verScrSze;
	dspAttributes.colorNeeds			= kDSpColorNeeds_Require;
	dspAttributes.displayDepthMask		= kDSpDepthMask_16;
	dspAttributes.displayBestDepth		= 16;
	dspAttributes.backBufferDepthMask	= kDSpDepthMask_All;	// must be specified even if only fornt buffer is needed
	dspAttributes.backBufferBestDepth	= 16;
	dspAttributes.pageCount				= 1;
	//dspAttributes.contextOptions 		= 0 | kDSpContextOption_DontSyncVBL; // no page flipping and no VBL sync needed
	// look for a DSp context:
	dspContext = NULL;
	OSStatus err = DSpFindBestContext(&dspAttributes, &dspContext);
	ulSetError(UL_DEBUG, " display width = %li, height = %li,  depth = %li, mask = %lX ",
			    dspAttributes.displayWidth,
			    dspAttributes.displayHeight,
				dspAttributes.displayBestDepth,
				dspAttributes.displayDepthMask ); 
	if (err != noErr)
	{
		if (err == kDSpContextNotFoundErr)
			ulSetError(UL_WARNING, "Unable to find a DrawSprocket context");
		else
			ulSetError(UL_WARNING, "DSpFindBestContext error");
		return false;
	}
	err = DSpContext_Reserve ( dspContext, &dspAttributes );
	if (noErr != err)
	{
		ulSetError(UL_WARNING, "Unable to set the display!");
		return false;
	}
	// activate DSp context:
	//DSpContext_FadeGammaOut (NULL, NULL);		// remove for debug
	DSpContext_SetState (dspContext, kDSpContextState_Active);
	if ( !MakeWindow( 0, 0, horScrSze, verScrSze, "" ) )  return false;
	//DSpContext_FadeGammaIn (NULL, NULL);
	return true;
}
#endif

void pwInit ( int multisample, int num_samples )
{
	pwInit ( 0,0,-1,-1, multisample, "", 0, num_samples ) ;
}


void pwInit ( int x, int y, int w, int h, int multisample,
              char *title, int border, int num_samples )
{
  if (pwInitialized)
  {
	ulSetError(UL_WARNING, "pwInit already called");
	return;
  }
  
  full_screen = ( (w<0) || (h<0) ) ? true : false ;
  Initialize();
  pwInitialized = true;
  pwWindow = NULL;
  
  // Initialize OpenGL stuff (format, context, ...):
  CreateContext(multisample, num_samples, full_screen);
  
  if (full_screen)
  {
	sleepTime = 0;
#ifdef UL_MACINTOSH
	// initialisation of fullscreen mode with DrawSprocket
	dspContext = NULL;
	if ( ! InitDSP() )
	{
		pwCleanup();	// releases GL contexts, windows, ...
		exit(4);
	}
	// attach OpenGl to the screen:
	aglSetDrawable(currContext, GetWindowPort (GRAFPTR pwWindow));
	/*
	ulSetError(UL_DEBUG, "aglSetDrawable error: %s",
					(const char*) aglErrorString(aglGetError()) );
	*/
	// activate our GL context
	aglSetCurrentContext(currContext);
#else
	aglSetCurrentContext(currContext);
	aglSetFullScreen (currContext, 0, 0, 0, 0);
#endif
	// initialise origin and size of the window (for pwGetSize)
	origin[0] = 0;
	origin[1] = 0;
	size[0] = horScrSze;
	size[1] = verScrSze;
  }
  else	// full_screen
  {
	  if ( ! MakeWindow(x,y,w,h, title) )
	  {
		  pwCleanup();	// releases GL contexts, windows, ...
		  exit(4);
	  }
	  // Attach the context to the window
  	aglSetDrawable(currContext, GetWindowPort (GRAFPTR pwWindow));
#ifdef UL_MACINTOSH
  	{	// because aglSetDrawable is slow... (not needed with OSX)
		EventRecord event;
		WaitNextEvent (everyEvent, &event, 2, NULL);
  	}
#endif
	aglSetCurrentContext(currContext);

	// create a default menu bar (with the "quit" command):
	MakeMenu();
	// initialise origin and size of the window (for pwGetSize)
	origin[0] = x;
	origin[1] = y;
	size[0] = w;
	size[1] = h;
  }
}


void pwGetSize ( int *w, int *h )
{
	*w = size[0];
	*h = size[1];
}

//special cursors:
static Cursor q_curs =		// question mark cursor
{
	{0x0380, 0x07c0, 0x0c60, 0x1830, 0x1830, 0x0030, 0x0060, 0x00c0,
	 0x0180, 0x0180, 0x0180, 0, 0, 0x0180, 0x0180, 0},	// data
	{0x0380, 0x07e0, 0x0ff0, 0x1e38, 0x1c38, 0x1c38, 0x0078, 0x00f0,
	 0x01e0, 0x01c0, 0x01c0, 0x01c0, 0, 0x01c0, 0x01c0, 0x01c0},	// mask
	{ 14, 7 } 	// hotspot
};
static Cursor c_curs =		// circle cursor
{
	{0, 0x03e0, 0x0c18, 0x1004, 0x2002, 0x2002, 0x4001, 0x4001,
	 0x4001, 0x4001, 0x4001, 0x2002, 0x2002, 0x1004, 0x0c18, 0x03e0},	// data
	{0, 0x03e0, 0x0ff8, 0x1c1c, 0x3006, 0x3006, 0x6003, 0x6003,
	 0x6003, 0x6003, 0x6003, 0x3006, 0x3006, 0x1c1c, 0x0ff8, 0x03e0},	// mask
	{ 8, 8 } 	// hotspot
};
static Cursor r_curs =		// right arrow cursor
{
	{0, 0x0008, 0x0018, 0x0038, 0x0078, 0x00f8, 0x01f8, 0x03f8,
	 0x07f8, 0x00f8, 0x00d8, 0x0188, 0x0180, 0x0300, 0x0300, 0},	 // data
	{0x000c, 0x001c, 0x003c, 0x007c, 0x00fc, 0x01fc, 0x03fc, 0x07fc,
	 0x0ffc, 0x0ffc, 0x01fc, 0x03dc, 0x03cc, 0x0780, 0x0780, 0x0700}, // mask
	{ 1, 12 } 	// hotspot
};
static Cursor x_curs =		// circle + cross cursor
{
	{0, 0x03e0, 0x0c98, 0x1084, 0x2082, 0x2082, 0x4081, 0x4081,
	 0x7fff, 0x4081, 0x4081, 0x2082, 0x2082, 0x1084, 0x0c98, 0x03e0},	// data
	{0, 0, 0, 0, 0, 0, 0, 0,
	 0, 0, 0, 0, 0, 0, 0, 0},	// mask
	{ 8, 8 } 	// hotspot
};


void pwSetCursor ( int c )
{
	UInt32 csr = 0xFFFF;
	Cursor * csrPtr;
	switch (c)
	{
		case PW_CURSOR_NONE   : HideCursor(); return;
		case PW_CURSOR_LEFT   : csr = kThemeArrowCursor; break;
		case PW_CURSOR_RIGHT  : csrPtr = &r_curs; break;
		case PW_CURSOR_AIM    : csrPtr = &x_curs; break;
		//case PW_CURSOR_CARET  : csr = kThemeIBeamCursor; break; // ???
		case PW_CURSOR_WAIT   : csr = kThemeWatchCursor; break;
		case PW_CURSOR_CROSS  : csr = kThemeCrossCursor; break;
		case PW_CURSOR_QUERY  : csrPtr = &q_curs; break;
		case PW_CURSOR_CIRCLE : csrPtr = &c_curs; break;
		case PW_CURSOR_CUSTOM :
		default : csr = kThemeArrowCursor;
				 ulSetError(UL_WARNING, "Unknown or unavailable cursor type\n");		
	}
	if (csr==0xFFFF)  SetCursor(csrPtr);
	else  SetThemeCursor(csr);
	ShowCursor();
}


void pwSetSize ( int w, int h )
{
	if (!pwInitialized)
	{
		ulSetError(UL_WARNING, "call to pwSetSize without call to pwInit");
		return;
	}
	if ( full_screen ) 
	{
		ulSetError(UL_WARNING, "call to pwSetSize in full screen mode");
		return;
	}
	if ( ( w == size[0] ) && ( h == size[1] ) ) return;
	
	GrafPtr		origPort;
	
	if (w<50) w = 50;
	if (w>horScrSze) w = horScrSze;
	if (h<50) h = 50;
	if (h>verScrSze) h = verScrSze;
	
	GetPort(&origPort);
	
	size[0] = w;		// save size
	size[1] = h;
	
	SizeWindow(GRAFPTR pwWindow, w, h, false);
	// update OpenGL Context:
	aglUpdateContext(currContext);
	
	if ( resizeCB != NULL )  (*resizeCB) ( w, h ) ;
	
	SetPort(origPort);
}


void pwSetOrigin ( int x, int y )
{
	if (!pwInitialized)
	{
		ulSetError(UL_WARNING, "call to pwSetOrigin without call to pwInit");
		return;
	}
	if ( full_screen ) 
	{
		ulSetError(UL_WARNING, "call to pwSetOrigin in full screen mode");
		return;
	}
	if ( ( x == origin[0] ) && ( y == origin[1] ) ) return;
	
	if ( (x + size[0]) < 10)   x = 10 - size[0];
	if ( x > (horScrSze-10) )  x = horScrSze-10;
	if ( (y + size[1]) < 60)   y = 60 - size[1];
	if ( y > (verScrSze-10) )  y = verScrSze-10;
	
	MoveWindow(GRAFPTR pwWindow, x, y, false);
	// update OpenGL Context:
	aglUpdateContext(currContext);
	
	origin[0] = x;				// save new origin
	origin[1] = y;
}


void pwSetSizeOrigin ( int x, int y, int w, int h )
{
	if (!pwInitialized)
	{
		ulSetError(UL_WARNING, "call to pwSetSizeOrigin without call to pwInit");
		return;
	}
	if ( full_screen ) 
	{
		ulSetError(UL_WARNING, "call to pwSetSizeOrigin in full screen mode");
		return;
	}
	
	pwSetOrigin( x, y );
	pwSetSize( w, h );
}


void pwSwapBuffers ()
{
	if (!pwInitialized)
	{
		ulSetError(UL_WARNING, "call to pwSwapBuffers without call to pwInit");
		return;
	}
	// glFlush () ;
	aglSwapBuffers ( currContext ) ;
	HandleEvents () ;
}


void pwCleanup ()
{
	if (!pwInitialized)
	{
		ulSetError(UL_WARNING, "call to pwCleanup without call to pwInit");
		return;
	}
	
	if (currContext != NULL)
	{
		aglSetDrawable (currContext, NULL);
		aglSetCurrentContext (NULL);
		aglDestroyContext ( currContext );
	}
	if (pwWindow != NULL)   DisposeWindow(GRAFPTR pwWindow);
	
#ifdef UL_MACINTOSH
	if (full_screen)
	{
		if (dspContext != NULL)
		{
			//DSpContext_FadeGammaOut (NULL, NULL);	// remove for debug
			DSpContext_SetState (dspContext, kDSpContextState_Inactive);
			//DSpContext_FadeGammaIn (NULL, NULL);	

			DSpContext_Release (dspContext);
		}
			
		DSpShutdown ();
	}
#endif

	pwInitialized = false;
}

#endif
