added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

View File

@ -0,0 +1,162 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/platformSemaphore.h"
#include "platform/platformVideo.h"
#include "platform/platformThread.h"
#include "console/console.h"
#include "platformMacCarb/macCarbEvents.h"
// Carbon alerts must be displayed from the main thead, because only the main
// thread recieves user events.
//-----------------------------------------------------------------------------
// Puts the main thread in a modal state & shows the alert.
// This should only be called from the main thread.
//-----------------------------------------------------------------------------
void MacCarbRunAlertMain()
{
// show the alert - this will return when the user hits a button.
DialogItemIndex hit;
OSStatus err = RunStandardAlert(platState.alertDlg,NULL,&hit);
if(err!=noErr)
Con::printf("RunStandardAlert error = %d, 0x%x", err, err);
platState.alertHit = hit;
Semaphore::releaseSemaphore(platState.alertSemaphore);
}
//-----------------------------------------------------------------------------
// Ensures that our alert windows will be shown above the main window.
//-----------------------------------------------------------------------------
static void _MacCarbSetAlertWindowLevel()
{
SetWindowGroupLevel( GetWindowGroupOfClass(kAlertWindowClass), kTAlertWindowLevel);
}
//-----------------------------------------------------------------------------
static void _MacCarbSendAlertToMainThread(DialogRef alert)
{
// create & post an event - this will tell the RAEL thread to display an alert.
platState.alertDlg = alert;
if(Thread::getCurrentThreadId() == platState.firstThreadId)
MacCarbRunAlertMain();
else
{
MacCarbSendTorqueEventToMain( kEventTorqueAlert);
}
// wait for the alert semaphore
Semaphore::acquireSemaphore(platState.alertSemaphore);
platState.alertDlg = NULL;
}
//-----------------------------------------------------------------------------
// Convenience function to encapsulate creating a DialogRef for an alert
//-----------------------------------------------------------------------------
static DialogRef _MacCarbCreateAlert( const char* error, const char* message, CFStringRef okText, CFStringRef cancelText )
{
if(platState.headless)
return NULL;
DialogRef alert;
AlertStdCFStringAlertParamRec params;
CFStringRef cfError, cfMessage;
GetStandardAlertDefaultParams(&params, kStdCFStringAlertVersionOne);
if(okText)
params.defaultText = okText;
if(cancelText)
{
params.cancelText = cancelText;
params.cancelButton = kAlertStdAlertCancelButton;
}
cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingUTF8);
cfMessage = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
OSStatus err=CreateStandardAlert(kAlertStopAlert, cfError, cfMessage, &params, &alert);
if(err!=noErr)
Con::printf("CreateStandardAlert error = %d, 0x%x", err, err);
return alert;
}
//-----------------------------------------------------------------------------
// Convenience function for putting the app in a modal state & showing an alert
//-----------------------------------------------------------------------------
static S32 _MacCarbRunAlert( DialogRef alert )
{
if(platState.headless)
return kAlertStdAlertCancelButton;
// prep to show the event - be sure we dont obscure it or eat it's events.
MacCarbRemoveCarbonEventHandlers();
_MacCarbSetAlertWindowLevel();
MacCarbSetHideCursor(false);
if(Video::isFullScreen() && platState.captureDisplay)
{
ShowMenuBar();
aglSetDrawable(platState.ctx, NULL);
}
_MacCarbSendAlertToMainThread(alert);
// go back to normal Torque operations...
if(Video::isFullScreen() && platState.captureDisplay)
{
HideMenuBar();
aglSetFullScreen(platState.ctx, Video::getResolution().w, Video::getResolution().h, 0, 0);
}
MacCarbInstallCarbonEventHandlers();
MacCarbCheckHideCursor();
return platState.alertHit;
}
//-----------------------------------------------------------------------------
void Platform::AlertOK(const char *windowTitle, const char *message)
{
DialogRef alert = _MacCarbCreateAlert(windowTitle, message,NULL,NULL);
_MacCarbRunAlert(alert);
}
//-----------------------------------------------------------------------------
bool Platform::AlertOKCancel(const char *windowTitle, const char *message)
{
DialogRef alert = _MacCarbCreateAlert(windowTitle, message,NULL,CFSTR("Cancel"));
S32 hit = _MacCarbRunAlert(alert);
return ( hit == kAlertStdAlertOKButton );
}
//-----------------------------------------------------------------------------
bool Platform::AlertRetry(const char *windowTitle, const char *message)
{
DialogRef alert = _MacCarbCreateAlert(windowTitle, message,CFSTR("Retry"),CFSTR("Cancel"));
S32 hit = _MacCarbRunAlert(alert);
return ( hit == kAlertStdAlertOKButton );
}
// paxorr - remove this when no longer needed.
//-----------------------------------------------------------------------------
ConsoleFunction( testAlert, void, 1,3,"[title],[mesage]")
{
const char *title = "The penguin is missing!";
const char *message = "Perhaps the salmon of doubt ate him.";
if(argc>1)
title = argv[1];
if(argc>2)
message = argv[2];
Platform::AlertOK(title,message);
Con::errorf("Returned from AlertOK");
bool ok = Platform::AlertOKCancel(title,message);
Con::errorf("Returned from AlertCancel, hit %s", ok?"OK":"Cancel");
ok = Platform::AlertRetry(title,message);
Con::errorf("Returned from AlertRetry, hit %s", ok?"Retry":"Cancel");
DialogRef alert = _MacCarbCreateAlert("Test","testing alternate cancel texts",NULL,CFSTR("cry havok!"));
_MacCarbRunAlert(alert);
}

View File

@ -0,0 +1,15 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// Puts the current thread in a modal state & shows the alert.
/// This should only be called from the main thread.
/// If you call it from any other thread, bad things will happen. So don't.
/// The 'main thread' here means the program's thread zero. The first thread.
/// In multithreaded Torque, this is NOT the game loop thread.
/// On the Mac, events and alerts have to happen in thread zero.
//-----------------------------------------------------------------------------
void MacCarbRunAlertMain();

View File

@ -0,0 +1,24 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/platformAL.h"
namespace Audio
{
/*! The MacOS X build links against the OpenAL framework.
It can be built to use either an internal framework, or the system framework.
Since OpenAL is weak-linked in at compile time, we don't need to init anything.
Stub it out...
*/
bool OpenALDLLInit() { return true; }
/*! Stubbed out, see the note on OpenALDLLInit().
*/
void OpenALDLLShutdown() { }
} // namespace Audio

View File

@ -0,0 +1,297 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "console/console.h"
#include "core/stringTable.h"
#include <sys/sysctl.h>
#include <mach/machine.h>
#include <math.h>
// The Gestalt() API will no longer be updated, so this code should migrate to
// the unix sysctl() and sysctlbyname() API. This is the current doctrine from Apple, dec 2005.
// Thanks to Gary "ChunkyKs" Briggs for contributing the sysctl() code patch.
Platform::SystemInfo_struct Platform::SystemInfo;
static char* _MacCarbGetMachineName();
static void _MacCarbGetCpuTypeAndFeatures(U32 *type, char **typeString, U32 *features);
static void _MacCarbGetBasicHWInfo(U32 *osVersion, U64 *cpuMhz, U32 *numCpus, U64 *memMbSize);
void Processor::init()
{
U32 osVersion;
U64 cpuMhz;
U32 numCpus;
U64 memMbSize;
U32 cpuType;
char* cpuTypeString;
U32 cpuFeatures;
Con::printf("System & Processor Information:");
// Get the mac model identifier, just for fun...
char *machineName = _MacCarbGetMachineName();
// get data about the machine out of sysctl and Gestalt ...
_MacCarbGetBasicHWInfo(&osVersion, &cpuMhz, &numCpus, &memMbSize);
_MacCarbGetCpuTypeAndFeatures(&cpuType, &cpuTypeString, &cpuFeatures);
// Set Torque's processor mhz var. This is reported to master servers.
Platform::SystemInfo.processor.mhz = cpuMhz;
// Set Torque's processor type var.
// Don't use special code paths based on this var, instead you should use the
// processor properties bits: Platform::SystemInfo.processor.properties
Platform::SystemInfo.processor.type = cpuType;
// Set Torque's processor name var.
Platform::SystemInfo.processor.name = StringTable->insert(cpuTypeString);
// Set Torques processor properties.
// These should determine what special code paths we use.
Platform::SystemInfo.processor.properties = cpuFeatures;
// Make pretty strings...
char freqString[32];
if(cpuMhz >= 1000)
dSprintf(freqString, 32, "%2.3g GHz", cpuMhz / 1000.0f);
else
dSprintf(freqString, 32, "%i MHz", cpuMhz);
char coresString[32] = "single core";
if(numCpus > 1)
dSprintf(coresString, 32, "%i cores", numCpus);
char memString[32];
if(memMbSize >= 1024)
dSprintf(memString, 32, "%2.1f GB", memMbSize / 1024.0f);
else
dSprintf(memString, 32, "%i MB", memMbSize);
// Dump info to console
Con::printf(" Mac OS X %x.%x.%x", (osVersion>>8), (osVersion&0xFF)>>4, (osVersion&0x0F));
Con::printf(" %s RAM", memString);
Con::printf(" %s %s (%s)", freqString, machineName, coresString);
Con::printf(" %s Architecture", cpuTypeString);
if(Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
Con::printf(" MMX detected");
if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE)
Con::printf(" SSE detected");
if(Platform::SystemInfo.processor.properties & CPU_PROP_ALTIVEC)
Con::printf(" Altivec detected");
Con::printf(" ");
}
static char* _MacCarbGetMachineName()
{
// data from: http://www.cocoadev.com/index.pl?MacintoshModels
// and http://www.theapplemuseum.com/index.php?id=36#
// and 10.4.8's /System/Library/SystemProfiler/SPPlatformReporter.spreporter/Contents/Resources/SPMachineTypes.plist
char* _unknownMac = "unknown Mac model";
char* _macNames[][2] = {
{ "AAPL,Gossamer", "PowerMacG3Series" },
{ "AAPL,PowerBook1998", "PowerBookG3Series" },
{ "AAPL,PowerMac G3", "PowerMacG3" },
{ "ADP2,1" , "Developer Transition Kit" },
{ "iMac,1" , "iMac" },
{ "iMac4,1" , "iMac Core Duo" },
{ "iMac4,2" , "iMac (Core Duo, mid 2006)" },
{ "iMac5,1" , "iMac (Core 2 Duo)" },
{ "iMac6,1" , "iMac (24-inch Core 2 Duo)" },
{ "MacBook1,1" , "MacBook (Core Duo)" },
{ "MacBook2,1" , "MacBook (Core 2 Duo)" },
{ "MacBookPro1,1" , "MacBook Pro (15-inch Core Duo)" },
{ "MacBookPro1,2" , "MacBook Pro (17-inch Core Duo)" },
{ "MacBookPro2,1" , "MacBook Pro (17-inch Core 2 Duo)" },
{ "MacBookPro2,2" , "MacBook Pro (15-inch Core 2 Duo)" },
{ "Macmini1,1" , "Mac mini (Core Duo/Solo)" },
{ "MacPro1,1" , "Mac Pro (Quad Xeon)" },
{ "MacPro1,1,Quad" , "Mac Pro (Quad Xeon)" },
{ "PowerBook1,1" , "PowerBook G3" },
{ "PowerBook2,1" , "iBook" },
{ "PowerBook2,2" , "iBook (FireWire)" },
{ "PowerBook3,1" , "PowerBook G3 (FireWire)" },
{ "PowerBook3,2" , "PowerBook G4" },
{ "PowerBook3,3" , "PowerBook G4 (Gigabit Ethernet)" },
{ "PowerBook3,4" , "PowerBook G4 (DVI)" },
{ "PowerBook3,5" , "PowerBook G4 (867, 1 GHz)" },
{ "PowerBook4,1" , "iBook G3" },
{ "PowerBook4,2" , "iBook G3" },
{ "PowerBook4,3" , "iBook G3" },
{ "PowerBook4,4" , "iBook" },
{ "PowerBook5,1" , "PowerBook G4 (17-inch)" },
{ "PowerBook5,2" , "PowerBook G4 (15-inch FW800)" },
{ "PowerBook5,3" , "PowerBook G4 (17-inch 1.33 GHz)" },
{ "PowerBook5,4" , "PowerBook G4 (15-inch 1.5/1.33 GHz)" },
{ "PowerBook5,5" , "PowerBook G4 (17-inch 1.5 GHz)" },
{ "PowerBook5,6" , "PowerBook G4 (15-inch 1.67/1.5 GHz)" },
{ "PowerBook5,7" , "PowerBook G4 (17-inch 1.67 GHz)" },
{ "PowerBook5,8" , "PowerBook G4 (Double-Layer SD, 15-inch)" },
{ "PowerBook5,9" , "PowerBook G4 (Double-Layer SD, 17-inch)" },
{ "PowerBook6,1" , "PowerBook G4 (12-inch)" },
{ "PowerBook6,2" , "PowerBook G4 (12-inch DVI)" },
{ "PowerBook6,3" , "iBook G4" },
{ "PowerBook6,4" , "PowerBook G4 (12-inch 1.33 GHz)" },
{ "PowerBook6,5" , "iBook G4" },
{ "PowerBook6,7" , "iBook G4" },
{ "PowerBook6,8" , "PowerBook G4 (12-inch 1.5 GHz)" },
{ "PowerMac1,1" , "Power Macintosh G3 (B&W)" },
{ "PowerMac1,2" , "Power Macintosh G4 (PCI-Graphics)" },
{ "PowerMac2,1" , "iMac (Slot-Loading)" },
{ "PowerMac2,2" , "iMac (2000)" },
{ "PowerMac3,1" , "Power Macintosh G4 (AGP-Graphics)" },
{ "PowerMac3,2" , "Power Macintosh G4 (AGP-Graphics)" },
{ "PowerMac3,3" , "Power Macintosh G4 (Gigabit Ethernet)" },
{ "PowerMac3,4" , "Power Macintosh G4 (Digital Audio)" },
{ "PowerMac3,5" , "Power Macintosh G4 (Quick Silver)" },
{ "PowerMac3,6" , "Power Macintosh G4 (Mirrored Drive Doors)" },
{ "PowerMac4,1" , "iMac (2001)" },
{ "PowerMac4,2" , "iMac (Flat Panel)" },
{ "PowerMac4,4" , "eMac G3" },
{ "PowerMac4,5" , "iMac (17-inch Flat Panel)" },
{ "PowerMac5,1" , "Power Macintosh G4 Cube" },
{ "PowerMac5,2" , "Power Macintosh G4 Cube" },
{ "PowerMac6,1" , "iMac (USB 2.0)" },
{ "PowerMac6,3" , "iMac (20-inch Flat Panel)" },
{ "PowerMac6,4" , "eMac (USB 2.0)" },
{ "PowerMac7,2" , "Power Macintosh G5" },
{ "PowerMac7,3" , "Power Macintosh G5" },
{ "PowerMac8,1" , "iMac G5" },
{ "PowerMac8,2" , "iMac G5 (Ambient Light Sensor)" },
{ "PowerMac9,1" , "Power Macintosh G5 (Late 2004)" },
{ "PowerMac10,1" , "Mac mini" },
{ "PowerMac10,2" , "Mac mini (Late 2005)" },
{ "PowerMac11,2" , "Power Macintosh G5 (PCIe)" },
{ "PowerMac11,2,Quad", "Power Macintosh G5 Quad" },
{ "PowerMac12,1" , "iMac G5 (iSight)" },
{ "RackMac1,1" , "Xserve G4" },
{ "RackMac1,2" , "Xserve G4 (Slot-Loading)" },
{ "RackMac3,1" , "Xserve G5" },
{ NULL , NULL }
};
char macModel[MAXPATHLEN];
U32 macModelLen = MAXPATHLEN;
macModel[macModelLen-1] = '\0';
sysctlbyname("hw.model", &macModel, &macModelLen, NULL, 0);
for(int i=0; _macNames[i][0] != NULL; i++)
{
if(!strcmp(_macNames[i][0], macModel))
return _macNames[i][1];
}
#if defined(TORQUE_DEBUG)
Con::warnf("Unknown mac model. Add a definition to _MacCarbGetMachineName() for %s.", macModel);
#endif
return _unknownMac;
}
void _MacCarbGetBasicHWInfo(U32 *osVersion, U64 *cpuMhz, U32 *numCpus, U64 *memMbSize)
{
U32 u64size = sizeof(U64);
U32 u32size = sizeof(U32);
// Get MacOSX version #
// Gestalt is still the only simple way to get the system version number, as of Jan 2007
Gestalt(gestaltSystemVersion, osVersion);
// Get system mem
sysctlbyname("hw.memsize", memMbSize, &u64size, NULL, 0);
*memMbSize /= 1024 * 1024;
// Get cpu clock speed
sysctlbyname("hw.cpufrequency", cpuMhz, &u64size, NULL, 0);
// sometimes the frequency is not a pretty number, so round it.
*cpuMhz = mCeil((float)(*cpuMhz)/(1000*1000));
// Get the # of processors or cores we have available
sysctlbyname("hw.ncpu", numCpus, &u32size, NULL, 0);
}
void _MacCarbGetCpuTypeAndFeatures(U32 *type, char **typeString, U32 *features)
{
int err;
U32 cputype;
U32 cpusubtype;
U32 cpufeature;
U32 u64size = sizeof(U64);
U32 u32size = sizeof(U32);
char *torqueCpuString;
U32 torqueCpuType;
U32 torqueCpuFeatures;
// Identify the cpu
err = sysctlbyname("hw.cputype", &cputype, &u32size, NULL, 0);
err |= sysctlbyname("hw.cpusubtype", &cpusubtype, &u32size, NULL, 0);
AssertWarn(err == 0, "Couldn't detect CPU type. Assuming generic x86.");
// determine the processor type & name
switch(cputype) {
case CPU_TYPE_X86:
case CPU_TYPE_X86_64:
torqueCpuString = "x86";
torqueCpuType = CPU_X86Compatible;
break;
case CPU_TYPE_POWERPC64:
case CPU_TYPE_POWERPC:
switch(cpusubtype)
{
case CPU_SUBTYPE_POWERPC_970:
torqueCpuString = "G5";
torqueCpuType = CPU_PowerPC_G5;
break;
case CPU_SUBTYPE_POWERPC_7400:
case CPU_SUBTYPE_POWERPC_7450:
torqueCpuString = "G4";
torqueCpuType = CPU_PowerPC_G4;
break;
case CPU_SUBTYPE_POWERPC_750:
torqueCpuString = "G3";
torqueCpuType = CPU_PowerPC_G3;
break;
default:
torqueCpuString = "PowerPC";
torqueCpuType = CPU_PowerPC_Unknown;
}
break;
default:
torqueCpuString = "unknown";
torqueCpuType = CPU_X86Compatible;
}
// Get CPU features. These should determine what special code paths we use.
torqueCpuFeatures = 0;
switch(torqueCpuType) {
case CPU_X86Compatible:
err = sysctlbyname("hw.optional.mmx", &cpufeature, &u32size, NULL, 0);
if(!err && cpufeature)
torqueCpuFeatures |= CPU_PROP_MMX;
err = sysctlbyname("hw.optional.sse", &cpufeature, &u32size, NULL, 0);
if(!err && cpufeature)
torqueCpuFeatures |= CPU_PROP_SSE;
break;
case CPU_PowerPC_G5:
case CPU_PowerPC_G4:
err = sysctlbyname("hw.optional.altivec", &cpufeature, &u32size, NULL, 0);
if(!err && cpufeature)
torqueCpuFeatures |= CPU_PROP_ALTIVEC;
break;
}
// return data
*type = torqueCpuType;
*typeString = torqueCpuString;
*features = torqueCpuFeatures;
}

View File

@ -0,0 +1,85 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
/// To target MacOS 10.2, you need to modify some global project build settings.
/// The default is to target 10.3, as that provides the best feature set and behavior in torque.
/// MACOSX_DEPLOYMENT_TARGET_ppc = 10.2
/// SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk
/// for more info on cross development with xcode, see:
/// http://developer.apple.com/documentation/DeveloperTools/Conceptual/cross_development/UniversalBinaries/chapter_4_section_1.html
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1030
#define NO_REDEFINE_10_2_COMPAT
#include "platformMacCarb/platformMacCarb.h"
#include "platformMacCarb/macCarbEvents.h"
#include "platform/platformThread.h"
S32 (* TransitionWindowWithOptions_ptr) (void*, U32, U32, const void*, bool, void*) = NULL;
bool (* CGCursorIsVisible_ptr) () = NULL;
U32 (* CGDisplayHideCursor_ptr) (U32) = NULL;
U32 (* CGDisplayShowCursor_ptr) (U32) = NULL;
// Replaces the transitions under 10.2.
// Our implementation does not perform transitions, it just sends the events
// that torque expects.
S32 TransitionWindowWithOptions_compat( void* theWind, U32, U32 transition,
const void*, bool, TransitionWindowOptions*)
{
if(transition == kWindowHideTransitionAction)
{
HideWindow(theWind);
MacCarbSendTorqueEventToMain(kEventTorqueReleaseWindow, theWind);
}
else
{
ShowWindow(theWind);
}
}
static bool sgCursorHidden = false;
bool CGCursorIsVisible_compat()
{
return sgCursorHidden;
}
U32 CGDisplayHideCursor_compat(U32)
{
AssertFatal(Thread::getCurrentThreadId() == platState.firstThreadId,
"Cannot call Mac Carbon toolbox routines from any thread but the first thread.");
HideCursor();
sgCursorHidden = true;
return 0;
}
U32 CGDisplayShowCursor_compat(U32)
{
AssertFatal(Thread::getCurrentThreadId() == platState.firstThreadId,
"Cannot call Mac Carbon toolbox routines from any thread but the first thread.");
ShowCursor();
sgCursorHidden = false;
return 0;
}
void MacCarbInit1020CompatInit()
{
#define INIT_COMPAT_FUNC( a ) \
a##_ptr = a; \
if( a == NULL ) \
a##_ptr = a##_compat
INIT_COMPAT_FUNC(CGCursorIsVisible);
INIT_COMPAT_FUNC(CGDisplayHideCursor);
INIT_COMPAT_FUNC(CGDisplayShowCursor);
INIT_COMPAT_FUNC(TransitionWindowWithOptions);
#undef INIT_COMPAT_FUNC
}
#endif //MAC_OS_X_VERSION_MIN_REQUIRED < 1030

View File

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// macCarbCompat.10.2.h
//-----------------------------------------------------------------------------
#ifndef _MACCARBCOMPAT_10_2_H_
#define _MACCARBCOMPAT_10_2_H_
/// MacCarbInit1020CompatInit sets up function pointers for stubs to functions
/// that are unavailable in 10.2, if we're running on 10.2
void MacCarbInit1020CompatInit();
// Gestalt values introduced in 10.3+ frameworks.
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1030
#define gestaltCPUG47447 0x0112
#define gestaltCPU970 0x0139
#endif
// Some agl flags introduced in 10.3+
#define AGL_COLOR_FLOAT 58
#define AGL_MULTISAMPLE 59
#define AGL_SUPERSAMPLE 60
#define AGL_SAMPLE_ALPHA 61
// Window transitions, introduced in 10.3+
#define TransitionWindowOptions TransitionWindowOptions_compat
typedef struct TransitionWindowOptions_compat {
U32 version;
double duration;
void* parentWindowOfSheet;
void* userData;
};
//const U32 kEventWindowTransitionCompleted = 89;
//const U32 kWindowFadeTransitionEffect = 4;
//const U32 kEventParamWindowTransitionAction = 'wtac';
//const U32 typeWindowTransitionAction = 'wtac';
// replacements for functions not implemented in 10.2:
extern S32 (* TransitionWindowWithOptions_ptr) (void*, U32, U32, const void*, bool, void*);
extern bool (* CGCursorIsVisible_ptr) ();
extern U32 (* CGDisplayHideCursor_ptr) (U32);
extern U32 (* CGDisplayShowCursor_ptr) (U32);
#ifndef NO_REDEFINE_10_2_COMPAT
#define CGCursorIsVisible CGCursorIsVisible_ptr
#define CGDisplayHideCursor CGDisplayHideCursor_ptr
#define CGDisplayShowCursor CGDisplayShowCursor_ptr
#define TransitionWindowWithOptions TransitionWindowWithOptions_ptr
#endif
#endif // _MACCARBCOMPAT_10_2_H_

View File

@ -0,0 +1,159 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platformMacCarb/macCarbConsole.h"
#include "platform/event.h"
#include "platform/gameInterface.h"
#include "platform/platformThread.h"
#include <stdio.h>
// TODO: convert this to use ncurses.
static void _MacCarbConsolePrintPrompt();
MacConsole *gConsole = NULL;
ConsoleFunction(enableWinConsole, void, 2, 2, "(bool enable)")
{
argc;
if (gConsole)
gConsole->enable(dAtob(argv[1]));
}
static void macConsoleConsumer(ConsoleLogEntry::Level, const char *line)
{
if (gConsole)
gConsole->processConsoleLine(line);
}
static void macConsoleInputLoopThread(S32 *arg)
{
if(!gConsole)
return;
gConsole->inputLoop();
}
void MacConsole::create()
{
gConsole = new MacConsole();
}
void MacConsole::destroy()
{
if (gConsole)
delete gConsole;
gConsole = NULL;
}
void MacConsole::enable(bool enabled)
{
if (gConsole == NULL)
return;
bool conIsAlive = consoleThread && consoleThread->isAlive();
if(consoleThread && !conIsAlive)
delete consoleThread;
consoleEnabled = enabled;
if(consoleEnabled)
{
if(!conIsAlive)
{
printf("Initializing Console...\n");
new Thread((ThreadRunFunction)macConsoleInputLoopThread,0,true);
printf("Console Initialized.\n");
_MacCarbConsolePrintPrompt();
}
}
else
{
printf("Deactivating Console.");
}
}
bool MacConsole::isEnabled()
{
if ( !gConsole )
return false;
return gConsole->consoleEnabled;
}
MacConsole::MacConsole()
{
consoleEnabled = platState.headless;
clearInBuf();
consoleThread = NULL;
Con::addConsumer(macConsoleConsumer);
}
MacConsole::~MacConsole()
{
Con::removeConsumer(macConsoleConsumer);
}
void MacConsole::processConsoleLine(const char *consoleLine)
{
if(consoleEnabled)
{
printf("%s\n", consoleLine);
}
}
void MacConsole::clearInBuf()
{
dMemset(inBuf, 0, MaxConsoleLineSize);
inBufPos=0;
}
void MacConsole::inputLoop()
{
Con::printf("Console Input Thread Started");
unsigned char c;
while(consoleEnabled)
{
c = fgetc(stdin);
if(feof(stdin) || ferror(stdin))
{
clearerr(stdin);
Platform::sleep(100);
continue;
}
if(c == '\n')
{
// exec the line
dStrcpy(postEvent.data, inBuf);
postEvent.size = ConsoleEventHeaderSize + dStrlen(inBuf) + 1;
Con::printf("=> %s",postEvent.data);
Game->postEvent(postEvent);
// clear the buffer
clearInBuf();
_MacCarbConsolePrintPrompt();
}
else
{
// add it to the buffer.
inBuf[inBufPos++] = c;
// if we're full, clear & warn.
if(inBufPos >= MaxConsoleLineSize-1)
{
clearInBuf();
Con::warnf("Line too long, discarding this command: %s", inBuf);
}
}
}
Con::printf("Console Input Thread Stopped");
}
static void _MacCarbConsolePrintPrompt()
{
printf("%s", Con::getVariable("Con::Prompt"));
}

View File

@ -0,0 +1,43 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _MACCARBCONSOLE_H_
#define _MACCARBCONSOLE_H_
#include "console/console.h"
#include "platform/event.h"
class Thread;
class MacConsole
{
private:
bool consoleEnabled;
Thread* consoleThread;
U32 inBufPos;
char inBuf[MaxConsoleLineSize];
ConsoleEvent postEvent;
void clearInBuf();
public:
static void create();
static void destroy();
static bool isEnabled();
MacConsole();
~MacConsole();
void enable(bool);
void processConsoleLine(const char *consoleLine);
void inputLoop();
};
extern MacConsole *gConsole;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
/// Hides the mouse cursor when the mouse is locked, when we are in fullscreen
/// mode, and when the cursor is inside the window bounds.
/// Optionally takes the cursor location, in window local coords.
void MacCarbCheckHideCursor(S32 x=-1, S32 y=-1);
/// Hides or shows the system mouse cursor, regardless of the mouse position,
/// and tracks the hidden state.
void MacCarbSetHideCursor(bool shouldHide);
/// Install event handlers on the current window & on the app.
void MacCarbInstallCarbonEventHandlers(void);
/// Remove all event handlers, except the alert event handlers.
void MacCarbRemoveCarbonEventHandlers(void);
/// Install event handlers for custom Torque inter-thread communication.
/// This must be called before starting multithreaded execution in main().
void MacCarbInstallTorqueCarbonEventHandlers(void);
/// create an event of class kEventClassTorque, and send it to the main thread
void MacCarbSendTorqueEventToMain( U32 eventKind, void* userData = NULL );
/// event type for alerts. The event class is an arbitrary val, it must not collide w/ kEventApp* .
const U32 kEventClassTorque = 'TORQ';
const U32 kEventTorqueAlert = 1;
const U32 kEventTorqueFadeInWindow = 2;
const U32 kEventTorqueFadeOutWindow = 3;
const U32 kEventTorqueReleaseWindow = 4;
const U32 kEventTorqueShowMenuBar = 5;
const U32 kEventParamTorqueData = 'tDAT'; // typeVoidPtr void*
//const U32 kEventParamTorqueSemaphorePtr = 'tSEM'; // typeVoidPtr void*
//const U32 kEventParamTorqueDialogRef = 'tDRF'; // typeDialogRef DialogRef
//const U32 kEventParamTorqueHitPtr = 'tHIT'; // typeVoidPtr U32*
//const U32 typeDialogRef = 'dlog'; // oddly, not provided in Frameworks.

View File

@ -0,0 +1,899 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "core/fileio.h"
#include "core/tVector.h"
#include "core/stringTable.h"
#include "console/console.h"
#include "platform/profiler.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <utime.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
//TODO: file io still needs some work...
// this is the main working dir path.
static StringTableEntry cwd = NULL;
#define MAX_MAC_PATH_LONG 2048
//-----------------------------------------------------------------------------
#if defined(TORQUE_OS_MAC_OSX)
#include <CoreFoundation/CFBundle.h>
#else
#include <CFBundle.h>
#endif
//-----------------------------------------------------------------------------
bool dFileDelete(const char * name)
{
if(!name )
return(false);
if (dStrlen(name) > MAX_MAC_PATH_LONG)
Con::warnf("dFileDelete: Filename length is pretty long...");
return(remove(name) == 0); // remove returns 0 on success
}
//-----------------------------------------------------------------------------
bool dFileTouch(const char *path)
{
if (!path || !*path)
return false;
// set file at path's modification and access times to now.
return( utimes( path, NULL) == 0); // utimes returns 0 on success.
}
//-----------------------------------------------------------------------------
// Constructors & Destructor
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// After construction, the currentStatus will be Closed and the capabilities
// will be 0.
//-----------------------------------------------------------------------------
File::File()
: currentStatus(Closed), capability(0)
{
handle = NULL;
}
//-----------------------------------------------------------------------------
// insert a copy constructor here... (currently disabled)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
File::~File()
{
close();
handle = NULL;
}
//-----------------------------------------------------------------------------
// Open a file in the mode specified by openMode (Read, Write, or ReadWrite).
// Truncate the file if the mode is either Write or ReadWrite and truncate is
// true.
//
// Sets capability appropriate to the openMode.
// Returns the currentStatus of the file.
//-----------------------------------------------------------------------------
File::Status File::open(const char *filename, const AccessMode openMode)
{
if (dStrlen(filename) > MAX_MAC_PATH_LONG)
Con::warnf("File::open: Filename length is pretty long...");
// Close the file if it was already open...
if (currentStatus != Closed)
close();
// create the appropriate type of file...
switch (openMode)
{
case Read:
handle = (void *)fopen(filename, "rb"); // read only
break;
case Write:
handle = (void *)fopen(filename, "wb"); // write only
break;
case ReadWrite:
handle = (void *)fopen(filename, "ab+"); // write(append) and read
break;
case WriteAppend:
handle = (void *)fopen(filename, "ab"); // write(append) only
break;
default:
AssertFatal(false, "File::open: bad access mode");
}
// handle not created successfully
if (handle == NULL)
return setStatus();
// successfully created file, so set the file capabilities...
switch (openMode)
{
case Read:
capability = FileRead;
break;
case Write:
case WriteAppend:
capability = FileWrite;
break;
case ReadWrite:
capability = FileRead | FileWrite;
break;
default:
AssertFatal(false, "File::open: bad access mode");
}
// must set the file status before setting the position.
currentStatus = Ok;
if (openMode == ReadWrite)
setPosition(0);
// success!
return currentStatus;
}
//-----------------------------------------------------------------------------
// Get the current position of the file pointer.
//-----------------------------------------------------------------------------
U32 File::getPosition() const
{
AssertFatal(currentStatus != Closed , "File::getPosition: file closed");
AssertFatal(handle != NULL, "File::getPosition: invalid file handle");
return ftell((FILE*)handle);
}
//-----------------------------------------------------------------------------
// Set the position of the file pointer.
// Absolute and relative positioning is supported via the absolutePos
// parameter.
//
// If positioning absolutely, position MUST be positive - an IOError results if
// position is negative.
// Position can be negative if positioning relatively, however positioning
// before the start of the file is an IOError.
//
// Returns the currentStatus of the file.
//-----------------------------------------------------------------------------
File::Status File::setPosition(S32 position, bool absolutePos)
{
AssertFatal(Closed != currentStatus, "File::setPosition: file closed");
AssertFatal(handle != NULL, "File::setPosition: invalid file handle");
if (currentStatus != Ok && currentStatus != EOS )
return currentStatus;
U32 finalPos;
if(absolutePos)
{
// absolute position
AssertFatal(0 <= position, "File::setPosition: negative absolute position");
// position beyond EOS is OK
fseek((FILE*)handle, position, SEEK_SET);
finalPos = ftell((FILE*)handle);
}
else
{
// relative position
AssertFatal((getPosition() + position) >= 0, "File::setPosition: negative relative position");
// position beyond EOS is OK
fseek((FILE*)handle, position, SEEK_CUR);
finalPos = ftell((FILE*)handle);
}
// ftell returns -1 on error. set error status
if (0xffffffff == finalPos)
return setStatus();
// success, at end of file
else if (finalPos >= getSize())
return currentStatus = EOS;
// success!
else
return currentStatus = Ok;
}
//-----------------------------------------------------------------------------
// Get the size of the file in bytes.
// It is an error to query the file size for a Closed file, or for one with an
// error status.
//-----------------------------------------------------------------------------
U32 File::getSize() const
{
AssertWarn(Closed != currentStatus, "File::getSize: file closed");
AssertFatal(handle != NULL, "File::getSize: invalid file handle");
if (Ok == currentStatus || EOS == currentStatus)
{
struct stat statData;
if(fstat(fileno((FILE*)handle), &statData) != 0)
return 0;
// return the size in bytes
return statData.st_size;
}
return 0;
}
//-----------------------------------------------------------------------------
// Flush the file.
// It is an error to flush a read-only file.
// Returns the currentStatus of the file.
//-----------------------------------------------------------------------------
File::Status File::flush()
{
AssertFatal(Closed != currentStatus, "File::flush: file closed");
AssertFatal(handle != NULL, "File::flush: invalid file handle");
AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file");
if (fflush((FILE*)handle) != 0)
return setStatus();
else
return currentStatus = Ok;
}
//-----------------------------------------------------------------------------
// Close the File.
//
// Returns the currentStatus
//-----------------------------------------------------------------------------
File::Status File::close()
{
// check if it's already closed...
if (Closed == currentStatus)
return currentStatus;
// it's not, so close it...
if (handle != NULL)
{
if (fclose((FILE*)handle) != 0)
return setStatus();
}
handle = NULL;
return currentStatus = Closed;
}
//-----------------------------------------------------------------------------
// Self-explanatory.
//-----------------------------------------------------------------------------
File::Status File::getStatus() const
{
return currentStatus;
}
//-----------------------------------------------------------------------------
// Sets and returns the currentStatus when an error has been encountered.
//-----------------------------------------------------------------------------
File::Status File::setStatus()
{
const char* err = strerror(errno);
switch (errno)
{
case EACCES: // permission denied
currentStatus = IOError;
break;
case EBADF: // Bad File Pointer
case EINVAL: // Invalid argument
case ENOENT: // file not found
case ENAMETOOLONG:
default:
currentStatus = UnknownError;
}
return currentStatus;
}
//-----------------------------------------------------------------------------
// Sets and returns the currentStatus to status.
//-----------------------------------------------------------------------------
File::Status File::setStatus(File::Status status)
{
return currentStatus = status;
}
//-----------------------------------------------------------------------------
// Read from a file.
// The number of bytes to read is passed in size, the data is returned in src.
// The number of bytes read is available in bytesRead if a non-Null pointer is
// provided.
//-----------------------------------------------------------------------------
File::Status File::read(U32 size, char *dst, U32 *bytesRead)
{
AssertFatal(Closed != currentStatus, "File::read: file closed");
AssertFatal(handle != NULL, "File::read: invalid file handle");
AssertFatal(NULL != dst, "File::read: NULL destination pointer");
AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability");
AssertWarn(0 != size, "File::read: size of zero");
if (Ok != currentStatus || 0 == size)
return currentStatus;
// read from stream
U32 nBytes = fread(dst, 1, size, (FILE*)handle);
// did we hit the end of the stream?
if( nBytes != size)
currentStatus = EOS;
// if bytesRead is a valid pointer, send number of bytes read there.
if(bytesRead)
*bytesRead = nBytes;
// successfully read size bytes
return currentStatus;
}
//-----------------------------------------------------------------------------
// Write to a file.
// The number of bytes to write is passed in size, the data is passed in src.
// The number of bytes written is available in bytesWritten if a non-Null
// pointer is provided.
//-----------------------------------------------------------------------------
File::Status File::write(U32 size, const char *src, U32 *bytesWritten)
{
AssertFatal(Closed != currentStatus, "File::write: file closed");
AssertFatal(handle != NULL, "File::write: invalid file handle");
AssertFatal(NULL != src, "File::write: NULL source pointer");
AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability");
AssertWarn(0 != size, "File::write: size of zero");
if ((Ok != currentStatus && EOS != currentStatus) || 0 == size)
return currentStatus;
// write bytes to the stream
U32 nBytes = fwrite(src, 1, size,(FILE*)handle);
// if we couldn't write everything, we've got a problem. set error status.
if(nBytes != size)
setStatus();
// if bytesWritten is a valid pointer, put number of bytes read there.
if(bytesWritten)
*bytesWritten = nBytes;
// return current File status, whether good or ill.
return currentStatus;
}
//-----------------------------------------------------------------------------
// Self-explanatory.
//-----------------------------------------------------------------------------
bool File::hasCapability(Capability cap) const
{
return (0 != (U32(cap) & capability));
}
//-----------------------------------------------------------------------------
S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b)
{
if(a > b)
return 1;
if(a < b)
return -1;
return 0;
}
//-----------------------------------------------------------------------------
// either time param COULD be null.
//-----------------------------------------------------------------------------
bool Platform::getFileTimes(const char *path, FileTime *createTime, FileTime *modifyTime)
{
// MacOSX is NOT guaranteed to be running off a HFS volume,
// and UNIX does not keep a record of a file's creation time anywhere.
// So instead of creation time we return changed time,
// just like the Linux platform impl does.
if (!path || !*path)
return false;
struct stat statData;
if (stat(path, &statData) == -1)
return false;
if(createTime)
*createTime = statData.st_ctime;
if(modifyTime)
*modifyTime = statData.st_mtime;
return true;
}
//-----------------------------------------------------------------------------
bool Platform::createPath(const char *file)
{
Con::warnf("creating path %s",file);
// if the path exists, we're done.
struct stat statData;
if( stat(file, &statData) == 0 )
{
return true; // exists, rejoice.
}
// get the parent path.
// we're not using basename because it's not thread safe.
U32 len = dStrlen(file);
char parent[len];
bool isDirPath = false;
dStrncpy(parent,file,len);
parent[len] = '\0';
if(parent[len - 1] == '/')
{
parent[len - 1] = '\0'; // cut off the trailing slash, if there is one
isDirPath = true; // we got a trailing slash, so file is a directory.
}
// recusively create the parent path.
// only recurse if newpath has a slash that isn't a leading slash.
char *slash = dStrrchr(parent,'/');
if( slash && slash != parent)
{
// snip the path just after the last slash.
slash[1] = '\0';
// recusively create the parent path. fail if parent path creation failed.
if(!Platform::createPath(parent))
return false;
}
// create *file if it is a directory path.
if(isDirPath)
{
// try tocreate the directory
if( mkdir(file, 0777) != 0) // app may reside in global apps dir, and so must be writable to all.
return false;
}
return true;
}
//-----------------------------------------------------------------------------
bool Platform::cdFileExists(const char *filePath, const char *volumeName, S32 serialNum)
{
return true;
}
// helper func for getWorkingDirectory
bool isMainDotCsPresent(char *dir)
{
char maincsbuf[MAX_MAC_PATH_LONG];
char *maincsname = "/main.cs";
U32 len = dStrlen(dir) + dStrlen(maincsname);
AssertISV(len < MAX_MAC_PATH_LONG, "Sorry, path is too long, I can't run from this folder.");
dSprintf(maincsbuf,MAX_MAC_PATH_LONG,"%s%s", dir, maincsname);
return Platform::isFile(maincsbuf);
}
//-----------------------------------------------------------------------------
/// Finds and sets the current working directory.
/// Torque tries to automatically detect whether you have placed the game files
/// inside or outside the application's bundle. It checks for the presence of
/// the file 'main.cs'. If it finds it, Torque will assume that the other game
/// files are there too. If Torque does not see 'main.cs' inside its bundle, it
/// will assume the files are outside the bundle.
/// Since you probably don't want to copy the game files into the app every time
/// you build, you will want to leave them outside the bundle for development.
///
/// Placing all content inside the application bundle gives a much better user
/// experience when you distribute your app.
StringTableEntry Platform::getWorkingDirectory()
{
if(!cwd)
{
char cwd_buf[MAX_MAC_PATH_LONG];
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef bundleUrl = CFBundleCopyBundleURL(mainBundle);
bool inside = true;
bool outside = false;
bool done = false;
while(!done)
{
// first look for game content inside the application bundle.
// then we look outside the bundle
// then we assume it's a tool, and the "bundle" = the binary file.
CFURLRef workingUrl;
if(inside)
workingUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault,bundleUrl,CFSTR("Contents/Resources"),true);
else if(outside)
workingUrl = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, bundleUrl);
else
{
workingUrl = bundleUrl;
CFRetain(workingUrl); // so that we can release bundleUrl twice.
}
CFStringRef workingString = CFURLCopyFileSystemPath(workingUrl, kCFURLPOSIXPathStyle);
CFMutableStringRef normalizedString = CFStringCreateMutableCopy(NULL, 0, workingString);
CFStringNormalize(normalizedString,kCFStringNormalizationFormC);
CFStringGetCString(normalizedString, cwd_buf, sizeof(cwd_buf)-1, kCFStringEncodingUTF8);
// if we dont see main.cs inside the bundle, try again looking outside
// we're done if we find it, or if we find it neither inside or outside.
if( isMainDotCsPresent(cwd_buf) || ( !inside && !outside))
done = true;
if(inside)
inside = false, outside = true;
else if(outside)
outside = false;
CFRelease(workingUrl);
CFRelease(workingString);
CFRelease(normalizedString);
}
//CFRelease(mainBundle); // apple docs say to release this, but that causes a sigsegv(11)
CFRelease(bundleUrl);
chdir(cwd_buf); // set the current working directory.
if(StringTable)
cwd = StringTable->insert(cwd_buf);
}
return cwd;
}
//-----------------------------------------------------------------------------
StringTableEntry Platform::getExecutableName()
{
char path_buf[MAX_MAC_PATH_LONG];
// get a cfurl to the executable name
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef bundleUrl = CFBundleCopyBundleURL(mainBundle);
// get a cfstring of just the app name
CFStringRef workingString = CFURLCopyLastPathComponent(bundleUrl);
CFMutableStringRef normalizedString = CFStringCreateMutableCopy(NULL, 0, workingString);
CFStringNormalize(normalizedString,kCFStringNormalizationFormC);
CFStringGetCString(normalizedString, path_buf, sizeof(path_buf)-1, kCFStringEncodingUTF8);
CFRelease(bundleUrl);
CFRelease(workingString);
CFRelease(normalizedString);
return StringTable->insert(path_buf);
}
//-----------------------------------------------------------------------------
bool Platform::isFile(const char *path)
{
if (!path || !*path)
return false;
// make sure we can stat the file
struct stat statData;
if( stat(path, &statData) < 0 )
return false;
// now see if it's a regular file
if( (statData.st_mode & S_IFMT) == S_IFREG)
return true;
return false;
}
//-----------------------------------------------------------------------------
bool Platform::isDirectory(const char *path)
{
if (!path || !*path)
return false;
// make sure we can stat the file
struct stat statData;
if( stat(path, &statData) < 0 )
return false;
// now see if it's a directory
if( (statData.st_mode & S_IFMT) == S_IFDIR)
return true;
return false;
}
S32 Platform::getFileSize(const char* pFilePath)
{
S32 size;
if (!pFilePath || !*pFilePath)
return 0;
struct stat statData;
if( stat(pFilePath, &statData) < 0 )
return 0;
// and return it's size in bytes
return (S32)statData.st_size;
}
//-----------------------------------------------------------------------------
bool Platform::isSubDirectory(const char *pathParent, const char *pathSub)
{
char fullpath[MAX_MAC_PATH_LONG];
dStrcpyl(fullpath, MAX_MAC_PATH_LONG, pathParent, "/", pathSub, NULL);
return isDirectory((const char *)fullpath);
}
//-----------------------------------------------------------------------------
// utility for platform::hasSubDirectory() and platform::dumpDirectories()
// ensures that the entry is a directory, and isnt on the ignore lists.
inline bool isGoodDirectory(dirent* entry)
{
return (entry->d_type == DT_DIR // is a dir
&& dStrcmp(entry->d_name,".") != 0 // not here
&& dStrcmp(entry->d_name,"..") != 0 // not parent
&& !Platform::isExcludedDirectory(entry->d_name)); // not excluded
}
//-----------------------------------------------------------------------------
bool Platform::hasSubDirectory(const char *path)
{
DIR *dir;
dirent *entry;
dir = opendir(path);
if(!dir)
return false; // we got a bad path, so no, it has no subdirectory.
while( entry = readdir(dir))
{
if(isGoodDirectory(entry) )
{
closedir(dir);
return true; // we have a subdirectory, that isnt on the exclude list.
}
}
closedir(dir);
return false; // either this dir had no subdirectories, or they were all on the exclude list.
}
//-----------------------------------------------------------------------------
bool recurseDumpDirectories(const char *basePath, const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
{
DIR *dir;
dirent *entry;
U32 len = dStrlen(basePath) + dStrlen(path) + 2;
char pathbuf[len];
// construct the file path
dSprintf(pathbuf, len, "%s/%s", basePath, path);
pathbuf[len] = '\0';
// be sure it opens.
dir = opendir(pathbuf);
if(!dir)
return false;
// look inside the current directory
while( entry = readdir(dir))
{
// we just want directories.
if(!isGoodDirectory(entry))
continue;
// TODO: better unicode file name handling
// // Apple's file system stores unicode file names in decomposed form.
// // ATSUI will not reliably draw out just the accent character by itself,
// // so our text renderer has no chance of rendering decomposed form unicode.
// // We have to convert the entry name to precomposed normalized form.
// CFStringRef cfdname = CFStringCreateWithCString(NULL,entry->d_name,kCFStringEncodingUTF8);
// CFMutableStringRef cfentryName = CFStringCreateMutableCopy(NULL,0,cfdname);
// CFStringNormalize(cfentryName,kCFStringNormalizationFormC);
//
// U32 entryNameLen = CFStringGetLength(cfentryName) * 4 + 1;
// char entryName[entryNameLen];
// CFStringGetCString(cfentryName, entryName, entryNameLen, kCFStringEncodingUTF8);
// entryName[entryNameLen-1] = NULL; // sometimes, CFStringGetCString() doesn't null terminate.
// CFRelease(cfentryName);
// CFRelease(cfdname);
// construct the new path string, we'll need this below.
U32 newpathlen = dStrlen(path) + dStrlen(entry->d_name) + 2;
char newpath[newpathlen];
if(dStrlen(path) > 0) // prevent extra slashes in the path
dSprintf(newpath, newpathlen,"%s/%s",path,entry->d_name);
else
dStrncpy(newpath,entry->d_name, newpathlen);
newpath[newpathlen] = '\0';
// we have a directory, add it to the list.
if( noBasePath )
directoryVector.push_back(StringTable->insert(newpath));
else {
U32 fullpathlen = dStrlen(basePath) + dStrlen(newpath) + 2;
char fullpath[fullpathlen];
dSprintf(fullpath,fullpathlen,"%s/%s",basePath,newpath);
fullpath[fullpathlen] = '\0';
directoryVector.push_back(StringTable->insert(fullpath));
}
// and recurse into it, unless we've run out of depth
if( depth != 0) // passing a val of -1 as the recurse depth means go forever
recurseDumpDirectories(basePath, newpath, directoryVector, depth-1, noBasePath);
}
closedir(dir);
return true;
}
//-----------------------------------------------------------------------------
bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
{
PROFILE_START(dumpDirectories);
int len = dStrlen(path);
char newpath[len];
dStrncpy(newpath,path,len);
newpath[len] = '\0';
if(newpath[len - 1] == '/')
newpath[len - 1] = '\0'; // cut off the trailing slash, if there is one
bool ret = recurseDumpDirectories(newpath, "", directoryVector, depth, noBasePath);
PROFILE_END();
return ret;
}
//-----------------------------------------------------------------------------
static bool recurseDumpPath(const char* curPath, Vector<Platform::FileInfo>& fileVector, U32 depth)
{
DIR *dir;
dirent *entry;
// be sure it opens.
dir = opendir(curPath);
if(!dir)
return false;
// look inside the current directory
while( entry = readdir(dir))
{
// construct the full file path. we need this to get the file size and to recurse
U32 len = dStrlen(curPath) + entry->d_namlen + 2;
char pathbuf[len];
dSprintf( pathbuf, len, "%s/%s", curPath, entry->d_name);
pathbuf[len] = '\0';
// ok, deal with directories and files seperately.
if( entry->d_type == DT_DIR )
{
if( depth == 0)
continue;
// filter out dirs we dont want.
if( !isGoodDirectory(entry) )
continue;
// recurse into the dir
recurseDumpPath( pathbuf, fileVector, depth-1);
}
else
{
//add the file entry to the list
// unlike recurseDumpDirectories(), we need to return more complex info here.
U32 fileSize = Platform::getFileSize(pathbuf);
fileVector.increment();
Platform::FileInfo& rInfo = fileVector.last();
rInfo.pFullPath = StringTable->insert(curPath);
rInfo.pFileName = StringTable->insert(entry->d_name);
rInfo.fileSize = fileSize;
}
}
closedir(dir);
return true;
}
//-----------------------------------------------------------------------------
bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo>& fileVector, S32 depth)
{
PROFILE_START(dumpPath);
int len = dStrlen(path);
char newpath[len+1];
dStrncpy(newpath,path,len);
newpath[len] = '\0'; // null terminate
if(newpath[len - 1] == '/')
newpath[len - 1] = '\0'; // cut off the trailing slash, if there is one
bool ret = recurseDumpPath( newpath, fileVector, depth);
PROFILE_END();
return ret;
}
//-----------------------------------------------------------------------------
#if defined(TORQUE_DEBUG)
ConsoleFunction(testHasSubdir,void,2,2,"tests platform::hasSubDirectory") {
Con::printf("testing %s",argv[1]);
Platform::addExcludedDirectory(".svn");
if(Platform::hasSubDirectory(argv[1]))
Con::printf(" has subdir");
else
Con::printf(" does not have subdir");
}
ConsoleFunction(testDumpDirectories,void,4,4,"testDumpDirectories('path', int depth, bool noBasePath)") {
Vector<StringTableEntry> paths;
S32 depth = dAtoi(argv[2]);
bool noBasePath = dAtob(argv[3]);
Platform::addExcludedDirectory(".svn");
Platform::dumpDirectories(argv[1],paths,dAtoi(argv[2]),dAtob(argv[3]));
Con::printf("Dumping directories starting from %s with depth %i", argv[1],depth);
for(Vector<StringTableEntry>::iterator itr = paths.begin(); itr != paths.end(); itr++) {
Con::printf(*itr);
}
}
ConsoleFunction(testDumpPaths, void, 3, 3, "testDumpPaths('path', int depth)")
{
Vector<Platform::FileInfo> files;
S32 depth = dAtoi(argv[2]);
Platform::addExcludedDirectory(".svn");
Platform::dumpPath(argv[1], files, depth);
for(Vector<Platform::FileInfo>::iterator itr = files.begin(); itr != files.end(); itr++) {
Con::printf("%s/%s",itr->pFullPath, itr->pFileName);
}
}
//-----------------------------------------------------------------------------
ConsoleFunction(testFileTouch, bool , 2,2, "testFileTouch('path')")
{
return dFileTouch(argv[1]);
}
ConsoleFunction(testGetFileTimes, bool, 2,2, "testGetFileTimes('path')")
{
FileTime create, modify;
bool ok;
ok = Platform::getFileTimes(argv[1],&create, &modify);
Con::printf("%s Platform::getFileTimes %i, %i", ok ? "+OK" : "-FAIL", create, modify);
}
#endif

View File

@ -0,0 +1,252 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platformMacCarb/macCarbFont.h"
#include "dgl/gFont.h"
#include "dgl/gBitmap.h"
#include "Math/mRect.h"
#include "console/console.h"
#include "core/unicode.h"
//------------------------------------------------------------------------------
// New Unicode capable font class.
PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
{
PlatformFont *retFont = new MacCarbFont;
if(retFont->create(name, size, charset))
return retFont;
delete retFont;
return NULL;
}
//------------------------------------------------------------------------------
MacCarbFont::MacCarbFont()
{
mStyle = NULL;
mLayout = NULL;
mColorSpace = NULL;
}
MacCarbFont::~MacCarbFont()
{
// apple docs say we should dispose the layout first.
ATSUDisposeTextLayout(mLayout);
ATSUDisposeStyle(mStyle);
CGColorSpaceRelease(mColorSpace);
}
//------------------------------------------------------------------------------
bool MacCarbFont::create( const char *name, U32 size, U32 charset)
{
// create and cache the style and layout.
// based on apple sample code at http://developer.apple.com/qa/qa2001/qa1027.html
// note: charset is ignored on mac. -- we don't need it to get the right chars.
// But do we need it to translate encodings? hmm...
CFStringRef cfsName;
ATSUFontID atsuFontID;
ATSFontRef atsFontRef;
Fixed atsuSize;
ATSURGBAlphaColor black;
ATSFontMetrics fontMetrics;
U32 scaledSize;
// Look up the font. We need it in 2 differnt formats, for differnt Apple APIs.
cfsName = CFStringCreateWithCString( kCFAllocatorDefault, name, kCFStringEncodingUTF8);
if(!cfsName)
Con::errorf("Error: could not make a cfstring out of \"%s\" ",name);
atsFontRef = ATSFontFindFromName( cfsName, kATSOptionFlagsDefault);
atsuFontID = FMGetFontFromATSFontRef( atsFontRef);
// make sure we found it. ATSFontFindFromName() appears to return 0 if it cant find anything. Apple docs contain no info on error returns.
if( !atsFontRef || !atsuFontID )
{
Con::errorf("MacCarbFont::create - could not load font -%s-",name);
return false;
}
// adjust the size. win dpi = 96, mac dpi = 72. 72/96 = .75
// Interestingly enough, 0.75 is not what makes things the right size.
scaledSize = size - 2 - (int)((float)size * 0.1);
mSize = scaledSize;
// Set up the size and color. We send these to ATSUSetAttributes().
atsuSize = IntToFixed(scaledSize);
black.red = black.green = black.blue = black.alpha = 1.0;
// Three parrallel arrays for setting up font, size, and color attributes.
ATSUAttributeTag theTags[] = { kATSUFontTag, kATSUSizeTag, kATSURGBAlphaColorTag};
ByteCount theSizes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(ATSURGBAlphaColor) };
ATSUAttributeValuePtr theValues[] = { &atsuFontID, &atsuSize, &black };
// create and configure the style object.
ATSUCreateStyle(&mStyle);
ATSUSetAttributes( mStyle, 3, theTags, theSizes, theValues );
// create the layout object,
ATSUCreateTextLayout(&mLayout);
// we'll bind the layout to a bitmap context when we actually draw.
// ATSUSetTextPointerLocation() - will set the text buffer
// ATSUSetLayoutControls() - will set the cg context.
// get font metrics, save our baseline and height
ATSFontGetHorizontalMetrics(atsFontRef, kATSOptionFlagsDefault, &fontMetrics);
mBaseline = scaledSize * fontMetrics.ascent;
mHeight = scaledSize * ( fontMetrics.ascent - fontMetrics.descent + fontMetrics.leading ) + 1;
// cache our grey color space, so we dont have to re create it every time.
mColorSpace = CGColorSpaceCreateDeviceGray();
// and finally cache the font's name. We use this to cheat some antialiasing options below.
mName = StringTable->insert(name);
return true;
}
//------------------------------------------------------------------------------
bool MacCarbFont::isValidChar(const UTF8 *str) const
{
// since only low order characters are invalid, and since those characters
// are single codeunits in UTF8, we can safely cast here.
return isValidChar((UTF16)*str);
}
bool MacCarbFont::isValidChar( const UTF16 ch) const
{
// We cut out the ASCII control chars here. Only printable characters are valid.
// 0x20 == 32 == space
if( ch < 0x20 )
return false;
return true;
}
PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF8 *str) const
{
return getCharInfo(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL)));
}
PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF16 ch) const
{
// We use some static data here to avoid re allocating the same variable in a loop.
// this func is primarily called by GFont::loadCharInfo(),
Rect imageRect;
CGContextRef imageCtx;
U32 bitmapDataSize;
ATSUTextMeasurement tbefore, tafter, tascent, tdescent;
OSStatus err;
// 16 bit character buffer for the ATUSI calls.
// -- hey... could we cache this at the class level, set style and loc *once*,
// then just write to this buffer and clear the layout cache, to speed up drawing?
static UniChar chUniChar[1];
chUniChar[0] = ch;
// Declare and clear out the CharInfo that will be returned.
static PlatformFont::CharInfo c;
dMemset(&c, 0, sizeof(c));
// prep values for GFont::addBitmap()
c.bitmapIndex = 0;
c.xOffset = 0;
c.yOffset = 0;
// put the text in the layout.
// we've hardcoded a string length of 1 here, but this could work for longer strings... (hint hint)
// note: ATSUSetTextPointerLocation() also clears the previous cached layout information.
ATSUSetTextPointerLocation( mLayout, chUniChar, 0, 1, 1);
ATSUSetRunStyle( mLayout, mStyle, 0,1);
// get the typographic bounds. this tells us how characters are placed relative to other characters.
ATSUGetUnjustifiedBounds( mLayout, 0, 1, &tbefore, &tafter, &tascent, &tdescent);
c.xIncrement = FixedToInt(tafter);
// find out how big of a bitmap we'll need.
// as a bonus, we also get the origin where we should draw, encoded in the Rect.
ATSUMeasureTextImage( mLayout, 0, 1, 0, 0, &imageRect);
U32 xFudge = 2;
U32 yFudge = 1;
c.width = imageRect.right - imageRect.left + xFudge; // add 2 because small fonts don't always have enough room
c.height = imageRect.bottom - imageRect.top + yFudge;
c.xOrigin = imageRect.left; // dist x0 -> center line
c.yOrigin = -imageRect.top; // dist y0 -> base line
// kick out early if the character is undrawable
if( c.width == xFudge || c.height == yFudge)
return c;
// allocate a greyscale bitmap and clear it.
bitmapDataSize = c.width * c.height;
c.bitmapData = new U8[bitmapDataSize];
dMemset(c.bitmapData,0x00,bitmapDataSize);
// get a graphics context on the bitmap
imageCtx = CGBitmapContextCreate( c.bitmapData, c.width, c.height, 8, c.width, mColorSpace, kCGImageAlphaNone);
if(!imageCtx) {
Con::errorf("Error: failed to create a graphics context on the CharInfo bitmap! Drawing a blank block.");
c.xIncrement = c.width;
dMemset(c.bitmapData,0x0F,bitmapDataSize);
return c;
}
// Turn off antialiasing for monospaced console fonts. yes, this is cheating.
if(mSize < 12 && ( dStrstr(mName,"Monaco")!=NULL || dStrstr(mName,"Courier")!=NULL ))
CGContextSetShouldAntialias(imageCtx, false);
// Set up drawing options for the context.
// Since we're not going straight to the screen, we need to adjust accordingly
CGContextSetShouldSmoothFonts(imageCtx, false);
CGContextSetRenderingIntent(imageCtx, kCGRenderingIntentAbsoluteColorimetric);
CGContextSetInterpolationQuality( imageCtx, kCGInterpolationNone);
CGContextSetGrayFillColor( imageCtx, 1.0, 1.0);
CGContextSetTextDrawingMode( imageCtx, kCGTextFill);
// tell ATSUI to substitute fonts as needed for missing glyphs
ATSUSetTransientFontMatching(mLayout, true);
// set up three parrallel arrays for setting up attributes.
// this is how most options in ATSUI are set, by passing arrays of options.
ATSUAttributeTag theTags[] = { kATSUCGContextTag };
ByteCount theSizes[] = { sizeof(CGContextRef) };
ATSUAttributeValuePtr theValues[] = { &imageCtx };
// bind the layout to the context.
ATSUSetLayoutControls( mLayout, 1, theTags, theSizes, theValues );
// Draw the character!
int yoff = c.height < 3 ? 1 : 0; // kludge for 1 pixel high characters, such as '-' and '_'
int xoff = 1;
err = ATSUDrawText( mLayout, 0, 1, IntToFixed(-imageRect.left + xoff), IntToFixed(imageRect.bottom + yoff ) );
CGContextRelease(imageCtx);
if(err != noErr) {
Con::errorf("Error: could not draw the character! Drawing a blank box.");
dMemset(c.bitmapData,0x0F,bitmapDataSize);
}
#if TORQUE_DEBUG
// Con::printf("Font Metrics: Rect = %2i %2i %2i %2i Char= %C, 0x%x Size= %i, Baseline= %i, Height= %i",imageRect.top, imageRect.bottom, imageRect.left, imageRect.right,ch,ch, mSize,mBaseline, mHeight);
// Con::printf("Font Bounds: left= %2i right= %2i Char= %C, 0x%x Size= %i",FixedToInt(tbefore), FixedToInt(tafter), ch,ch, mSize);
#endif
return c;
}
//-----------------------------------------------------------------------------
// The following code snippet demonstrates how to get the elusive GlyphIDs,
// which are needed when you want to do various complex and arcane things
// with ATSUI and CoreGraphics.
//
// ATSUGlyphInfoArray glyphinfoArr;
// ATSUGetGlyphInfo( mLayout, kATSUFromTextBeginning, kATSUToTextEnd,sizeof(ATSUGlyphInfoArray), &glyphinfoArr);
// ATSUGlyphInfo glyphinfo = glyphinfoArr.glyphs[0];
// Con::printf(" Glyphinfo: screenX= %i, idealX=%f, deltaY=%f", glyphinfo.screenX, glyphinfo.idealX, glyphinfo.deltaY);

View File

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include <ApplicationServices/ApplicationServices.h>
#include "platform/platformFont.h"
class MacCarbFont : public PlatformFont
{
private:
// Caches style, layout and colorspace data to speed up character drawing.
// TODO: style colors
ATSUStyle mStyle;
ATSUTextLayout mLayout;
CGColorSpaceRef mColorSpace;
// Cache the baseline and height for the getter methods below.
U32 mHeight; // distance between lines
U32 mBaseline; // distance from drawing point to typographic baseline,
// think of the drawing point as the upper left corner of a text box.
// note: 'baseline' is synonymous with 'ascent' in Torque.
// Cache the size and name requested in create()
U32 mSize;
StringTableEntry mName;
public:
MacCarbFont();
virtual ~MacCarbFont();
/// Look up the requested font, cache style, layout, colorspace, and some metrics.
virtual bool create( const char* name, U32 size, U32 charset = TGE_ANSI_CHARSET);
/// Determine if the character requested is a drawable character, or if it should be ignored.
virtual bool isValidChar( const UTF16 ch) const;
virtual bool isValidChar( const UTF8 *str) const;
/// Get some vertical data on the font at large. Useful for drawing multiline text, and sizing text boxes.
virtual U32 getFontHeight() const;
virtual U32 getFontBaseLine() const;
// Draw the character to a temporary bitmap, and fill the CharInfo with various text metrics.
virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const;
virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const;
};
inline U32 MacCarbFont::getFontHeight() const
{
return mHeight;
}
inline U32 MacCarbFont::getFontBaseLine() const
{
return mBaseline;
}

Binary file not shown.

View File

@ -0,0 +1,297 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// Many OSX frameworks include OpenTransport, and OT's new operator conflicts
// with our redefinition of 'new', so we have to pre-include platformMacCarb.h,
// which contains the workaround.
#include <OpenGL/OpenGL.h>
#include "platformMacCarb/platformMacCarb.h"
#include "platformMacCarb/platformGL.h"
#include "platformMacCarb/macCarbUtil.h"
#include "console/console.h"
#include "dgl/dgl.h"
GLState gGLState;
bool gOpenGLDisablePT = false;
bool gOpenGLDisableCVA = false;
bool gOpenGLDisableTEC = false;
bool gOpenGLDisableARBMT = false;
bool gOpenGLDisableFC = true; //false;
bool gOpenGLDisableTCompress = false;
bool gOpenGLNoEnvColor = false;
float gOpenGLGammaCorrection = 0.5;
bool gOpenGLNoDrawArraysAlpha = false;
//-----------------------------------------------------------------------------
/// These stubs are legacy stuff for the d3d wrapper layer.
// The code that requires these stubs should probably be ifdef'd out of any non w32 build
//-----------------------------------------------------------------------------
GLboolean glAvailableVertexBufferEXT() { return(false); }
GLint glAllocateVertexBufferEXT(GLsizei size, GLint format, GLboolean preserve) { return(0); }
void* glLockVertexBufferEXT(GLint handle, GLsizei size) { return(NULL); }
void glUnlockVertexBufferEXT(GLint handle) {}
void glSetVertexBufferEXT(GLint handle) {}
void glOffsetVertexBufferEXT(GLint handle, GLuint offset) {}
void glFillVertexBufferEXT(GLint handle, GLint first, GLsizei count) {}
void glFreeVertexBufferEXT(GLint handle) {}
//-----------------------------------------------------------------------------
// helper function for getGLCapabilities.
// returns a new CGL context for platState.hDisplay
CGLContextObj getContextForCapsCheck()
{
// silently create an opengl context on the current display, so that
// we can get valid renderer and capability info
// some of the following code is from:
// http://developer.apple.com/technotes/tn2002/tn2080.html#TAN55
// From the CG display id, we can create a pixel format & context
// and with that context we can check opengl capabilities
CGOpenGLDisplayMask cglDisplayMask = CGDisplayIDToOpenGLDisplayMask(platState.cgDisplay);
CGLPixelFormatAttribute attribs[] = {kCGLPFADisplayMask, (CGLPixelFormatAttribute)cglDisplayMask, (CGLPixelFormatAttribute) NULL};
// create the pixel format. we will not use numPixelFormats, but the docs
// do not say we're allowed to pass NULL for the 3rd arg.
long numPixelFormats = 0;
CGLPixelFormatObj pixelFormat = NULL;
CGLChoosePixelFormat(attribs, &pixelFormat, &numPixelFormats);
if(pixelFormat)
{
// create a context. we don't need the pixel format once the context is created.
CGLContextObj ctx;
CGLCreateContext( pixelFormat, NULL, &ctx );
CGLDestroyPixelFormat( pixelFormat );
if(ctx)
return ctx;
}
// if we can't get a good context, we can't check caps... this won't be good.
Con::errorf("I could not create a cgl context on the display for gl capabilities checking!");
return NULL;
}
// Find out which extensions are available for this renderer.
void getGLCapabilities( )
{
OSStatus err = 0;
AssertFatal(platState.cgDisplay, "getGLCapabilities() was called before a monitor was chosen!");
// silently create an opengl context on the current display,
// so that we can get valid renderer and capability info.
// we save off the current context so that we can silently restore it.
// the user should not be aware of this little shuffle.
CGLContextObj curr_ctx = CGLGetCurrentContext ();
CGLContextObj temp_ctx = getContextForCapsCheck();
if(!temp_ctx)
{
Con::errorf("OpenGL may not be set up correctly!");
return;
}
CGLSetCurrentContext(temp_ctx);
// Get the OpenGL info strings we'll need
const char* pVendString = (const char*) glGetString( GL_VENDOR );
const char* pRendString = (const char*) glGetString( GL_RENDERER );
const char* pVersString = (const char*) glGetString( GL_VERSION );
const char* pExtString = (const char*) glGetString( GL_EXTENSIONS );
// Output some driver info to the console:
Con::printf( "OpenGL driver information:" );
if ( pVendString )
Con::printf( " Vendor: %s", pVendString );
if ( pRendString )
Con::printf( " Renderer: %s", pRendString );
if ( pVersString )
Con::printf( " Version: %s", pVersString );
// pre-clear the structure
dMemset(&gGLState, 0, sizeof(gGLState));
if(pExtString)
{
// EXT_paletted_texture ========================================
if (dStrstr(pExtString, (const char*)"GL_EXT_paletted_texture") != NULL)
gGLState.suppPalettedTexture = true;
// EXT_compiled_vertex_array ========================================
gGLState.suppLockedArrays = false;
if (dStrstr(pExtString, (const char*)"GL_EXT_compiled_vertex_array") != NULL)
gGLState.suppLockedArrays = true;
// ARB_multitexture ========================================
if (dStrstr(pExtString, (const char*)"GL_ARB_multitexture") != NULL)
gGLState.suppARBMultitexture = true;
// EXT_blend_color
if(dStrstr(pExtString, (const char*)"GL_EXT_blend_color") != NULL)
gGLState.suppEXTblendcolor = true;
// EXT_blend_minmax
if(dStrstr(pExtString, (const char*)"GL_EXT_blend_minmax") != NULL)
gGLState.suppEXTblendminmax = true;
// NV_vertex_array_range ========================================
// does not appear to be supported by apple, at all. ( as of 10.4.3 )
// GL_APPLE_vertex_array_range is similar, and may be nearly identical.
if (dStrstr(pExtString, (const char*)"GL_NV_vertex_array_range") != NULL)
gGLState.suppVertexArrayRange = true;
// EXT_fog_coord ========================================
if (dStrstr(pExtString, (const char*)"GL_EXT_fog_coord") != NULL)
gGLState.suppFogCoord = true;
// ARB_texture_compression ========================================
if (dStrstr(pExtString, (const char*)"GL_ARB_texture_compression") != NULL)
gGLState.suppTextureCompression = true;
// 3DFX_texture_compression_FXT1 ========================================
if (dStrstr(pExtString, (const char*)"GL_3DFX_texture_compression_FXT1") != NULL)
gGLState.suppFXT1 = true;
// EXT_texture_compression_S3TC ========================================
if (dStrstr(pExtString, (const char*)"GL_EXT_texture_compression_s3tc") != NULL)
gGLState.suppS3TC = true;
// EXT_vertex_buffer ========================================
// This extension is deprecated, and not supported by Apple. ( 10.4.3 )
// Instead, the ARB Vertex Buffer extension is supported.
// The new extension has a different API, so TGE should be updated to use it.
if (dStrstr(pExtString, (const char*)"GL_EXT_vertex_buffer") != NULL)
gGLState.suppVertexBuffer = true;
// Anisotropic filtering ========================================
gGLState.suppTexAnisotropic = (dStrstr(pExtString, (const char*)"GL_EXT_texture_filter_anisotropic") != NULL);
if (gGLState.suppTexAnisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gGLState.maxAnisotropy);
// Binary states, i.e., no supporting functions ========================================
// NOTE:
// Some of these have multiple representations, via EXT and|or ARB and|or NV and|or SGIS ... etc.
// Check all relative versions.
gGLState.suppPackedPixels = (dStrstr(pExtString, (const char*)"GL_EXT_packed_pixels") != NULL);
gGLState.suppPackedPixels |= (dStrstr(pExtString, (const char*)"GL_APPLE_packed_pixel") != NULL);
gGLState.suppTextureEnvCombine = (dStrstr(pExtString, (const char*)"GL_EXT_texture_env_combine") != NULL);
gGLState.suppTextureEnvCombine|= (dStrstr(pExtString, (const char*)"GL_ARB_texture_env_combine") != NULL);
gGLState.suppEdgeClamp = (dStrstr(pExtString, (const char*)"GL_EXT_texture_edge_clamp") != NULL);
gGLState.suppEdgeClamp |= (dStrstr(pExtString, (const char*)"GL_SGIS_texture_edge_clamp") != NULL);
gGLState.suppEdgeClamp |= (dStrstr(pExtString, (const char*)"GL_ARB_texture_border_clamp") != NULL);
gGLState.suppTexEnvAdd = (dStrstr(pExtString, (const char*)"GL_ARB_texture_env_add") != NULL);
gGLState.suppTexEnvAdd |= (dStrstr(pExtString, (const char*)"GL_EXT_texture_env_add") != NULL);
}
// Texture combine units ========================================
if (gGLState.suppARBMultitexture)
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gGLState.maxTextureUnits);
else
gGLState.maxTextureUnits = 1;
// Swap interval ========================================
// Mac inherently supports a swap interval via AGL-set-integer.
gGLState.suppSwapInterval = true;
// FSAA support, TODO: check for ARB multisample support
// multisample support should be checked via CGL
gGLState.maxFSAASamples = 4;
// dump found extensions to the console...
Con::printf("OpenGL Init: Enabled Extensions");
if (gGLState.suppARBMultitexture) Con::printf(" ARB_multitexture (Max Texture Units: %d)", gGLState.maxTextureUnits);
if (gGLState.suppEXTblendcolor) Con::printf(" EXT_blend_color");
if (gGLState.suppEXTblendminmax) Con::printf(" EXT_blend_minmax");
if (gGLState.suppPalettedTexture) Con::printf(" EXT_paletted_texture");
if (gGLState.suppLockedArrays) Con::printf(" EXT_compiled_vertex_array");
if (gGLState.suppVertexArrayRange) Con::printf(" NV_vertex_array_range");
if (gGLState.suppTextureEnvCombine) Con::printf(" EXT_texture_env_combine");
if (gGLState.suppPackedPixels) Con::printf(" EXT_packed_pixels");
if (gGLState.suppFogCoord) Con::printf(" EXT_fog_coord");
if (gGLState.suppTextureCompression) Con::printf(" ARB_texture_compression");
if (gGLState.suppS3TC) Con::printf(" EXT_texture_compression_s3tc");
if (gGLState.suppFXT1) Con::printf(" 3DFX_texture_compression_FXT1");
if (gGLState.suppTexEnvAdd) Con::printf(" (ARB|EXT)_texture_env_add");
if (gGLState.suppTexAnisotropic) Con::printf(" EXT_texture_filter_anisotropic (Max anisotropy: %f)", gGLState.maxAnisotropy);
if (gGLState.suppSwapInterval) Con::printf(" Vertical Sync");
if (gGLState.maxFSAASamples) Con::printf(" ATI_FSAA");
Con::warnf("OpenGL Init: Disabled Extensions");
if (!gGLState.suppARBMultitexture) Con::warnf(" ARB_multitexture");
if (!gGLState.suppEXTblendcolor) Con::warnf(" EXT_blend_color");
if (!gGLState.suppEXTblendminmax) Con::warnf(" EXT_blend_minmax");
if (!gGLState.suppPalettedTexture) Con::warnf(" EXT_paletted_texture");
if (!gGLState.suppLockedArrays) Con::warnf(" EXT_compiled_vertex_array");
if (!gGLState.suppVertexArrayRange) Con::warnf(" NV_vertex_array_range");
if (!gGLState.suppTextureEnvCombine) Con::warnf(" EXT_texture_env_combine");
if (!gGLState.suppPackedPixels) Con::warnf(" EXT_packed_pixels");
if (!gGLState.suppFogCoord) Con::warnf(" EXT_fog_coord");
if (!gGLState.suppTextureCompression) Con::warnf(" ARB_texture_compression");
if (!gGLState.suppS3TC) Con::warnf(" EXT_texture_compression_s3tc");
if (!gGLState.suppFXT1) Con::warnf(" 3DFX_texture_compression_FXT1");
if (!gGLState.suppTexEnvAdd) Con::warnf(" (ARB|EXT)_texture_env_add");
if (!gGLState.suppTexAnisotropic) Con::warnf(" EXT_texture_filter_anisotropic");
if (!gGLState.suppSwapInterval) Con::warnf(" Vertical Sync");
if (!gGLState.maxFSAASamples) Con::warnf(" ATI_FSAA");
Con::printf("");
// Set some console variables:
Con::setBoolVariable( "$FogCoordSupported", gGLState.suppFogCoord );
Con::setBoolVariable( "$TextureCompressionSupported", gGLState.suppTextureCompression );
Con::setBoolVariable( "$AnisotropySupported", gGLState.suppTexAnisotropic );
Con::setBoolVariable( "$PalettedTextureSupported", gGLState.suppPalettedTexture );
Con::setBoolVariable( "$SwapIntervalSupported", gGLState.suppSwapInterval );
if (!gGLState.suppPalettedTexture && Con::getBoolVariable("$pref::OpenGL::forcePalettedTexture",false))
{
Con::setBoolVariable("$pref::OpenGL::forcePalettedTexture", false);
Con::setBoolVariable("$pref::OpenGL::force16BitTexture", true);
}
// get fsaa samples. default to normal, no antialiasing
// TODO: clamp this against ARB_multisample capabilities.
gFSAASamples = Con::getIntVariable("$pref::OpenGL::numFSAASamples", 1);
// done. silently restore the old cgl context.
CGLSetCurrentContext(curr_ctx);
CGLDestroyContext(temp_ctx);
}
//-----------------------------------------------------------------------------
/// Change FSAA level.
/// Currently, this will only work for ATI Radeon chipsets.
/// On other chipsets, this has no effect.
/// Pass in 1 for normal/no-AA, 2 for 2x-AA, 4 for 4x-AA.
//-----------------------------------------------------------------------------
void dglSetFSAASamples(GLint aasamp)
{
if (gGLState.maxFSAASamples<2) return;
// fix out of range values
if (aasamp<1) aasamp = 1;
else
if (aasamp==3) aasamp = 2;
else
if (aasamp>4) aasamp = 4;
aglSetInteger(platState.ctx, ATI_FSAA_LEVEL, (const long*)&aasamp);
Con::printf(">>Number of FSAA samples is now [%d].", aasamp);
Con::setIntVariable("$pref::OpenGL::numFSAASamples", aasamp);
// Apperantly, we used to apply some Bling for tradeshows.
// it might be fun to reimplement this.
if (Con::getIntVariable("$pref::TradeshowDemo", 0))
Con::executef(2, "setFSAABadge", aasamp>1?"true":"false");
}

View File

@ -0,0 +1,920 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/platformInput.h"
#include "platform/event.h"
#include "console/console.h"
#include "platform/gameInterface.h"
#include "core/unicode.h"
// headers for clipboard access
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
// Static class variables:
InputManager* Input::smManager;
bool Input::smActive;
bool gInputEnabled = false;
bool gMouseEnabled = false;
bool gKBEnabled = false;
bool gMouseActive = false;
bool gKBActive = false;
//------------------------------------------------------------------------------
// Helper functions. Should migrate to an InputManager object at some point.
bool enableKeyboard(void);
void disableKeyboard(void);
bool activateKeyboard(void);
void deactivateKeyboard(void);
bool enableMouse(void);
void disableMouse(void);
bool activateMouse(void);
void deactivateMouse(void);
static void fillAsciiTable();
//------------------------------------------------------------------------------
//
// This function gets the standard ASCII code corresponding to our key code
// and the existing modifier key state.
//
//------------------------------------------------------------------------------
struct AsciiData
{
struct KeyData
{
U16 ascii;
bool isDeadChar;
};
KeyData upper;
KeyData lower;
KeyData goofy;
};
#define NUM_KEYS ( KEY_OEM_102 + 1 )
#define KEY_FIRST KEY_ESCAPE
static AsciiData AsciiTable[NUM_KEYS];
//--------------------------------------------------------------------------
void Input::init()
{
OSStatus status = noErr;
Con::printf( "Input Init:" );
destroy();
smManager = NULL;
smActive = false;
// set up the object that tells the system we want unicode input
InterfaceTypeList tsmDocTypeList = { kUnicodeDocument };
NewTSMDocument( 1, tsmDocTypeList, &platState.tsmDoc, NULL);
UseInputWindow( platState.tsmDoc, true);
// stop the double-cursor thing
Con::setBoolVariable("$pref::Gui::noClampTorqueCursorToWindow", true);
// enable main input
Input::enable();
Con::printf( "" );
}
//------------------------------------------------------------------------------
ConsoleFunction( isJoystickDetected, bool, 1, 1, "Always false on the MAC." )
{
/*
argc; argv;
return( DInputDevice::joystickDetected() );
*/
return(false);
}
//------------------------------------------------------------------------------
ConsoleFunction( getJoystickAxes, const char*, 2, 2, "(handle instance)" )
{
return( "" );
}
//------------------------------------------------------------------------------
static void fillAsciiTable()
{
}
//------------------------------------------------------------------------------
U16 Input::getKeyCode( U16 asciiCode )
{
U16 keyCode = 0;
U16 i;
// This is done three times so the lowerkey will always
// be found first. Some foreign keyboards have duplicate
// chars on some keys.
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
{
if ( AsciiTable[i].lower.ascii == asciiCode )
{
keyCode = i;
break;
};
}
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
{
if ( AsciiTable[i].upper.ascii == asciiCode )
{
keyCode = i;
break;
};
}
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
{
if ( AsciiTable[i].goofy.ascii == asciiCode )
{
keyCode = i;
break;
};
}
return( keyCode );
}
//------------------------------------------------------------------------------
U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
{
if ( keyCode >= NUM_KEYS )
return 0;
switch ( keyState )
{
case STATE_LOWER:
return AsciiTable[keyCode].lower.ascii;
case STATE_UPPER:
return AsciiTable[keyCode].upper.ascii;
case STATE_GOOFY:
return AsciiTable[keyCode].goofy.ascii;
default:
return(0);
}
}
//------------------------------------------------------------------------------
void Input::destroy()
{
#ifdef LOG_INPUT
if ( gInputLog )
{
log( "*** CLOSING LOG ***\n" );
CloseHandle( gInputLog );
gInputLog = NULL;
}
#endif
// turn us off.
if (gInputEnabled)
disable();
/*
if ( smManager && smManager->isEnabled() )
{
smManager->disable();
delete smManager;
smManager = NULL;
}
*/
}
//------------------------------------------------------------------------------
bool Input::enable()
{
//Con::printf( "[]Input::enable." );
gInputEnabled = true;
// if ( smManager && !smManager->isEnabled() )
// return( smManager->enable() );
enableMouse();
//enableKeyboard();
return( gInputEnabled );
}
//------------------------------------------------------------------------------
void Input::disable()
{
//Con::printf( "[]Input::disable." );
gInputEnabled = false;
// if ( smManager && smManager->isEnabled() )
// smManager->disable();
disableMouse();
//disableKeyboard();
}
//------------------------------------------------------------------------------
void Input::activate()
{
smActive = true;
enableMouse();
// enableKeyboard();
}
//------------------------------------------------------------------------------
void Input::deactivate()
{
//Con::printf( "[]Input::deactivate." );
deactivateMouse();
//deactivateKeyboard();
smActive = false;
}
//------------------------------------------------------------------------------
void Input::reactivate()
{
// don't think mac needs to do anything right now!!!!!! TBD
// This is soo hacky...
// SetForegroundWindow( winState.appWindow );
// PostMessage( winState.appWindow, WM_ACTIVATE, WA_ACTIVE, NULL );
}
//------------------------------------------------------------------------------
bool Input::isEnabled()
{
// if ( smManager )
// return smManager->isEnabled();
return(gInputEnabled);
}
//------------------------------------------------------------------------------
bool Input::isActive()
{
return smActive;
}
//------------------------------------------------------------------------------
void Input::process()
{
if (!smActive || !gInputEnabled)
return;
if (!gMouseEnabled || !gMouseActive)
return;
// if ( smManager && smManager->isEnabled() && smActive )
// smManager->process();
}
//------------------------------------------------------------------------------
InputManager* Input::getManager()
{
return( smManager );
}
//--------------------------------------------------------------------------
#pragma message("input remap table might need tweaking - rumors of ibooks having diff virt keycodes, might need intermediate remap...")
static U8 VcodeRemap[256] =
{
KEY_A, // 0x00
KEY_S, // 0x01
KEY_D, // 0x02
KEY_F, // 0x03
KEY_H, // 0x04
KEY_G, // 0x05
KEY_Z, // 0x06
KEY_X, // 0x07
KEY_C, // 0x08
KEY_V, // 0x09
KEY_Y, // 0x0A // this is questionable - not normal Y code
KEY_B, // 0x0B
KEY_Q, // 0x0C
KEY_W, // 0x0D
KEY_E, // 0x0E
KEY_R, // 0x0F
KEY_Y, // 0x10
KEY_T, // 0x11
KEY_1, // 0x12
KEY_2, // 0x13
KEY_3, // 0x14
KEY_4, // 0x15
KEY_6, // 0x16
KEY_5, // 0x17
KEY_EQUALS, // 0x18
KEY_9, // 0x19
KEY_7, // 0x1A
KEY_MINUS, // 0x1B
KEY_8, // 0x1C
KEY_0, // 0x1D
KEY_RBRACKET, // 0x1E
KEY_O, // 0x1F
KEY_U, // 0x20
KEY_LBRACKET, // 0x21
KEY_I, // 0x22
KEY_P, // 0x23
KEY_RETURN, // 0x24
KEY_L, // 0x25
KEY_J, // 0x26
KEY_APOSTROPHE, // 0x27
KEY_K, // 0x28
KEY_SEMICOLON, // 0x29
KEY_BACKSLASH, // 0x2A
KEY_COMMA, // 0x2B
KEY_SLASH, // 0x2C
KEY_N, // 0x2D
KEY_M, // 0x2E
KEY_PERIOD, // 0x2F
KEY_TAB, // 0x30
KEY_SPACE, // 0x31
KEY_TILDE, // 0x32
KEY_BACKSPACE, // 0x33
0, // 0x34 //?
KEY_ESCAPE, // 0x35
0, // 0x36 //?
KEY_ALT, // 0x37 // best mapping for mac Cmd key
KEY_LSHIFT, // 0x38
KEY_CAPSLOCK, // 0x39
KEY_MAC_OPT, // 0x3A // direct map mac Option key -- better than KEY_WIN_WINDOWS
KEY_CONTROL, // 0x3B
KEY_RSHIFT, // 0x3C
0, // 0x3D
0, // 0x3E
0, // 0x3F
0, // 0x40
KEY_DECIMAL, // 0x41
0, // 0x42
KEY_MULTIPLY, // 0x43
0, // 0x44
KEY_ADD, // 0x45
KEY_SUBTRACT, // 0x46 // secondary code?
KEY_NUMLOCK, // 0x47 // also known as Clear on mac...
KEY_SEPARATOR, // 0x48 // secondary code? for KPEqual
0, // 0x49
0, // 0x4A
KEY_DIVIDE, // 0x4B
KEY_NUMPADENTER, // 0x4C
KEY_DIVIDE, // 0x4D // secondary code?
KEY_SUBTRACT, // 0x4E
0, // 0x4F
0, // 0x50
KEY_SEPARATOR, // 0x51 // WHAT IS SEP? This is KPEqual on mac.
KEY_NUMPAD0, // 0x52
KEY_NUMPAD1, // 0x53
KEY_NUMPAD2, // 0x54
KEY_NUMPAD3, // 0x55
KEY_NUMPAD4, // 0x56
KEY_NUMPAD5, // 0x57
KEY_NUMPAD6, // 0x58
KEY_NUMPAD7, // 0x59
0, // 0x5A
KEY_NUMPAD8, // 0x5B
KEY_NUMPAD9, // 0x5C
0, // 0x5D
0, // 0x5E
0, // 0x5F
KEY_F5, // 0x60
KEY_F6, // 0x61
KEY_F7, // 0x62
KEY_F3, // 0x63
KEY_F8, // 0x64
KEY_F9, // 0x65
0, // 0x66
KEY_F11, // 0x67
0, // 0x68
KEY_PRINT, // 0x69
0, // 0x6A
KEY_SCROLLLOCK, // 0x6B
0, // 0x6C
KEY_F10, // 0x6D
0, // 0x6E
KEY_F12, // 0x6F
0, // 0x70
KEY_PAUSE, // 0x71
KEY_INSERT, // 0x72 // also known as mac Help
KEY_HOME, // 0x73
KEY_PAGE_UP, // 0x74
KEY_DELETE, // 0x75 // FwdDel
KEY_F4, // 0x76
KEY_END, // 0x77
KEY_F2, // 0x78
KEY_PAGE_DOWN, // 0x79
KEY_F1, // 0x7A
KEY_LEFT, // 0x7B
KEY_RIGHT, // 0x7C
KEY_DOWN, // 0x7D
KEY_UP, // 0x7E
0, // 0x7F
0, // 0x80
0, // 0x81
0, // 0x82
0, // 0x83
0, // 0x84
0, // 0x85
0, // 0x86
0, // 0x87
0, // 0x88
0, // 0x89
0, // 0x8A
0, // 0x8B
0, // 0x8C
0, // 0x8D
0, // 0x8E
0, // 0x8F
0, // 0x90
0, // 0x91
0, // 0x92
0, // 0x93
0, // 0x94
0, // 0x95
0, // 0x96
0, // 0x97
0, // 0x98
0, // 0x99
0, // 0x9A
0, // 0x9B
0, // 0x9C
0, // 0x9D
0, // 0x9E
0, // 0x9F
0, // 0xA0
0, // 0xA1
0, // 0xA2
0, // 0xA3
0, // 0xA4
0, // 0xA5
0, // 0xA6
0, // 0xA7
0, // 0xA8
0, // 0xA9
0, // 0xAA
0, // 0xAB
0, // 0xAC
0, // 0xAD
0, // 0xAE
0, // 0xAF
0, // 0xB0
0, // 0xB1
0, // 0xB2
0, // 0xB3
0, // 0xB4
0, // 0xB5
0, // 0xB6
0, // 0xB7
0, // 0xB8
0, // 0xB9
0, // 0xBA
0, // 0xBB
0, // 0xBC
0, // 0xBD
0, // 0xBE
0, // 0xBF
0, // 0xC0
0, // 0xC1
0, // 0xC2
0, // 0xC3
0, // 0xC4
0, // 0xC5
0, // 0xC6
0, // 0xC7
0, // 0xC8
0, // 0xC9
0, // 0xCA
0, // 0xCB
0, // 0xCC
0, // 0xCD
0, // 0xCE
0, // 0xCF
0, // 0xD0
0, // 0xD1
0, // 0xD2
0, // 0xD3
0, // 0xD4
0, // 0xD5
0, // 0xD6
0, // 0xD7
0, // 0xD8
0, // 0xD9
0, // 0xDA
0, // 0xDB
0, // 0xDC
0, // 0xDD
0, // 0xDE
0, // 0xDF
0, // 0xE0
0, // 0xE1
0, // 0xE2
0, // 0xE3
0, // 0xE4
0, // 0xE5
0, // 0xE6
0, // 0xE7
0, // 0xE8
0, // 0xE9
0, // 0xEA
0, // 0xEB
0, // 0xEC
0, // 0xED
0, // 0xEE
0, // 0xEF
0, // 0xF0
0, // 0xF1
0, // 0xF2
0, // 0xF3
0, // 0xF4
0, // 0xF5
0, // 0xF6
0, // 0xF7
0, // 0xF8
0, // 0xF9
0, // 0xFA
0, // 0xFB
0, // 0xFC
0, // 0xFD
0, // 0xFE
0 // 0xFF
};
U8 TranslateOSKeyCode(U8 vcode)
{
return VcodeRemap[vcode];
}
#pragma mark ---- Clipboard functions ----
//-----------------------------------------------------------------------------
const char* Platform::getClipboard()
{
// mac clipboards can contain multiple items,
// and each item can be in several differnt flavors,
// such as unicode or plaintext or pdf, etc.
// scan through the clipboard, and return the 1st piece of actual text.
ScrapRef clip;
char *retBuf = "";
OSStatus err = noErr;
char *dataBuf = "";
// get a local ref to the system clipboard
GetScrapByName( kScrapClipboardScrap, kScrapGetNamedScrap, &clip );
// First try to get unicode data, then try to get plain text data.
Size dataSize = 0;
bool plaintext = false;
err = GetScrapFlavorSize(clip, kScrapFlavorTypeUnicode, &dataSize);
if( err != noErr || dataSize <= 0)
{
Con::errorf("some error getting unicode clip");
plaintext = true;
err = GetScrapFlavorSize(clip, kScrapFlavorTypeText, &dataSize);
}
// kick out if we don't have any data.
if( err != noErr || dataSize <= 0)
{
Con::errorf("no data, kicking out. size = %i",dataSize);
return "";
}
if( err == noErr && dataSize > 0 )
{
// ok, we've got something! allocate a buffer and copy it in.
char buf[dataSize+1];
dMemset(buf, 0, dataSize+1);
dataBuf = buf;
// plain text needs no conversion.
// unicode data needs to be converted to normalized utf-8 format.
if(plaintext)
{
GetScrapFlavorData(clip, kScrapFlavorTypeText, &dataSize, &buf);
retBuf = Con::getReturnBuffer(dataSize + 1);
dMemcpy(retBuf,buf,dataSize);
}
else
{
GetScrapFlavorData(clip, kScrapFlavorTypeUnicode, &dataSize, &buf);
// normalize
CFStringRef cfBuf = CFStringCreateWithBytes(NULL, (const UInt8*)buf, dataSize, kCFStringEncodingUnicode, false);
CFMutableStringRef normBuf = CFStringCreateMutableCopy(NULL, 0, cfBuf);
CFStringNormalize(normBuf, kCFStringNormalizationFormC);
// convert to utf-8
U32 normBufLen = CFStringGetLength(normBuf);
U32 retBufLen = CFStringGetMaximumSizeForEncoding(normBufLen,kCFStringEncodingUTF8) + 1; // +1 for the null terminator
retBuf = Con::getReturnBuffer(retBufLen);
CFStringGetCString( normBuf, retBuf, retBufLen, kCFStringEncodingUTF8);
CFStringRef newCFstring = CFStringCreateWithBytes(NULL, retBuf, dStrlen(retBuf), kCFStringEncodingUTF8, false);
dataSize = retBufLen;
}
// manually null terminate, just in case.
retBuf[dataSize] = 0;
}
// return the data, or the empty string if we did not find any data.
return retBuf;
}
//-----------------------------------------------------------------------------
bool Platform::setClipboard(const char *text)
{
ScrapRef clip;
U32 textSize;
OSStatus err = noErr;
// make sure we have something to copy
textSize = dStrlen(text);
if(textSize == 0)
return false;
// get a local ref to the system clipboard
GetScrapByName( kScrapClipboardScrap, kScrapClearNamedScrap, &clip );
// put the data on the clipboard as text
err = PutScrapFlavor( clip, kScrapFlavorTypeText, kScrapFlavorMaskNone, textSize, text);
// put the data on the clipboard as unicode
const UTF16 *utf16Data = convertUTF8toUTF16(text);
err |= PutScrapFlavor( clip, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone,
dStrlen(utf16Data) * sizeof(UTF16), utf16Data);
delete [] utf16Data;
// and see if we were successful.
if( err == noErr )
return true;
else
return false;
}
#pragma mark -
//------------------------------------------------------------------------------
bool enableKeyboard()
{
if ( !gInputEnabled )
return( false );
if ( gKBEnabled && gKBActive )
return( true );
gKBEnabled = true;
if ( Input::isActive() )
gKBEnabled = activateKeyboard();
if ( gKBEnabled )
{
Con::printf( "Hardware-direct keyboard enabled." );
#ifdef LOG_INPUT
Input::log( "Keyboard enabled.\n" );
#endif
}
else
{
Con::warnf( "Hardware-direct keyboard failed to enable!" );
#ifdef LOG_INPUT
Input::log( "Keyboard failed to enable!\n" );
#endif
}
return( gKBEnabled );
}
//------------------------------------------------------------------------------
void disableKeyboard()
{
if ( !gInputEnabled || !gKBEnabled )
return;
deactivateKeyboard();
gKBEnabled = false;
Con::printf( "Hardware-direct keyboard disabled." );
#ifdef LOG_INPUT
Input::log( "Keyboard disabled.\n" );
#endif
}
//------------------------------------------------------------------------------
bool activateKeyboard()
{
if ( !gInputEnabled || !Input::isActive() || !gKBEnabled )
return( false );
OSStatus status = noErr;
if (status==noErr)
gKBActive = true;
#ifdef LOG_INPUT
Input::log( gKBActive ? "Keyboard activated.\n" : "Keyboard failed to activate!\n" );
#endif
return( gKBActive );
}
//------------------------------------------------------------------------------
void deactivateKeyboard()
{
if ( gInputEnabled && gKBActive )
{
OSStatus status = noErr;
gKBActive = false;
#ifdef LOG_INPUT
Input::log( "Keyboard deactivated.\n" );
#endif
}
}
//------------------------------------------------------------------------------
bool enableMouse()
{
if ( !gInputEnabled )
return( false );
if ( gMouseEnabled && gMouseActive )
return( true );
gMouseEnabled = activateMouse();
bool hwMouse = false;
// if (gMouseEnabled)
// Con::printf( "%s %s", hwMouse?"Hardware-direct mouse":"Basic mouse capture", "enabled.");
// else
// Con::warnf( "%s %s", hwMouse?"Hardware-direct mouse":"Basic mouse capture", "failed to enable!");
#ifdef LOG_INPUT
Input::log( "Mouse %s.\n", gMouseEnabled?"enabled":"failed to enable");
#endif
return( gMouseEnabled );
}
//------------------------------------------------------------------------------
void disableMouse()
{
if ( !gInputEnabled || !gMouseEnabled )
return;
deactivateMouse();
gMouseEnabled = false;
bool hwMouse = false;
Con::printf( "%s disabled", hwMouse?"Hardware-direct mouse":"Basic mouse capture");
#ifdef LOG_INPUT
Input::log( "Mouse disabled.\n" );
#endif
}
//------------------------------------------------------------------------------
bool activateMouse()
{
if ( !gInputEnabled || !Input::isActive() || !gMouseEnabled )
return( false );
if (gMouseActive)
return(true);
gMouseActive = true;
#ifdef LOG_INPUT
Input::log( gMouseActive ? "Mouse activated.\n" : "Mouse failed to activate!\n" );
#endif
return( gMouseActive );
}
//------------------------------------------------------------------------------
void deactivateMouse()
{
// Con::printf( ">> dm one. %s %s %s", gInputEnabled?"true":"false", gMouseActive?"true":"false");
if ( !gInputEnabled || !gMouseActive ) return;
gMouseActive = false;
// Con::printf( ">> dm two." );
#ifdef LOG_INPUT
Input::log( "Mouse deactivated.\n" );
#endif
}
//------------------------------------------------------------------------------
ConsoleFunction( enableMouse, bool, 1, 1, "enableMouse()" )
{
return( enableMouse() );
}
//------------------------------------------------------------------------------
ConsoleFunction( disableMouse, void, 1, 1, "disableMouse()" )
{
disableMouse();
}
//------------------------------------------------------------------------------
void printInputState(void)
{
if ( gInputEnabled )
{
Con::printf( "Low-level input system is enabled." );
Con::printf( "- Keyboard is %sabled and %sactive.",
gKBEnabled ? "en" : "dis",
gKBActive ? "" : "in" );
Con::printf( "- Mouse is %sabled and %sactive.",
gMouseEnabled ? "en" : "dis",
gMouseActive ? "" : "in" );
/*
Con::printf( "- Joystick is %sabled and %sactive.",
gJoystickEnabled() ? "en" : "dis",
gJoystickActive() ? "" : "in" );
*/
}
else
{
Con::printf( "Low-level input system is disabled." );
}
}
//------------------------------------------------------------------------------
ConsoleFunction( echoInputState, void, 1, 1, "echoInputState()" )
{
printInputState();
}
//------------------------------------------------------------------------------
ConsoleFunction( toggleInputState, void, 1, 1, "toggleInputState()" )
{
if (gInputEnabled)
Input::disable();
else
Input::enable();
printInputState();
}
//------------------------------------------------------------------------------
ConsoleFunction( deactivateKeyboard, void, 1, 1, "deactivateKeyboard();")
{
// these are only useful on the windows side. They deal with some vagaries of win32 DirectInput.
}
ConsoleFunction( activateKeyboard, void, 1, 1, "activateKeyboard();")
{
// these are only useful on the windows side. They deal with some vagaries of win32 DirectInput.
}
//------------------------------------------------------------------------------
void Input::setCursorPos(S32 x, S32 y)
{
if (!platState.appWindow )
return;
Rect r;
Point targetPoint;
GrafPtr savePort;
// get the center of the window
GetWindowBounds(platState.appWindow, kWindowContentRgn, &r);
targetPoint.h = r.left + x;
targetPoint.v = r.top + y;
CGDirectDisplayID displayID = platState.cgDisplay;
CGRect bounds = CGDisplayBounds(displayID);
CGPoint cgWndCenter;
cgWndCenter.x = targetPoint.h + bounds.origin.x;
cgWndCenter.y = targetPoint.v + bounds.origin.y;
CGSetLocalEventsSuppressionInterval(0);
CGDisplayMoveCursorToPoint(displayID, cgWndCenter);
CGSetLocalEventsSuppressionInterval(0.25);
}

View File

@ -0,0 +1,243 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platformMacCarb/macCarbEvents.h"
#include "platform/platformThread.h"
#include "platform/gameInterface.h"
#include "core/fileio.h"
//-----------------------------------------------------------------------------
// _MacCarbRunTorqueMain() starts the Game.main() loop
//-----------------------------------------------------------------------------
static void _MacCarbRunTorqueMain()
{
platState.torqueThreadId = Thread::getCurrentThreadId();
platState.windowSize.set(0,0);
platState.lastTimeTick = Platform::getRealMilliseconds();
platState.appReturn = Game->main(platState.argc, platState.argv);
if(!platState.headless)
{
HICommand cmd;
dMemset(&cmd, 0, sizeof(HICommand));
cmd.commandID = kHICommandQuit;
ProcessHICommand( &cmd );
}
}
//-----------------------------------------------------------------------------
// Handler stub for bsd signals.
//-----------------------------------------------------------------------------
static void _MacCarbSignalHandler(int )
{
// we send the torque thread a SIGALRM to wake it up from usleep()
// when transitioning from background - forground.
}
//-----------------------------------------------------------------------------
// Thread subclass, for running Torque in multithreaded mode
//-----------------------------------------------------------------------------
class TorqueMainThread : public Thread
{
public:
TorqueMainThread() : Thread(NULL,0,false) { }
virtual void run(S32 arg)
{
signal(SIGALRM, _MacCarbSignalHandler);
_MacCarbRunTorqueMain();
}
};
//-----------------------------------------------------------------------------
// RunAppEventLoop Callback, for running Torque in single threaded mode
//-----------------------------------------------------------------------------
void _MacCarbRAELCallback(EventLoopTimerRef theTimer, void *userData)
{
_MacCarbRunTorqueMain();
}
#pragma mark -
//-----------------------------------------------------------------------------
// command line arg processing
//-----------------------------------------------------------------------------
#define KEYISDOWN(key) ((((unsigned char *)currKeyState)[key>>3] >> (key & 7)) & 1)
static bool _MacCarbCheckProcessTxtFileArgs()
{
// this is yucky, but the easiest way to ignore the cmd line args:
KeyMap currKeyState;
GetKeys(currKeyState);
if (KEYISDOWN(0x38)) // check shift key -- actually LShift.
return false;
else
return true;
}
//-----------------------------------------------------------------------------
static void _MacCarbGetTxtFileArgs(int &argc, char** argv, int maxargc)
{
argc = 0;
const U32 kMaxTextLen = 2048; // arbitrary
U32 textLen;
char* text = new char[kMaxTextLen];
// open the file, kick out if we can't
File cmdfile;
File::Status err = cmdfile.open("maccmdline.txt", cmdfile.Read);
if(err != File::Ok)
return;
// read in the first kMaxTextLen bytes, kick out if we get errors or no data
err = cmdfile.read(kMaxTextLen-1, text, &textLen);
if(!((err == File::Ok || err == File::EOS) || textLen > 0))
{
cmdfile.close();
return;
}
// null terminate
text[textLen++] = '\0';
// truncate to the 1st line of the file
for(int i = 0; i < textLen; i++)
{
if( text[i] == '\n' || text[i] == '\r' )
{
text[i] = '\0';
textLen = i+1;
break;
}
}
// tokenize the args with nulls, save them in argv, count them in argc
char* tok;
for(tok = dStrtok(text, " "); tok && argc < maxargc; tok = dStrtok(NULL, " "))
{
argv[argc++] = tok;
}
}
//-----------------------------------------------------------------------------
static void _MacCarbFilterCmdLineArgs( int &argc, char** argv)
{
// MacOSX gui apps get at least 2 args: the full path to their binary,
// and a process serial number, formed something like: "-psn_0_123456".
// Torque doesnt want to see the psn arg, so we strip it out.
int j = 0;
for(int i = 0; i < argc; i++)
{
if(dStrncmp(argv[i], "-psn", 4) != 0)
argv[j++] = argv[i];
if(dStrncmp(argv[i], "-headless", 9) == 0)
{
printf("entering headless mode\n");
platState.headless = true;
}
}
argc = j;
}
#pragma mark -
//-----------------------------------------------------------------------------
// main() - the real one - this is the actual program entry point.
//-----------------------------------------------------------------------------
S32 main(S32 argc, const char **argv)
{
const int kMaxCmdlineArgs = 32; // arbitrary
// get the actual command line args
S32 newArgc = argc;
char* newArgv[kMaxCmdlineArgs];
for(int i=0; i < argc && i < kMaxCmdlineArgs; i++)
newArgv[i] = argv[i];
if( _MacCarbCheckProcessTxtFileArgs() )
{
// get the text file args
S32 textArgc;
char* textArgv[kMaxCmdlineArgs];
_MacCarbGetTxtFileArgs(textArgc, textArgv, kMaxCmdlineArgs);
// merge them
int i=0;
while(i < textArgc && newArgc < kMaxCmdlineArgs)
newArgv[newArgc++] = textArgv[i++];
}
// filter them
_MacCarbFilterCmdLineArgs( newArgc, newArgv);
// store them in platState
platState.argc = newArgc;
platState.argv = newArgv;
MacCarbInit1020CompatInit();
// Headless mode is for sitations where torque must run as a command line
// tool, without a connection to the window server. Any windowing or event
// calls may crash the app if there is no window server, so we avoid them
// in headless mode.
if(!platState.headless)
{
InitCursor();
FlushEvents( everyEvent, 0 );
SetEventMask(everyEvent);
// push us to the front, just to be sure
ProcessSerialNumber psn = { 0, kCurrentProcess };
SetFrontProcess(&psn);
}
// save away OS version info into platState.
Gestalt(gestaltSystemVersion, (SInt32 *) &(platState.osVersion));
// Update the current working directory.
Platform::getWorkingDirectory();
// now, we prepare to hand off execution to torque & macosx.
platState.appReturn = 0;
platState.firstThreadId = Thread::getCurrentThreadId();
#if !defined(TORQUE_MULTITHREAD)
// Install a one-shot timer to run the game, then call RAEL to install
// the default application handler (which can't be called directly).
EventLoopTimerRef timer;
InstallEventLoopTimer(GetCurrentEventLoop(), 0, 0,
NewEventLoopTimerUPP(_MacCarbRAELCallback), NULL, &timer);
RunApplicationEventLoop();
#else
// Put the Torque application loop in one thread, and the event listener loop
// in the other thread. The event loop must use the process's initial thread.
// We need to cache a ref to the main event queue because GetMainEventQueue
// is not thread safe pre 10.4 .
platState.mainEventQueue = GetMainEventQueue();
// We need to install event handlers for interthread communication.
// Events and some system calls must happen in the process's initial thread.
MacCarbInstallTorqueCarbonEventHandlers();
TorqueMainThread mainLoop;
mainLoop.start();
if(!platState.headless)
{
//printf("starting RAEL\n");
RunApplicationEventLoop();
}
//printf("trying to join main loop...\n");
mainLoop.join();
//printf("main loop joined.\n");
#endif
InitCursor(); // don't leave it in a screwy state...
//printf("exiting...\n");
return(platState.appReturn);
}

View File

@ -0,0 +1,138 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/platform.h"
#include "console/console.h"
#include "math/mMath.h"
#include "terrain/blender.h"
extern void mInstallLibrary_C();
extern void mInstallLibrary_Vec();
extern void mInstall_Library_SSE();
extern void mInstallLibrary_ASM();
//--------------------------------------
ConsoleFunction( MathInit, void, 1, 10, "([DETECT|C|VEC|SSE])")
{
U32 properties = CPU_PROP_C; // C entensions are always used
if (argc == 1)
{
Math::init(0);
return;
}
for (argc--, argv++; argc; argc--, argv++)
{
if (dStricmp(*argv, "DETECT") == 0) {
Math::init(0);
return;
}
if (dStricmp(*argv, "C") == 0) {
properties |= CPU_PROP_C;
continue;
}
if (dStricmp(*argv, "VEC") == 0) {
properties |= CPU_PROP_ALTIVEC;
continue;
}
if (dStricmp(*argv, "SSE") == 0) {
properties |= CPU_PROP_SSE;
continue;
}
Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv);
}
Math::init(properties);
}
//------------------------------------------------------------------------------
void Math::init(U32 properties)
{
if (!properties)
// detect what's available
properties = Platform::SystemInfo.processor.properties;
else
// Make sure we're not asking for anything that's not supported
properties &= Platform::SystemInfo.processor.properties;
Con::printf("Math Init:");
Con::printf(" Installing Standard C extensions");
mInstallLibrary_C();
if(Platform::SystemInfo.processor.type == CPU_X86Compatible)
{
Con::printf(" Installing Assembly extensions");
mInstallLibrary_ASM();
}
#if defined(__VEC__)
Blender::smUseVecBlender = false;
if (properties & CPU_PROP_ALTIVEC)
{
Con::printf(" Installing Altivec extensions");
mInstallLibrary_Vec();
Blender::smUseVecBlender = true;
}
#endif
if (properties & CPU_PROP_SSE)
{
Con::printf(" Installing SSE extensions");
mInstall_Library_SSE();
}
Con::printf(" ");
}
//------------------------------------------------------------------------------
F32 Platform::getRandom()
{
return platState.platRandom.randF();
}
//-----------------------------------------------------------------------------
// Intel asm math
//-----------------------------------------------------------------------------
#if defined(i386)
static S32 m_mulDivS32_ASM(S32 a, S32 b, S32 c)
{ // a * b / c
S32 r;
__asm__ __volatile__(
"imul %2\n"
"idiv %3\n"
: "=a" (r) : "a" (a) , "b" (b) , "c" (c)
);
return r;
}
static U32 m_mulDivU32_ASM(S32 a, S32 b, U32 c)
{ // a * b / c
S32 r;
__asm__ __volatile__(
"mov $0, %%edx\n"
"mul %2\n"
"div %3\n"
: "=a" (r) : "a" (a) , "b" (b) , "c" (c)
);
return r;
}
#endif
//------------------------------------------------------------------------------
void mInstallLibrary_ASM()
{
// stubbed out for ppc builds
#if defined(i386)
m_mulDivS32 = m_mulDivS32_ASM;
m_mulDivU32 = m_mulDivU32_ASM;
#endif
}

View File

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include <stdlib.h>
#include <string.h>
//--------------------------------------
#ifdef new
#undef new
#endif
void* FN_CDECL operator new(dsize_t dt, void* ptr)
{
return (ptr);
}
//--------------------------------------
void* dRealMalloc(dsize_t in_size)
{
return malloc(in_size);
}
//--------------------------------------
void dRealFree(void* in_pFree)
{
free(in_pFree);
}
void* dMemcpy(void *dst, const void *src, dsize_t size)
{
return memcpy(dst,src,size);
}
//--------------------------------------
void* dMemmove(void *dst, const void *src, dsize_t size)
{
return memmove(dst,src,size);
}
//--------------------------------------
void* dMemset(void *dst, int c, dsize_t size)
{
return memset(dst,c,size);
}
//--------------------------------------
int dMemcmp(const void *ptr1, const void *ptr2, dsize_t len)
{
return(memcmp(ptr1, ptr2, len));
}

View File

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include "platform/platform.h"
#include "platform/platformMutex.h"
#include "platform/platformThread.h"
// TODO: examine & dump errno if pthread_* funcs fail. ( only in debug build )
typedef struct MacCarbMutex
{
pthread_mutex_t mMutex;
bool locked;
U32 lockedByThread;
};
void* Mutex::createMutex(void)
{
bool ok;
MacCarbMutex *m = new MacCarbMutex();
pthread_mutexattr_t attr;
ok = pthread_mutexattr_init(&attr);
ok = pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
ok = pthread_mutex_init(&(m->mMutex),&attr);
AssertFatal(ok == 0, "Mutex::createMutex() failed: pthread_mutex_init() failed.");
m->locked = false;
m->lockedByThread = 0;
return (void*)m;
}
void Mutex::destroyMutex(void* mutex)
{
bool ok;
MacCarbMutex *m = (MacCarbMutex*)mutex;
ok = pthread_mutex_destroy( &(m->mMutex) );
AssertFatal(ok == 0, "Mutex::destroyMutex() failed: pthread_mutex_destroy() failed.");
delete mutex;
}
bool Mutex::lockMutex( void *mutex, bool block)
{
int ok;
MacCarbMutex *m = (MacCarbMutex*)mutex;
if(block)
{
ok = pthread_mutex_lock( &(m->mMutex) );
AssertFatal( ok != EINVAL, "Mutex::lockMutex() failed: invalid mutex.");
AssertFatal( ok != EDEADLK, "Mutex::lockMutex() failed: system detected a deadlock!");
AssertFatal( ok == 0, "Mutex::lockMutex() failed: pthread_mutex_lock() failed -- unknown reason.");
}
else {
ok = pthread_mutex_trylock( &(m->mMutex) );
// returns EBUSY if mutex was locked by another thread,
// returns EINVAL if mutex was not a valid mutex pointer,
// returns 0 if lock succeeded.
AssertFatal( ok != EINVAL, "Mutex::lockMutex(non blocking) failed: invalid mutex.");
if( ok != 0 )
return false;
AssertFatal( ok == 0, "Mutex::lockMutex(non blocking) failed: pthread_mutex_trylock() failed -- unknown reason.");
}
m->locked = true;
m->lockedByThread = Thread::getCurrentThreadId();
return true;
}
void Mutex::unlockMutex( void *mutex)
{
bool ok;
MacCarbMutex *m = (MacCarbMutex*)mutex;
ok = pthread_mutex_unlock( &(m->mMutex) );
AssertFatal( ok == 0, "Mutex::unlockMutex() failed: pthread_mutex_unlock() failed.");
m->locked = false;
m->lockedByThread = 0;
}

View File

@ -0,0 +1,57 @@
// macCarbNPatch.h
//
// mac specific implementation(s) of NPatch functionality
// since each platform might use slightly diff methods to control.
// current Mac NPatches is ATI TRUFORM implementation, accessed on OS9 via a
// back door method. OSX tests for the ATIX extension.
#if !defined(TORQUE_OS_MAC_OSX)
#define AGLSETINT_NPATCH_FLAG ((unsigned long)500)
#define AGLSETINT_NPATCH_LOD ((unsigned long)501)
#define AGLSETINT_NPATCH_POINTINTERP ((unsigned long)502)
#define AGLSETINT_NPATCH_NORMALINTERP ((unsigned long)503)
#endif
// for the moment, this seems to be the best roundup of
// the npatch extensions on the PC.
#ifndef GL_ATIX_pn_triangles
#define GL_ATIX_pn_triangles 1
#define GL_PN_TRIANGLES_ATIX 0x6090
#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATIX 0x6091
#define GL_PN_TRIANGLES_POINT_MODE_ATIX 0x6092
#define GL_PN_TRIANGLES_NORMAL_MODE_ATIX 0x6093
#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATIX 0x6094
#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATIX 0x6095
#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATIX 0x6096
#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATIX 0x6097
#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATIX 0x6098
#if defined(TORQUE_OS_MAC_OSX) // for the moment...
extern void glPNTrianglesiATIX(GLenum pname, GLint param);
extern void glPNTrianglesfATIX(GLenum pname, GLfloat param);
#endif
#endif
typedef void (*PFNGLPNTRIANGLESIATIPROC)(GLenum pname, GLint param);
//typedef void (APIENTRY *PFNGLPNTRIANGLESFATIPROC)(GLenum pname, GLfloat param);
#define GL_NPATCH_EXT_STRING "GL_ATIX_pn_triangles"
#define GL_NPATCH_SETINT_STRING "glPNTrianglesiATIX"
typedef PFNGLPNTRIANGLESIATIPROC PFNNPatchSetInt;
#define GETINT_NPATCH_MAX_LEVEL GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATIX
#define GL_NPATCH_FLAG GL_PN_TRIANGLES_ATIX
#define SETINT_NPATCH_LOD GL_PN_TRIANGLES_TESSELATION_LEVEL_ATIX
#define SETINT_NPATCH_POINTINTERP GL_PN_TRIANGLES_POINT_MODE_ATIX
#define SETINT_NPATCH_NORMALINTERP GL_PN_TRIANGLES_NORMAL_MODE_ATIX
#define NPATCH_POINTINTERP_MIN GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATIX
#define NPATCH_POINTINTERP_MAX GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATIX
#define NPATCH_NORMALINTERP_MIN GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATIX
#define NPATCH_NORMALINTERP_MAX GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATIX

View File

@ -0,0 +1,845 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// Touch-ups by William Taysom
#include "PlatformMacCarb/platformMacCarb.h"
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformNetAsync.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/time.h>
// Header clean-up by William Taysom
#include <sys/ioctl.h>
// IPX fixes from William Taysom.
#define IPX_NODE_LEN 6
// for 10.2 compatability...
#ifndef socklen_t
#define socklen_t unsigned int
#endif
struct sockaddr_ipx
{
sa_family_t sipx_family;
U16 sipx_port;
U32 sipx_network;
unsigned char sipx_node[IPX_NODE_LEN];
U8 sipx_type;
unsigned char sipx_zero; /* 16 byte fill */
};
// end wtaysom changes (May 26, 2004)
#include <stdlib.h>
#include "console/console.h"
#include "platform/gameInterface.h"
#include "core/fileStream.h"
#include "core/tVector.h"
static Net::Error getLastError();
static S32 defaultPort = 28000;
static S32 netPort = 0;
static int ipxSocket = InvalidSocket;
static int udpSocket = InvalidSocket;
// local enum for socket states for polled sockets
enum SocketState
{
InvalidState,
Connected,
ConnectionPending,
Listening,
NameLookupRequired
};
// the Socket structure helps us keep track of the
// above states
struct Socket
{
Socket()
{
fd = InvalidSocket;
state = InvalidState;
remoteAddr[0] = 0;
remotePort = -1;
}
NetSocket fd;
S32 state;
char remoteAddr[256];
S32 remotePort;
};
// list of polled sockets
static Vector<Socket*> gPolledSockets;
static Socket* addPolledSocket(NetSocket& fd, S32 state,
char* remoteAddr = NULL, S32 port = -1)
{
Socket* sock = new Socket();
sock->fd = fd;
sock->state = state;
if (remoteAddr)
dStrcpy(sock->remoteAddr, remoteAddr);
if (port != -1)
sock->remotePort = port;
gPolledSockets.push_back(sock);
return sock;
}
enum {
MaxConnections = 1024,
};
bool netSocketWaitForWritable(NetSocket fd, S32 timeoutMs)
{
fd_set writefds;
timeval timeout;
FD_ZERO(&writefds);
FD_SET( fd, &writefds );
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = ( timeoutMs % 1000 ) * 1000;
if( select(fd + 1, NULL, &writefds, NULL, &timeout) > 0 )
return true;
return false;
}
bool Net::init()
{
NetAsync::startAsync();
return(true);
}
void Net::shutdown()
{
while (gPolledSockets.size() > 0)
closeConnectTo(gPolledSockets[0]->fd);
closePort();
NetAsync::stopAsync();
}
static void netToIPSocketAddress(const NetAddress *address, struct sockaddr_in *sockAddr)
{
dMemset(sockAddr, 0, sizeof(struct sockaddr_in));
sockAddr->sin_family = AF_INET;
sockAddr->sin_port = htons(address->port);
char tAddr[20];
dSprintf(tAddr, 20, "%d.%d.%d.%d\n", address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3]);
//fprintf(stdout,"netToIPSocketAddress(): %s\n",tAddr);fflush(NULL);
sockAddr->sin_addr.s_addr = inet_addr(tAddr);
}
static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address)
{
address->type = NetAddress::IPAddress;
address->port = htons(sockAddr->sin_port);
char *tAddr;
tAddr = inet_ntoa(sockAddr->sin_addr);
//fprintf(stdout,"IPSocketToNetAddress(): %s\n",tAddr);fflush(NULL);
U8 nets[4];
nets[0] = atoi(strtok(tAddr, "."));
nets[1] = atoi(strtok(NULL, "."));
nets[2] = atoi(strtok(NULL, "."));
nets[3] = atoi(strtok(NULL, "."));
//fprintf(stdout,"0 = %d, 1 = %d, 2 = %d, 3 = %d\n", nets[0], nets[1], nets[2], nets[3]);
address->netNum[0] = nets[0];
address->netNum[1] = nets[1];
address->netNum[2] = nets[2];
address->netNum[3] = nets[3];
}
static void netToIPXSocketAddress(const NetAddress *address, sockaddr_ipx *sockAddr)
{
dMemset(sockAddr, 0, sizeof(sockaddr_ipx));
sockAddr->sipx_family = AF_INET;
sockAddr->sipx_port = htons(address->port);
sockAddr->sipx_network = address->netNum[0];
sockAddr->sipx_node[0] = address->nodeNum[0];
sockAddr->sipx_node[1] = address->nodeNum[1];
sockAddr->sipx_node[2] = address->nodeNum[2];
sockAddr->sipx_node[3] = address->nodeNum[3];
sockAddr->sipx_node[4] = address->nodeNum[4];
sockAddr->sipx_node[5] = address->nodeNum[5];
}
NetSocket Net::openListenPort(U16 port)
{
if(Game->isJournalReading())
{
U32 ret;
Game->journalRead(&ret);
return NetSocket(ret);
}
NetSocket sock = openSocket();
if (sock == InvalidSocket)
{
Con::errorf("Unable to open listen socket: %s", strerror(errno));
return InvalidSocket;
}
if (bind(sock, port) != NoError)
{
Con::errorf("Unable to bind port %d: %s", port, strerror(errno));
::close(sock);
return InvalidSocket;
}
if (listen(sock, 4) != NoError)
{
Con::errorf("Unable to listen on port %d: %s", port, strerror(errno));
::close(sock);
return InvalidSocket;
}
setBlocking(sock, false);
addPolledSocket(sock, Listening);
if (Game->isJournalWriting())
Game->journalWrite(U32(sock));
return sock;
}
NetSocket Net::openConnectTo(const char *addressString)
{
if(!dStrnicmp(addressString, "ipx:", 4))
return InvalidSocket;
if(!dStrnicmp(addressString, "ip:", 3))
addressString += 3; // eat off the ip:
char remoteAddr[256];
dStrcpy(remoteAddr, addressString);
char *portString = dStrchr(remoteAddr, ':');
U16 port;
if(portString)
{
*portString++ = 0;
port = htons(dAtoi(portString));
}
else
port = htons(defaultPort);
if(!dStricmp(remoteAddr, "broadcast"))
return InvalidSocket;
if(Game->isJournalReading())
{
U32 ret;
Game->journalRead(&ret);
return NetSocket(ret);
}
NetSocket sock = openSocket();
setBlocking(sock, false);
sockaddr_in ipAddr;
dMemset(&ipAddr, 0, sizeof(ipAddr));
if (inet_aton(remoteAddr, &ipAddr.sin_addr) != 0)
{
ipAddr.sin_port = port;
ipAddr.sin_family = AF_INET;
if(::connect(sock, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1 &&
errno != EINPROGRESS)
{
Con::errorf("Error connecting %s: %s",
addressString, strerror(errno));
::close(sock);
sock = InvalidSocket;
}
if(sock != InvalidSocket) {
// add this socket to our list of polled sockets
addPolledSocket(sock, ConnectionPending);
}
}
else
{
// need to do an asynchronous name lookup. first, add the socket
// to the polled list
addPolledSocket(sock, NameLookupRequired, remoteAddr, port);
// queue the lookup
gNetAsync.queueLookup(remoteAddr, sock);
}
if(Game->isJournalWriting())
Game->journalWrite(U32(sock));
return sock;
}
void Net::closeConnectTo(NetSocket sock)
{
if(Game->isJournalReading())
return;
// if this socket is in the list of polled sockets, remove it
for (int i = 0; i < gPolledSockets.size(); ++i)
if (gPolledSockets[i]->fd == sock)
{
delete gPolledSockets[i];
gPolledSockets.erase(i);
break;
}
closeSocket(sock);
}
Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, int bufferSize)
{
if(Game->isJournalReading())
{
U32 e;
Game->journalRead(&e);
return (Net::Error) e;
}
Net::Error e = send(socket, buffer, bufferSize);
if(Game->isJournalWriting())
Game->journalWrite(U32(e));
return e;
}
bool Net::openPort(S32 port)
{
if(udpSocket != InvalidSocket)
close(udpSocket);
if(ipxSocket != InvalidSocket)
close(ipxSocket);
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
ipxSocket = socket(AF_IPX, SOCK_DGRAM, 0);
if(udpSocket != InvalidSocket)
{
Net::Error error;
error = bind(udpSocket, port);
if(error == NoError)
error = setBufferSize(udpSocket, 32768);
if(error == NoError)
error = setBroadcast(udpSocket, true);
if(error == NoError)
error = setBlocking(udpSocket, false);
if(error == NoError)
Con::printf("UDP initialized on port %d", port);
else
{
close(udpSocket);
udpSocket = InvalidSocket;
Con::printf("Unable to initialize UDP - error %d", error);
}
}
if(ipxSocket != InvalidSocket)
{
Net::Error error = NoError;
sockaddr_ipx ipxAddress;
memset((char *)&ipxAddress, 0, sizeof(ipxAddress));
ipxAddress.sipx_family = AF_IPX;
ipxAddress.sipx_port = htons(port);
S32 err = ::bind(ipxSocket, (struct sockaddr *)&ipxAddress, sizeof(ipxAddress));
if(err)
error = getLastError();
if(error == NoError)
error = setBufferSize(ipxSocket, 32768);
if(error == NoError)
error = setBroadcast(ipxSocket, true);
if(error == NoError)
error = setBlocking(ipxSocket, false);
if(error == NoError)
Con::printf("IPX initialized on port %d", port);
else
{
close(ipxSocket);
ipxSocket = InvalidSocket;
Con::printf("Unable to initialize IPX - error %d", error);
}
}
netPort = port;
return ipxSocket != InvalidSocket || udpSocket != InvalidSocket;
}
void Net::closePort()
{
if(ipxSocket != InvalidSocket)
close(ipxSocket);
if(udpSocket != InvalidSocket)
close(udpSocket);
}
Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize)
{
if(Game->isJournalReading())
return NoError;
if(address->type == NetAddress::IPAddress)
{
sockaddr_in ipAddr;
netToIPSocketAddress(address, &ipAddr);
if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0,
(sockaddr *) &ipAddr, sizeof(sockaddr_in)) == -1)
return getLastError();
else
return NoError;
}
}
void Net::process()
{
sockaddr sa;
PacketReceiveEvent receiveEvent;
for(;;)
{
U32 addrLen = sizeof(sa);
S32 bytesRead = -1;
if(udpSocket != InvalidSocket)
bytesRead = recvfrom(udpSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
if(bytesRead == -1 && ipxSocket != InvalidSocket)
{
addrLen = sizeof(sa);
bytesRead = recvfrom(ipxSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
}
if(bytesRead == -1)
break;
if(sa.sa_family == AF_INET)
IPSocketToNetAddress((sockaddr_in *) &sa, &receiveEvent.sourceAddress);
else
continue;
NetAddress &na = receiveEvent.sourceAddress;
if(na.type == NetAddress::IPAddress &&
na.netNum[0] == 127 &&
na.netNum[1] == 0 &&
na.netNum[2] == 0 &&
na.netNum[3] == 1 &&
na.port == netPort)
continue;
if(bytesRead <= 0)
continue;
receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead;
Game->postEvent(receiveEvent);
}
// process the polled sockets. This blob of code performs functions
// similar to WinsockProc in winNet.cc
if (gPolledSockets.size() == 0)
return;
static ConnectedNotifyEvent notifyEvent;
static ConnectedAcceptEvent acceptEvent;
static ConnectedReceiveEvent cReceiveEvent;
S32 optval;
socklen_t optlen = sizeof(S32);
S32 bytesRead;
Net::Error err;
bool removeSock = false;
Socket *currentSock = NULL;
sockaddr_in ipAddr;
NetSocket incoming = InvalidSocket;
char out_h_addr[1024];
int out_h_length = 0;
for (S32 i = 0; i < gPolledSockets.size();
/* no increment, this is done at end of loop body */)
{
removeSock = false;
currentSock = gPolledSockets[i];
switch (currentSock->state)
{
case InvalidState:
Con::errorf("Error, InvalidState socket in polled sockets list");
break;
case ConnectionPending:
notifyEvent.tag = currentSock->fd;
// see if it is now connected
if (getsockopt(currentSock->fd, SOL_SOCKET, SO_ERROR,
&optval, &optlen) == -1)
{
Con::errorf("Error getting socket options: %s", strerror(errno));
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
Game->postEvent(notifyEvent);
removeSock = true;
}
else
{
if (optval == EINPROGRESS)
// still connecting...
break;
if (optval == 0)
{
// poll for writable status to be sure we're connected.
bool ready = netSocketWaitForWritable(currentSock->fd,0);
if(!ready)
break;
// connected.
notifyEvent.state = ConnectedNotifyEvent::Connected;
Game->postEvent(notifyEvent);
currentSock->state = Connected;
}
else
{
// some kind of error
Con::errorf("Error connecting: %s", strerror(errno));
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
Game->postEvent(notifyEvent);
removeSock = true;
}
}
break;
case Connected:
bytesRead = 0;
// try to get some data
err = Net::recv(currentSock->fd, cReceiveEvent.data, MaxPacketDataSize, &bytesRead);
if(err == Net::NoError)
{
if (bytesRead > 0)
{
// got some data, post it
cReceiveEvent.tag = currentSock->fd;
cReceiveEvent.size = ConnectedReceiveEventHeaderSize +
bytesRead;
Game->postEvent(cReceiveEvent);
}
else
{
// zero bytes read means EOF
if (bytesRead < 0)
// ack! this shouldn't happen
Con::errorf("Unexpected error on socket: %s",
strerror(errno));
notifyEvent.tag = currentSock->fd;
notifyEvent.state = ConnectedNotifyEvent::Disconnected;
Game->postEvent(notifyEvent);
removeSock = true;
}
}
else if (err != Net::NoError && err != Net::WouldBlock)
{
Con::errorf("Error reading from socket: %s", strerror(errno));
notifyEvent.tag = currentSock->fd;
notifyEvent.state = ConnectedNotifyEvent::Disconnected;
Game->postEvent(notifyEvent);
removeSock = true;
}
break;
case NameLookupRequired:
// is the lookup complete?
if (!gNetAsync.checkLookup(
currentSock->fd, out_h_addr, &out_h_length,
sizeof(out_h_addr)))
break;
notifyEvent.tag = currentSock->fd;
if (out_h_length == -1)
{
Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr);
notifyEvent.state = ConnectedNotifyEvent::DNSFailed;
removeSock = true;
}
else
{
// try to connect
dMemcpy(&(ipAddr.sin_addr.s_addr), out_h_addr, out_h_length);
ipAddr.sin_port = currentSock->remotePort;
ipAddr.sin_family = AF_INET;
if(::connect(currentSock->fd, (struct sockaddr *)&ipAddr,
sizeof(ipAddr)) == -1)
{
if (errno == EINPROGRESS)
{
notifyEvent.state = ConnectedNotifyEvent::DNSResolved;
currentSock->state = ConnectionPending;
}
else
{
Con::errorf("Error connecting to %s: %s",
currentSock->remoteAddr, strerror(errno));
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
removeSock = true;
}
}
else
{
notifyEvent.state = ConnectedNotifyEvent::Connected;
currentSock->state = Connected;
}
}
Game->postEvent(notifyEvent);
break;
case Listening:
incoming =
Net::accept(currentSock->fd, &acceptEvent.address);
if(incoming != InvalidSocket)
{
acceptEvent.portTag = currentSock->fd;
acceptEvent.connectionTag = incoming;
setBlocking(incoming, false);
addPolledSocket(incoming, Connected);
Game->postEvent(acceptEvent);
}
break;
}
// only increment index if we're not removing the connection, since
// the removal will shift the indices down by one
if (removeSock)
closeConnectTo(currentSock->fd);
else
i++;
}
}
NetSocket Net::openSocket()
{
int retSocket;
retSocket = socket(AF_INET, SOCK_STREAM, 0);
if(retSocket == InvalidSocket)
return InvalidSocket;
else
return retSocket;
}
Net::Error Net::closeSocket(NetSocket socket)
{
if(socket != InvalidSocket)
{
if(!close(socket))
return NoError;
else
return getLastError();
}
else
return NotASocket;
}
Net::Error Net::connect(NetSocket socket, const NetAddress *address)
{
if(address->type != NetAddress::IPAddress)
return WrongProtocolType;
sockaddr_in socketAddress;
netToIPSocketAddress(address, &socketAddress);
if(!::connect(socket, (sockaddr *) &socketAddress, sizeof(socketAddress)))
return NoError;
return getLastError();
}
Net::Error Net::listen(NetSocket socket, S32 backlog)
{
if(!::listen(socket, backlog))
return NoError;
return getLastError();
}
NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress)
{
sockaddr_in socketAddress;
U32 addrLen = sizeof(socketAddress);
int retVal = ::accept(acceptSocket, (sockaddr *) &socketAddress, &addrLen);
if(retVal != InvalidSocket)
{
IPSocketToNetAddress(&socketAddress, remoteAddress);
return retVal;
}
return InvalidSocket;
}
Net::Error Net::bind(NetSocket socket, U16 port)
{
S32 error;
sockaddr_in socketAddress;
dMemset((char *)&socketAddress, 0, sizeof(socketAddress));
socketAddress.sin_family = AF_INET;
// It's entirely possible that there are two NIC cards.
// We let the user specify which one the server runs on.
// thanks to [TPG]P1aGu3 for the name
const char* serverIP = Con::getVariable( "Pref::Net::BindAddress" );
// serverIP is guaranteed to be non-0.
AssertFatal( serverIP, "serverIP is NULL!" );
if( serverIP[0] != '\0' ) {
// we're not empty
socketAddress.sin_addr.s_addr = inet_addr( serverIP );
if( socketAddress.sin_addr.s_addr != INADDR_NONE ) {
Con::printf( "Binding server port to %s", serverIP );
} else {
Con::warnf( ConsoleLogEntry::General,
"inet_addr() failed for %s while binding!",
serverIP );
socketAddress.sin_addr.s_addr = INADDR_ANY;
}
} else {
Con::printf( "Binding server port to default IP" );
socketAddress.sin_addr.s_addr = INADDR_ANY;
}
socketAddress.sin_port = htons(port);
error = ::bind(socket, (sockaddr *) &socketAddress, sizeof(socketAddress));
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize)
{
S32 error;
error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize));
if(!error)
error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize));
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::setBroadcast(NetSocket socket, bool broadcast)
{
S32 bc = broadcast;
S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc));
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::setBlocking(NetSocket socket, bool blockingIO)
{
int notblock = !blockingIO;
S32 error = ioctl(socket, FIONBIO, &notblock);
if(!error)
return NoError;
return getLastError();
}
Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize)
{
errno = 0;
S32 bytesWritten = ::send(socket, (const char*)buffer, bufferSize, 0);
if(bytesWritten == -1)
Con::errorf("Could not write to socket. Error: %s",strerror(errno));
return getLastError();
}
Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead)
{
*bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0);
if(*bytesRead == -1)
return getLastError();
return NoError;
}
bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2)
{
if((a1->type != a2->type) ||
(*((U32 *)a1->netNum) != *((U32 *)a2->netNum)) ||
(a1->port != a2->port))
return false;
if(a1->type == NetAddress::IPAddress)
return true;
for(S32 i = 0; i < 6; i++)
if(a1->nodeNum[i] != a2->nodeNum[i])
return false;
return true;
}
bool Net::stringToAddress(const char *addressString, NetAddress *address)
{
// assume IP if it doesn't have ipx: at the front.
if(!dStrnicmp(addressString, "ip:", 3))
addressString += 3; // eat off the ip:
sockaddr_in ipAddr;
char remoteAddr[256];
if(strlen(addressString) > 255)
return false;
dStrcpy(remoteAddr, addressString);
char *portString = dStrchr(remoteAddr, ':');
if(portString)
*portString++ = '\0';
struct hostent *hp;
if(!dStricmp(remoteAddr, "broadcast"))
ipAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
else
{
if (inet_aton(remoteAddr,&ipAddr.sin_addr) == 0) // error
{
if((hp = gethostbyname(remoteAddr)) == 0)
return false;
else
memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(in_addr));
}
}
if(portString)
ipAddr.sin_port = htons(dAtoi(portString));
else
ipAddr.sin_port = htons(defaultPort);
ipAddr.sin_family = AF_INET;
IPSocketToNetAddress(&ipAddr, address);
return true;
}
void Net::addressToString(const NetAddress *address, char addressString[256])
{
if(address->type == NetAddress::IPAddress)
{
sockaddr_in ipAddr;
netToIPSocketAddress(address, &ipAddr);
if(ipAddr.sin_addr.s_addr == htonl(INADDR_BROADCAST))
dSprintf(addressString, 256, "IP:Broadcast:%d", ntohs(ipAddr.sin_port));
else
dSprintf(addressString, 256, "IP:%s:%d", inet_ntoa(ipAddr.sin_addr),
ntohs(ipAddr.sin_port));
// dSprintf(addressString, 256, "IP:%d:%d", ipAddr.sin_addr.s_addr,
// ntohs(ipAddr.sin_port));
}
else
{
return;
dSprintf(addressString, 256, "IPX:%.2X%.2X%.2X%.2X:%.2X%.2X%.2X%.2X%.2X%.2X:%d",
address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3],
address->nodeNum[0], address->nodeNum[1], address->nodeNum[2], address->nodeNum[3], address->nodeNum[4], address->nodeNum[5],
address->port);
}
}
Net::Error getLastError()
{
if (errno == EAGAIN)
return Net::WouldBlock;
if (errno == 0)
return Net::NoError;
return Net::UnknownError;
}

View File

@ -0,0 +1,819 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//
// Portions taken from OpenGL Full Screen.c sample from Apple Computer, Inc.
// (that's where many of the lead helper functions originated from, but code
// has been significantly changed & revised.)
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platformMacCarb/platformGL.h"
#include "platformMacCarb/maccarbOGLVideo.h"
#include "console/console.h"
#include "math/mPoint.h"
#include "platform/event.h"
#include "platform/gameInterface.h"
#include "console/consoleInternal.h"
#include "console/ast.h"
#include "core/fileStream.h"
#include "platformMacCarb/macCarbUtil.h"
#include "platformMacCarb/macCarbEvents.h"
#include "dgl/dgl.h"
// TODO: Card Profiling code isn't doing anything.
AGLContext agl_ctx;
//-----------------------------------------------------------------------------------------
// prototypes and globals -- !!!!!!TBD - globals should mostly go away, into platState.
//-----------------------------------------------------------------------------------------
GLenum dumpAGLDebugStr (void);
GLenum dumpGLDebugStr (void);
void dumpPixelFormatList(AGLPixelFormat fmt);
#pragma mark -
//-----------------------------------------------------------------------------------------
// if error dump agl errors to debugger string, return error
//-----------------------------------------------------------------------------------------
GLenum dumpAGLDebugStr (void)
{
GLenum err = aglGetError();
if (err != AGL_NO_ERROR)
Con::errorf ((char *)aglErrorString(err));
return err;
}
//-----------------------------------------------------------------------------------------
// if error dump gl errors to debugger string, return error
//-----------------------------------------------------------------------------------------
GLenum dumpGLDebugStr (void)
{
GLenum err = glGetError();
if (GL_NO_ERROR != err)
Con::errorf ((char *)gluErrorString(err));
return err;
}
#pragma mark -
//-----------------------------------------------------------------------------------------
// Creates a dummy AGL context, so that naughty objects that call OpenGL before the window
// exists will not crash the game.
// If for some reason we fail to get a default contet, assert -- something's very wrong.
//-----------------------------------------------------------------------------------------
void initDummyAgl(void)
{
short i = 0;
GLint attrib[64];
AGLPixelFormat fmt;
AGLContext ctx;
// clear the existing agl context
if(platState.ctx != NULL)
aglDestroyContext(platState.ctx);
platState.ctx = NULL;
agl_ctx = NULL;
// set up an attribute array for the pixel format.
attrib [i++] = AGL_RGBA; // red green blue and alpha
attrib [i++] = AGL_DOUBLEBUFFER; // double buffered
attrib [i++] = AGL_NONE; // terminate the list.
// choose a pixel format that works for any current display device, and matches
// the attributes above as closely as possible.
fmt = aglChoosePixelFormat(NULL, 0, attrib);
dumpAGLDebugStr ();
AssertFatal(fmt, "Could not find a valid default pixel format in initDummyAgl()");
dumpPixelFormatList(fmt);
// create an agl context. NULL = don't share resouces with any other context.
ctx = aglCreateContext (fmt, NULL);
dumpAGLDebugStr ();
AssertFatal(ctx, "Could not create a default agl context in initDummyAgl()");
// make gl calls go to our dummy context
if (!aglSetCurrentContext (ctx))
{
dumpAGLDebugStr ();
AssertFatal(false,"Could not set a default agl context as the current context.");
}
// pixel format is no longer needed once the context has been created
aglDestroyPixelFormat(fmt);
platState.ctx = ctx;
agl_ctx = ctx; // maintain up aglMacro.h context
}
GDHandle allDevs[32];
U32 nAllDevs;
AGLPixelFormat findValidPixelFormat(bool fullscreen, U32 bpp, U32 samples, bool recovery = true)
{
AssertWarn(bpp==16 || bpp==32 || bpp==0, "An unusual bit depth was requested in findValidPixelFormat(). clamping to 16|32");
if(bpp)
bpp = bpp > 16 ? 32 : 16;
AssertWarn(samples <= 6, "An unusual multisample depth was requested in findValidPixelFormat(). clamping to 0...6");
samples = samples > 6 ? 6 : samples;
// create an agl pixel format
int i = 0;
GLint attr[64];
AGLPixelFormat fmt;
// basic hardware accelerated format options
attr[i++] = AGL_RGBA; // red green blue and alpha format
attr[i++] = AGL_DOUBLEBUFFER; // double buffered format
attr[i++] = AGL_ACCELERATED; // ask for hardware acceleration
attr[i++] = AGL_NO_RECOVERY; // prohibit use of a software rendering fallback
// request fullscreen capable format if needed
if(fullscreen)
attr[i++] = AGL_FULLSCREEN;
if(bpp != 0)
{
// native pixel formats are argb 1555 & argb 8888.
U32 colorbits = 0;
U32 alphabits = 0;
if(bpp == 16)
{
colorbits = 5; // ARGB 1555
alphabits = 1;
}
else if(bpp == 32)
colorbits = alphabits = 8; // ARGB 8888
attr[i++] = AGL_DEPTH_SIZE;
attr[i++] = bpp;
attr[i++] = AGL_PIXEL_SIZE;
attr[i++] = bpp;
attr[i++] = AGL_RED_SIZE;
attr[i++] = colorbits;
attr[i++] = AGL_GREEN_SIZE;
attr[i++] = colorbits;
attr[i++] = AGL_BLUE_SIZE;
attr[i++] = colorbits;
attr[i++] = AGL_ALPHA_SIZE;
attr[i++] = alphabits;
}
if(samples != 0)
{
attr[i++] = AGL_SAMPLE_BUFFERS_ARB; // number of multisample buffers
attr[i++] = 1; // currently only 1 is supported
attr[i++] = AGL_SAMPLES_ARB; // number of samples per pixel
attr[i++] = samples + samples % 2; // must be 2, 4, 6 ...
}
attr[i++] = AGL_NONE; // null-terminate the list
fmt = aglChoosePixelFormat(&platState.hDisplay, 1, attr);
dumpAGLDebugStr();
if(!fmt && recovery)
{
// recovery.
// first try ignoring samples. then try ignoring the bit depth.
// we'll try the following order:
// specified bit depth, specified multisample depth
// specified bit depth, lower/no multisample
// any bit depth, specified multisample depth
// any bit depth, lower/no multisample
// downgrade multisample, trying for a valid format
for(int fewerSamples = samples - 1; !fmt && fewerSamples >=0; fewerSamples--)
{
Con::errorf("error selecting pixel format, trying again with %s, bpp=%i, multisample level=%i",
fullscreen ? "fullscreen" : "windowed", bpp, fewerSamples);
fmt = findValidPixelFormat(fullscreen, bpp, fewerSamples, false);
}
// allow any bit depth, send in requested multisample level, and ALLOW RECOVERY.
// if this attempt fails, we'll hit the above 'downgrade multisample' block in the recursion.
if(!fmt && bpp != 0)
{
Con::warnf("error selecting pixel format, trying again with %s, bpp=%i, multisample level=%i",
fullscreen ? "fullscreen" : "windowed", 0, samples);
fmt = findValidPixelFormat(fullscreen, 0, samples, true);
}
}
return fmt;
}
void dumpPixelFormat(AGLPixelFormat fmt)
{
Con::printf(" ------------------------------------------------------------");
Con::printf("Describing pixel format 0x%x", fmt);
GLint val;
#define DumpAGLPixelFormatAttr(attr) \
aglDescribePixelFormat(fmt, AGL_##attr, &val); \
dumpAGLDebugStr(); \
Con::printf(" %20s %4i", #attr ,val);
DumpAGLPixelFormatAttr(ALL_RENDERERS);
DumpAGLPixelFormatAttr(BUFFER_SIZE);
DumpAGLPixelFormatAttr(LEVEL);
DumpAGLPixelFormatAttr(RGBA);
DumpAGLPixelFormatAttr(DOUBLEBUFFER)
DumpAGLPixelFormatAttr(STEREO);
DumpAGLPixelFormatAttr(AUX_BUFFERS);
DumpAGLPixelFormatAttr(GREEN_SIZE);
DumpAGLPixelFormatAttr(RED_SIZE);
DumpAGLPixelFormatAttr(BLUE_SIZE);
DumpAGLPixelFormatAttr(ALPHA_SIZE);
DumpAGLPixelFormatAttr(DEPTH_SIZE);
DumpAGLPixelFormatAttr(STENCIL_SIZE);
DumpAGLPixelFormatAttr(ACCUM_RED_SIZE);
DumpAGLPixelFormatAttr(ACCUM_GREEN_SIZE);
DumpAGLPixelFormatAttr(ACCUM_BLUE_SIZE);
DumpAGLPixelFormatAttr(ACCUM_ALPHA_SIZE);
DumpAGLPixelFormatAttr(PIXEL_SIZE);
DumpAGLPixelFormatAttr(MINIMUM_POLICY);
DumpAGLPixelFormatAttr(MAXIMUM_POLICY);
DumpAGLPixelFormatAttr(OFFSCREEN);
DumpAGLPixelFormatAttr(FULLSCREEN);
DumpAGLPixelFormatAttr(SAMPLE_BUFFERS_ARB);
DumpAGLPixelFormatAttr(SAMPLES_ARB);
DumpAGLPixelFormatAttr(AUX_DEPTH_STENCIL);
DumpAGLPixelFormatAttr(COLOR_FLOAT);
DumpAGLPixelFormatAttr(MULTISAMPLE);
DumpAGLPixelFormatAttr(SUPERSAMPLE);
DumpAGLPixelFormatAttr(SAMPLE_ALPHA);
#undef DumpAGLPixelFormatAttr
Con::printf(" ------------------------------------------------------------");
}
void dumpPixelFormatList(AGLPixelFormat fmt)
{
Con::printf("Dumping list of pixel formats:");
while(fmt!=NULL)
{
dumpPixelFormat(fmt);
fmt = aglNextPixelFormat(fmt);
}
}
#pragma mark -
//------------------------------------------------------------------------------
OpenGLDevice::OpenGLDevice()
{
// Set the device name:
mDeviceName = "OpenGL";
// macs games are not generally full screen only
mFullScreenOnly = false;
}
//------------------------------------------------------------------------------
void OpenGLDevice::initDevice()
{
// pick a monitor to run on
enumMonitors();
// choose a monitor at save it in the plat state
platState.hDisplay = chooseMonitor();
platState.cgDisplay = MacCarbGetCGDisplayFromQDDisplay(platState.hDisplay);
// figure out & cache what Resolution's this card-monitor combo can support
enumDisplayModes(platState.cgDisplay);
}
//------------------------------------------------------------------------------
// Fill Vector<Resolution> mResoultionList with list of supported modes
//------------------------------------------------------------------------------
bool OpenGLDevice::enumDisplayModes(CGDirectDisplayID display)
{
mResolutionList.clear();
// get the display, and the list of all available modes.
CFArrayRef modeArray = CGDisplayAvailableModes(display);
if(!modeArray)
{
// we're probably in headless mode. still, best not to leave the list emtpy.
Resolution headless( 640, 480, 32 );
mResolutionList.push_back( headless );
return false;
}
int len = CFArrayGetCount(modeArray);
for(int i=0; i < len; i++)
{
// get this mode.
CFNumberRef num;
int width, height, bpp;
CFDictionaryRef mode = (CFDictionaryRef) CFArrayGetValueAtIndex(modeArray,i);
// get width
num = CFDictionaryGetValue( mode, kCGDisplayWidth );
CFNumberGetValue(num, kCFNumberLongType,&width);
// get height
num = CFDictionaryGetValue( mode, kCGDisplayHeight );
CFNumberGetValue(num, kCFNumberLongType,&height);
// get bpp
num = CFDictionaryGetValue( mode, kCGDisplayBitsPerPixel );
CFNumberGetValue(num, kCFNumberLongType,&bpp);
// add to the list
if( bpp != 8 )
{
Resolution newRes( width, height, bpp );
mResolutionList.push_back( newRes );
}
}
// // fill it with some standard sizes for a machine, just to have them available.
// Point sizes[4] = {{640,480},{800,600},{1024,768},{1280,1024}};
// for(int i=0; i<4; i++)
// {
// // Point is { short v, short h }, so we have to reverse the h and v here, sorry for any confusion.
// Resolution newRes16( sizes[i].v, sizes[i].h, 16 );
// Resolution newRes32( sizes[i].v, sizes[i].h, 32 );
// mResolutionList.push_back( newRes16 );
// mResolutionList.push_back( newRes32 );
// }
return true;
}
//------------------------------------------------------------------------------
// Fill mMonitorList with list of supported modes
// Guaranteed to include at least the main device.
//------------------------------------------------------------------------------
bool OpenGLDevice::enumMonitors()
{
// DMGetFirstScreenDevice() et al are deprecated as of 10.4, but we need them
// for AGL, which operates on GDHandle's for displays.
// As far as I know, you can get a CGDirectDisplayID from a GDHandle,
// but not the other way around.
mMonitorList.clear();
GDHandle dev = DMGetFirstScreenDevice( true );
nAllDevs = 0;
while( dev )
{
Con::printf( " active displays = 0x%x CGDisplayID = 0x%x", dev, MacCarbGetCGDisplayFromQDDisplay(dev));
mMonitorList.push_back(dev);
allDevs[nAllDevs++] = dev;
dev = DMGetNextScreenDevice( dev, true);
}
return true;
}
//------------------------------------------------------------------------------
// Chooses a monitor based on $pref, on the results of tors(), & on the
// current window's screen.
//------------------------------------------------------------------------------
GDHandle OpenGLDevice::chooseMonitor()
{
// TODO: choose monitor based on which one contains most of the window.
// NOTE: do not call cleanup before calling choose, or we won't have a window to consider.
AssertFatal(!mMonitorList.empty(), "Cannot choose a monitor if the list is empty!");
U32 monNum = Con::getIntVariable("$pref::Video::monitorNum", 0);
if(monNum >= mMonitorList.size())
{
Con::errorf("invalid monitor number %i", monNum);
monNum = 0;
Con::setIntVariable("$pref::Video::monitorNum", 0);
}
Con::printf("using display 0x%x", mMonitorList[monNum]);
return mMonitorList[monNum];
}
//------------------------------------------------------------------------------
// Activate
// this is called once, as a result of createCanvas() in scripts.
// dumps OpenGL driver info for the current screen
// creates an initial window via setScreenMode
bool OpenGLDevice::activate( U32 width, U32 height, U32 bpp, bool fullScreen )
{
Con::printf( " OpenGLDevice activating..." );
// Never unload a code module. This makes destroying & recreating contexts faster.
aglConfigure(AGL_RETAIN_RENDERERS, GL_TRUE);
// gets opengl rendering capabilities of the screen pointed to by platState.hDisplay
// sets up dgl with the capabilities info, & reports opengl status.
getGLCapabilities();
// Create the window or capture fullscreen
if(!setScreenMode( width, height, bpp, fullScreen, true, false ))
return false;
// set the displayDevice pref to "OpenGL"
Con::setVariable( "$pref::Video::displayDevice", mDeviceName );
// set vertical sync now because it doesnt need setting every time we setScreenMode()
setVerticalSync( !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ));
return true;
}
//------------------------------------------------------------------------------
// returns TRUE if textures need resurrecting in future...
//------------------------------------------------------------------------------
bool OpenGLDevice::cleanupContextAndWindow()
{
bool needResurrect = false;
Con::printf( "Cleaning up the display device..." );
// Delete the rendering context and it's specific data.
if (platState.ctx)
{
// The OpenGL texture handles are specific to each context.
// We'll need to get new ones for a new context, so kill the texture manager to clear 'em.
if (!Video::smNeedResurrect)
{
Con::printf( "Killing the texture manager..." );
Game->textureKill();
needResurrect = true;
}
// make the agl context not-current, which stops openGL calls from going anywhere.
Con::printf( "Clearing the current AGL context..." );
aglSetCurrentContext(NULL);
#if defined(USE_AGL_MACRO)
agl_ctx = NULL; // maintain aglMacro.h context
#endif
// detatch the agl context from it's window
Con::printf( "Clearing the current drawable..." );
aglSetDrawable(platState.ctx, NULL);
// delete the context
Con::printf( "Deleting the rendering context..." );
aglDestroyContext(platState.ctx);
// clear our handle to the context
platState.ctx = NULL;
}
// delete the app window if it exists
if ( platState.appWindow )
{
Con::printf( "Destroying the window..." );
MacCarbFadeAndReleaseWindow(platState.appWindow);
platState.appWindow = NULL;
}
// clear the Resolution state, so setScreenMode() will know not to early-out.
smCurrentRes = Resolution(0,0,0);
return(needResurrect);
}
//------------------------------------------------------------------------------
void OpenGLDevice::shutdown()
{
Con::printf( "Shutting down the OpenGL display device..." );
// clean up the context, the window, and kill the texture manager
cleanupContextAndWindow();
}
//------------------------------------------------------------------------------
// This is the real workhorse function of the DisplayDevice...
//
bool OpenGLDevice::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt, bool repaint )
{
Con::printf(" set screen mode %i x %i x %i, %s, %s, %s",width, height, bpp,
fullScreen ? "fullscreen" : "windowed",
forceIt ? "force it" : "dont force it",
repaint ? "repaint" : "dont repaint"
);
// validation, early outs --------------------------------------------------
// sanity check. some scripts are liable to pass in bad values.
if(!bpp)
bpp = platState.desktopBitsPixel;
Resolution newRes = Resolution(width, height, bpp);
// if no values changing and we're not forcing a change, kick out. prevents thrashing.
if(!forceIt && smIsFullScreen == fullScreen && smCurrentRes == newRes)
return(true);
// we have a new context, this is now safe to do:
// delete any contexts or windows that exist, and kill the texture manager.
bool needResurrect = cleanupContextAndWindow();
Con::printf( ">> Attempting to change display settings to %s %dx%dx%d...",
fullScreen?"fullscreen":"windowed", newRes.w, newRes.h, newRes.bpp );
// monitor selection -------------------------------------------------------
// set our preferred monitor. default is the screen that has the menu bar.
GDHandle prevDisplay = platState.hDisplay;
platState.hDisplay = chooseMonitor();
platState.cgDisplay = MacCarbGetCGDisplayFromQDDisplay(platState.hDisplay);
AssertFatal(platState.hDisplay,"We chose a null monitor? Panic!");
// if we're changing screens, we must know what the new one is capable of
if(prevDisplay != platState.hDisplay)
{
enumDisplayModes(platState.cgDisplay);
getGLCapabilities();
}
// create an agl rendering context ------------------------------------------
AGLPixelFormat fmt = NULL;
AGLContext ctx = NULL;
// select pixel format. fall back to more generic options until we get something.
fmt = findValidPixelFormat(fullScreen, bpp, 0);
AssertFatal(fmt, "We utterly failed to choose a valid AGL pixel format.");
// print out the pixel format list we got.
//dumpPixelFormatList(fmt);
// create the agl rendering context
ctx = aglCreateContext(fmt, NULL);
dumpAGLDebugStr();
AssertISV( ctx, "We could not create a valid AGL rendering context.");
if(!ctx)
return false;
// format is not needed once we have a context.
aglDestroyPixelFormat(fmt);
if(fullScreen && platState.captureDisplay)
{
// capture main display & go to full screen mode
// TODO: allow frequency selection?
aglSetFullScreen(ctx, newRes.w, newRes.h, 0, 0);
dumpAGLDebugStr();
Con::printf("set AGL fullscreen");
MacCarbShowMenuBar(false);
}
else
{
// create a window, get it's drawable, and attach our context to the window
bool isFullscreenWindow = (fullScreen && !platState.captureDisplay);
platState.appWindow = MacCarbCreateOpenGLWindow( platState.hDisplay, newRes.w, newRes.h, isFullscreenWindow );
if(!platState.appWindow)
{
Con::errorf("OpenGLDevice::setScreenMode - Failed to create a new window!");
return false;
}
CGrafPtr drawable = GetWindowPort(platState.appWindow);
aglSetDrawable(ctx, drawable);
dumpAGLDebugStr();
Con::printf("Set up AGL windowed support");
}
// send opengl commands to this context.
aglSetCurrentContext(ctx);
#if defined(USE_AGL_MACRO)
agl_ctx = ctx; // maintain aglMacro.h context
#endif
// save the context
platState.ctx = ctx;
// clear out garbage from the gl window.
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT );
Con::printf("Cleared gl buffers");
// set opengl options & other options ---------------------------------------
// ensure data is packed tightly in memory. this defaults to 4.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// TODO: set gl arb multisample enable & hint
dglSetFSAASamples(gFSAASamples);
// set vertical sync
setVerticalSync(!Con::getBoolVariable( "$pref::Video::disableVerticalSync" ));
// set torque variables ----------------------------------------------------
// save window size for dgl
Platform::setWindowSize( newRes.w, newRes.h );
// update smIsFullScreen and pref
smIsFullScreen = fullScreen;
Con::setBoolVariable( "$pref::Video::fullScreen", smIsFullScreen );
// save resolution
smCurrentRes = newRes;
// save resolution to prefs
char buf[32];
if(fullScreen)
{
dSprintf( buf, sizeof(buf), "%d %d %d", newRes.w, newRes.h, newRes.bpp);
Con::setVariable("$pref::Video::resolution", buf);
}
else
{
dSprintf( buf, sizeof(buf), "%d %d", newRes.w, newRes.h);
Con::setVariable("$pref::Video::windowedRes", buf);
}
// fade the window into existance, asynchronously
if(platState.appWindow)
MacCarbFadeInWindow(platState.appWindow);
// begin rendering again ----------------------------------------------------
if( needResurrect )
{
// Reload the textures gl names
Con::printf( "Resurrecting the texture manager..." );
Game->textureResurrect();
}
// reattach the event handlers to the new window.
MacCarbRemoveCarbonEventHandlers();
MacCarbInstallCarbonEventHandlers();
if( repaint )
Con::evaluate( "resetCanvas();" );
return true;
}
//------------------------------------------------------------------------------
void OpenGLDevice::swapBuffers()
{
if (platState.ctx)
aglSwapBuffers(platState.ctx);
#if defined(TORQUE_DEBUG)
if(gOutlineEnabled)
glClear(GL_COLOR_BUFFER_BIT);
#endif
}
//------------------------------------------------------------------------------
const char* OpenGLDevice::getDriverInfo()
{
// Prepare some driver info for the console:
const char* vendorString = (const char*) glGetString( GL_VENDOR );
const char* rendererString = (const char*) glGetString( GL_RENDERER );
const char* versionString = (const char*) glGetString( GL_VERSION );
const char* extensionsString = (const char*) glGetString( GL_EXTENSIONS );
U32 bufferLen = ( vendorString ? dStrlen( vendorString ) : 0 )
+ ( rendererString ? dStrlen( rendererString ) : 0 )
+ ( versionString ? dStrlen( versionString ) : 0 )
+ ( extensionsString ? dStrlen( extensionsString ) : 0 )
+ 4;
char* returnString = Con::getReturnBuffer( bufferLen );
dSprintf( returnString, bufferLen, "%s\t%s\t%s\t%s",
( vendorString ? vendorString : "" ),
( rendererString ? rendererString : "" ),
( versionString ? versionString : "" ),
( extensionsString ? extensionsString : "" ) );
return( returnString );
}
typedef struct MacCarbGamma
{
F32 r, g, b;
F32 scale;
};
static MacCarbGamma _MacGamma;
//------------------------------------------------------------------------------
bool OpenGLDevice::getGammaCorrection(F32 &g)
{
// rgb gamma exponents
CGGammaValue red,green,blue;
// rgb min & max gamma. we'll ignore these for the present.
CGGammaValue rm, rx, gm, gx, bm, bx;
// grab the gamma values
CGDirectDisplayID display = platState.cgDisplay;
CGGetDisplayTransferByFormula(display, &rm, &rx, &red, &gm, &gx, &green, &bm, &bx, &blue);
// save the original gamma vals, and the current scale.
static bool once = true;
if(once)
{
once = false;
_MacGamma.r = red;
_MacGamma.g = green;
_MacGamma.b = blue;
_MacGamma.scale = 1.0;
}
g = _MacGamma.scale;
return true;
}
//------------------------------------------------------------------------------
bool OpenGLDevice::setGammaCorrection(F32 g)
{
// revert to default colorsync settings if g approaches 1.0
F32 epsilon = 0.01f;
if( mFabs(g - 1.0) <= epsilon )
{
CGDisplayRestoreColorSyncSettings();
return false;
}
// rgb gamma exponents
CGGammaValue red,green,blue;
// rgb min & max gamma. we'll ignore these for the present.
CGGammaValue rm, rx, gm, gx, bm, bx;
CGGetDisplayTransferByFormula(platState.cgDisplay, &rm, &rx, &red, &gm, &gx, &green, &bm, &bx, &blue);
// scale the original gamma values.
red = _MacGamma.r * g;
green = _MacGamma.g * g;
blue = _MacGamma.b * g;
_MacGamma.scale = g;
// set the gamma.
CGSetDisplayTransferByFormula(platState.cgDisplay, rm, rx, red, gm, gx, green, bm, bx, blue);
return true;
}
//------------------------------------------------------------------------------
bool OpenGLDevice::setVerticalSync( bool on )
{
if (!platState.ctx)
return false;
bool ret = aglSetInteger(platState.ctx, AGL_SWAP_INTERVAL, (GLint*)&on);
return ret;
}
//------------------------------------------------------------------------------
DisplayDevice* OpenGLDevice::create()
{
// set up a dummy default agl context.
// this will be replaced later with the window's context,
// but we need agl_ctx to be valid at all times,
// since some things try to make gl calls before the device is activated.
initDummyAgl();
// create the DisplayDevice
OpenGLDevice* newOGLDevice = new OpenGLDevice();
// gather monitor & resolution info
// delegated out to initDevice() in case the display config changes.
newOGLDevice->initDevice();
return newOGLDevice;
}
#pragma mark -
Resolution Video::getDesktopResolution()
{
Resolution res;
Rect r = (**platState.hDisplay).gdRect;
res.w = r.right - r.left;
res.h = r.bottom - r.top;
res.bpp = (**(**platState.hDisplay).gdPMap).pixelSize;
platState.desktopWidth = res.w;
platState.desktopHeight = res.h;
platState.desktopBitsPixel = res.bpp;
return res;
}
#pragma mark -
ConsoleFunction( dumpDisplayIDs, void, 1,1, "")
{
Con::errorf("=-== Dumping display ids =-==");
GDHandle dev = GetMainDevice();
Con::printf("main display GetMainDevice = 0x%x CGDisplayID = 0x%x", dev, MacCarbGetCGDisplayFromQDDisplay(dev));
dev = DMGetFirstScreenDevice( true );
Con::printf( "first active display = 0x%x CGDisplayID = 0x%x", dev, MacCarbGetCGDisplayFromQDDisplay(dev));
while( ( dev = DMGetNextScreenDevice( dev, true)) != NULL)
{
Con::printf( " active displays = 0x%x CGDisplayID = 0x%x", dev, MacCarbGetCGDisplayFromQDDisplay(dev));
}
CGDirectDisplayID mainid = CGMainDisplayID();
Con::printf("main display CGMainDisplayID = 0x%x", mainid);
CGDirectDisplayID lastDisplay, displayArray[64] ;
CGDisplayCount numDisplays ;
CGGetActiveDisplayList( 64, displayArray, &numDisplays );
for(int i=0; i< numDisplays; i++)
{
mainid = displayArray[i];
Con::printf(" CG active display list CGMainDisplayID = 0x%x", mainid);
}
}

View File

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _MACCARBOGLVIDEO_H_
#define _MACCARBOGLVIDEO_H_
#include "platform/platformVideo.h"
class OpenGLDevice : public DisplayDevice
{
private:
/// Handles to all the devices ( monitors ) attached to the system.
Vector<GDHandle> mMonitorList;
/// Gamma value
F32 mGamma;
/// Fills mResolutionList with a list of valid resolutions for a particular screen
bool enumDisplayModes(CGDirectDisplayID hDevice);
/// Fills mMonitorList with all available monitors
bool enumMonitors();
/// Chooses a monitor based on $pref::the results of enumMontors()
GDHandle chooseMonitor();
/// Cleans up the opengl context, and destroys the rendering window
bool cleanupContextAndWindow();
public:
OpenGLDevice();
static DisplayDevice* create();
/// The following are inherited from DisplayDevice
void initDevice();
bool activate( U32 width, U32 height, U32 bpp, bool fullScreen );
void shutdown();
bool setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt = false, bool repaint = true );
void swapBuffers();
const char* getDriverInfo();
bool getGammaCorrection(F32 &g);
bool setGammaCorrection(F32 g);
bool setVerticalSync( bool on );
};
#endif // _MACCARBOGLVIDEO_H_

View File

@ -0,0 +1,220 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// The debug render modes are only built in a debug build,
// partially because a release build should not need them
// and partially because using aglMacro.h or cglMacro.h would prevent us from
// playing this little function-pointer-hijacking trick
#if defined(TORQUE_DEBUG)
#define NO_REDEFINE_GL_FUNCS
#include "platformMacCarb/platformGL.h"
#include "console/console.h"
bool gOutlineEnabled = false;
void (* glDrawElementsProcPtr) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) = glDrawElements;
void (* glDrawArraysProcPtr) (GLenum mode, GLint first, GLsizei count) = glDrawArrays;
void (* glNormDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) = glDrawElements;
void (* glNormDrawArrays) (GLenum mode, GLint first, GLsizei count) = glDrawArrays;
/// A utility for the outline drawing routines
static U32 getIndex(GLenum type, const void *indices, U32 i)
{
if(type == GL_UNSIGNED_BYTE)
return ((U8 *) indices)[i];
else if(type == GL_UNSIGNED_SHORT)
return ((U16 *) indices)[i];
else
return ((U32 *) indices)[i];
}
void glOutlineDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
{
if(mode == GL_POLYGON)
mode = GL_LINE_LOOP;
if(mode == GL_POINTS || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP || mode == GL_LINES)
glDrawElements( mode, count, type, indices );
else
{
glBegin(GL_LINES);
if(mode == GL_TRIANGLE_STRIP)
{
U32 i;
for(i = 0; i < count - 1; i++)
{
glArrayElement(getIndex(type, indices, i));
glArrayElement(getIndex(type, indices, i + 1));
if(i + 2 != count)
{
glArrayElement(getIndex(type, indices, i));
glArrayElement(getIndex(type, indices, i + 2));
}
}
}
else if(mode == GL_TRIANGLE_FAN)
{
for(U32 i = 1; i < count; i ++)
{
glArrayElement(getIndex(type, indices, 0));
glArrayElement(getIndex(type, indices, i));
if(i != count - 1)
{
glArrayElement(getIndex(type, indices, i));
glArrayElement(getIndex(type, indices, i + 1));
}
}
}
else if(mode == GL_TRIANGLES)
{
for(U32 i = 3; i <= count; i += 3)
{
glArrayElement(getIndex(type, indices, i - 3));
glArrayElement(getIndex(type, indices, i - 2));
glArrayElement(getIndex(type, indices, i - 2));
glArrayElement(getIndex(type, indices, i - 1));
glArrayElement(getIndex(type, indices, i - 3));
glArrayElement(getIndex(type, indices, i - 1));
}
}
else if(mode == GL_QUADS)
{
for(U32 i = 4; i <= count; i += 4)
{
glArrayElement(getIndex(type, indices, i - 4));
glArrayElement(getIndex(type, indices, i - 3));
glArrayElement(getIndex(type, indices, i - 3));
glArrayElement(getIndex(type, indices, i - 2));
glArrayElement(getIndex(type, indices, i - 2));
glArrayElement(getIndex(type, indices, i - 1));
glArrayElement(getIndex(type, indices, i - 4));
glArrayElement(getIndex(type, indices, i - 1));
}
}
else if(mode == GL_QUAD_STRIP)
{
if(count < 4)
return;
glArrayElement(getIndex(type, indices, 0));
glArrayElement(getIndex(type, indices, 1));
for(U32 i = 4; i <= count; i += 2)
{
glArrayElement(getIndex(type, indices, i - 4));
glArrayElement(getIndex(type, indices, i - 2));
glArrayElement(getIndex(type, indices, i - 3));
glArrayElement(getIndex(type, indices, i - 1));
glArrayElement(getIndex(type, indices, i - 2));
glArrayElement(getIndex(type, indices, i - 1));
}
}
glEnd();
}
}
void glOutlineDrawArrays(GLenum mode, GLint first, GLsizei count)
{
if(mode == GL_POLYGON)
mode = GL_LINE_LOOP;
if(mode == GL_POINTS || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP || mode == GL_LINES)
glDrawArrays( mode, first, count );
else
{
glBegin(GL_LINES);
if(mode == GL_TRIANGLE_STRIP)
{
U32 i;
for(i = 0; i < count - 1; i++)
{
glArrayElement(first + i);
glArrayElement(first + i + 1);
if(i + 2 != count)
{
glArrayElement(first + i);
glArrayElement(first + i + 2);
}
}
}
else if(mode == GL_TRIANGLE_FAN)
{
for(U32 i = 1; i < count; i ++)
{
glArrayElement(first);
glArrayElement(first + i);
if(i != count - 1)
{
glArrayElement(first + i);
glArrayElement(first + i + 1);
}
}
}
else if(mode == GL_TRIANGLES)
{
for(U32 i = 3; i <= count; i += 3)
{
glArrayElement(first + i - 3);
glArrayElement(first + i - 2);
glArrayElement(first + i - 2);
glArrayElement(first + i - 1);
glArrayElement(first + i - 3);
glArrayElement(first + i - 1);
}
}
else if(mode == GL_QUADS)
{
for(U32 i = 4; i <= count; i += 4)
{
glArrayElement(first + i - 4);
glArrayElement(first + i - 3);
glArrayElement(first + i - 3);
glArrayElement(first + i - 2);
glArrayElement(first + i - 2);
glArrayElement(first + i - 1);
glArrayElement(first + i - 4);
glArrayElement(first + i - 1);
}
}
else if(mode == GL_QUAD_STRIP)
{
if(count < 4)
return;
glArrayElement(first + 0);
glArrayElement(first + 1);
for(U32 i = 4; i <= count; i += 2)
{
glArrayElement(first + i - 4);
glArrayElement(first + i - 2);
glArrayElement(first + i - 3);
glArrayElement(first + i - 1);
glArrayElement(first + i - 2);
glArrayElement(first + i - 1);
}
}
glEnd();
}
}
ConsoleFunction(GLEnableOutline,void,2,2,"GLEnableOutline( true | false ) - sets whether to render wireframe")
{
gOutlineEnabled = dAtob(argv[1]);
if(gOutlineEnabled)
{
glDrawElementsProcPtr = glOutlineDrawElements;
glDrawArraysProcPtr = glOutlineDrawArrays;
Con::printf("swapped to outline mode funcs");
} else {
glDrawElementsProcPtr = glDrawElements;
glDrawArraysProcPtr = glDrawArrays;
Con::printf("swapped to normal funcs");
}
}
#endif

View File

@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef __MACCARBOUTLINE__
#define __MACCARBOUTLINE__
#if defined(TORQUE_DEBUG)
#ifndef __GL_OUTLINE_FUNCS__
#define __GL_OUTLINE_FUNCS__
extern bool gOutlineEnabled;
extern void (* glDrawElementsProcPtr) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
extern void (* glDrawArraysProcPtr) (GLenum mode, GLint first, GLsizei count);
void glOutlineDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
void glOutlineDrawArrays(GLenum mode, GLint first, GLsizei count);
extern void (* glNormDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
extern void (* glNormDrawArrays) (GLenum mode, GLint first, GLsizei count);
#ifndef NO_REDEFINE_GL_FUNCS
#define glDrawElements glDrawElementsProcPtr
#define glDrawArrays glDrawArraysProcPtr
#else
#warning glDrawElements and glDrawArrays not redefined
#endif // NO_REDEFINE_GL_FUNCS
#endif // __GL_OUTLINE_FUNCS__
#endif // TORQUE_DEBUG
#endif // __MACCARBOUTLINE__

View File

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/event.h"
#include "platform/gameInterface.h"
#include "platformMacCarb/macCarbEvents.h"
void Platform::postQuitMessage(const U32 in_quitVal)
{
// guard against multiple quit messages. Quit event flow gets complex.
if(platState.quit)
return;
platState.quit = true;
Event quitEvent;
quitEvent.type = QuitEventType;
Game->postEvent(quitEvent);
MacCarbRemoveCarbonEventHandlers();
}
void Platform::debugBreak()
{
DebugStr("\pDEBUG_BREAK!");
}
void Platform::forceShutdown(S32 returnValue)
{
exit(returnValue);
}
void Platform::restartInstance()
{
// execl() leaves open file descriptors open, that's the main reason it's not
// used here. We want to start fresh.
if( Game->isRunning() )
{
Con::errorf("The game is still running, we cant relaunch now!");
return;
}
// get the path to the torque executable
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef execURL = CFBundleCopyExecutableURL(mainBundle);
CFStringRef execString = CFURLCopyFileSystemPath(execURL, kCFURLPOSIXPathStyle);
// append ampersand so that we can launch without blocking.
// encase in quotes so that spaces in the path are accepted.
CFMutableStringRef mut = CFStringCreateMutableCopy(NULL, 0, execString);
CFStringInsert(mut, 0, CFSTR("\""));
CFStringAppend(mut, CFSTR("\" & "));
U32 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mut), kCFStringEncodingUTF8);
char *execCString = new char[len+1];
CFStringGetCString(mut, execCString, len, kCFStringEncodingUTF8);
execCString[len] = '\0';
Con::printf("---- %s -----",execCString);
system(execCString);
}

View File

@ -0,0 +1,100 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// platformSemaphore.h does not ask for any inter process communication,
// and the posix semaphores require a file to be created on disk.
// which could create annoyances if the appication crashes...
// so we'll just roll our own semaphore here.
// note: this is not a bulletproof solution to the starving problem...
// see: The Little Book of Semapores, by Allen B. Downey, at http://greenteapress.com/semaphores/
#include <pthread.h>
#include "platform/platform.h"
#include "platform/platformSemaphore.h"
typedef struct MacCarbSemaphore
{
pthread_mutex_t mDarkroom;
// pthread_mutex_t mFoyer; // second lock, to help control starving.
pthread_cond_t mCond;
int count;
};
void * Semaphore::createSemaphore(U32 initialCount)
{
bool ok;
MacCarbSemaphore *semaphore = new MacCarbSemaphore();
ok = pthread_mutex_init(&semaphore->mDarkroom,NULL);
AssertFatal(ok == 0,"Create semaphore failed at creating mutex mDarkroom.");
// ok = pthread_mutex_init(&semaphore->mFoyer,NULL);
// AssertFatal(ok != 0,"Create semaphore failed at creating mutex mFoyer.");
ok = pthread_cond_init(&semaphore->mCond,NULL);
AssertFatal(ok == 0,"Create semaphore failed at creating condition mCond.");
semaphore->count = initialCount;
return((void*)semaphore);
}
void Semaphore::destroySemaphore(void * semaphore)
{
MacCarbSemaphore *sem = static_cast<MacCarbSemaphore*>(semaphore);
pthread_mutex_destroy(&sem->mDarkroom);
// pthread_mutex_destroy(sem->mFoyer);
pthread_cond_destroy(&sem->mCond);
delete sem;
}
bool Semaphore::acquireSemaphore(void * semaphore, bool block)
{
bool ok;
AssertFatal(semaphore, "Semaphore::acquireSemaphore: invalid semaphore");
MacCarbSemaphore *sem = static_cast<MacCarbSemaphore*>(semaphore);
ok = pthread_mutex_lock(&sem->mDarkroom);
AssertFatal(ok == 0,"Mutex Lock failed on mDarkroom in acquireSemaphore().");
if(sem->count <= 0 && !block) {
ok = pthread_mutex_unlock(&sem->mDarkroom);
AssertFatal(ok == 0,"Mutex Unlock failed on mDarkroom when not blocking in acquireSemaphore().");
return false;
}
while( sem->count <= 0 ) {
ok = pthread_cond_wait(&sem->mCond, &sem->mDarkroom); // releases mDarkroom while blocked.
AssertFatal(ok == 0,"Waiting on mCond failed in acquireSemaphore().");
}
sem->count--;
ok = pthread_mutex_unlock(&sem->mDarkroom);
AssertFatal(ok == 0,"Mutex Unlock failed on mDarkroom when leaving acquireSemaphore().");
return true;
}
void Semaphore::releaseSemaphore(void * semaphore)
{
bool ok;
AssertFatal(semaphore, "Semaphore::releaseSemaphore: invalid semaphore");
MacCarbSemaphore *sem = static_cast<MacCarbSemaphore*>(semaphore);
ok = pthread_mutex_lock(&sem->mDarkroom);
AssertFatal(ok == 0,"Mutex Lock failed on mDarkroom in releaseSemaphore().");
sem->count++;
if(sem->count > 0) {
ok = pthread_cond_signal(&sem->mCond);
AssertFatal(ok == 0,"Signaling mCond failed in releaseSemaphore().");
}
ok = pthread_mutex_unlock(&sem->mDarkroom);
AssertFatal(ok == 0,"Mutex Unlock failed on mDarkroom when leaving releaseSemaphore().");
}

View File

@ -0,0 +1,335 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/platform.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include "console/console.h"
#include "platform/profiler.h"
char *dStrdup_r(const char *src, const char *file, dsize_t line)
{
char *buffer = (char *) dMalloc_r(dStrlen(src) + 1, file, line);
dStrcpy(buffer, src);
return buffer;
}
char *dStrnew(const char *src)
{
char *buffer = new char[dStrlen(src) + 1];
dStrcpy(buffer, src);
return buffer;
}
char* dStrcat(char *dst, const char *src)
{
return strcat(dst,src);
}
char* dStrncat(char *dst, const char *src, dsize_t len)
{
return strncat(dst,src,len);
}
// concatenates a list of src's onto the end of dst
// the list of src's MUST be terminated by a NULL parameter
// dStrcatl(dst, sizeof(dst), src1, src2, NULL);
char* dStrcatl(char *dst, dsize_t dstSize, ...)
{
const char* src;
char *p = dst;
AssertFatal(dstSize > 0, "dStrcatl: destination size is set zero");
dstSize--; // leave room for string termination
// find end of dst
while (dstSize && *p++)
dstSize--;
va_list args;
va_start(args, dstSize);
// concatenate each src to end of dst
while ( (src = va_arg(args, const char*)) != NULL )
while( dstSize && *src )
{
*p++ = *src++;
dstSize--;
}
va_end(args);
// make sure the string is terminated
*p = 0;
return dst;
}
// copy a list of src's into dst
// the list of src's MUST be terminated by a NULL parameter
// dStrccpyl(dst, sizeof(dst), src1, src2, NULL);
char* dStrcpyl(char *dst, dsize_t dstSize, ...)
{
const char* src;
char *p = dst;
AssertFatal(dstSize > 0, "dStrcpyl: destination size is set zero");
dstSize--; // leave room for string termination
va_list args;
va_start(args, dstSize);
// concatenate each src to end of dst
while ( (src = va_arg(args, const char*)) != NULL )
while( dstSize && *src )
{
*p++ = *src++;
dstSize--;
}
va_end(args);
// make sure the string is terminated
*p = 0;
return dst;
}
int dStrcmp(const char *str1, const char *str2)
{
return strcmp(str1, str2);
}
int dStrcmp( const UTF16 *str1, const UTF16 *str2)
{
int ret;
const UTF16 *a, *b;
a = str1;
b = str2;
while( *a && *b && (ret = *a - *b) == 0)
a++, b++;
return ret;
}
int dStricmp(const char *str1, const char *str2)
{
char c1, c2;
while (1)
{
c1 = tolower(*str1++);
c2 = tolower(*str2++);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
if (c1 == 0) return 0;
}
}
int dStrncmp(const char *str1, const char *str2, dsize_t len)
{
return strncmp(str1, str2, len);
}
int dStrnicmp(const char *str1, const char *str2, dsize_t len)
{
return strncasecmp( str1, str2, len );
}
char* dStrcpy(char *dst, const char *src)
{
return strcpy(dst,src);
}
char* dStrncpy(char *dst, const char *src, dsize_t len)
{
return strncpy(dst,src,len);
}
dsize_t dStrlen(const char *str)
{
if(!str)
return 0;
return strlen(str);
}
char* dStrupr(char *str)
{
char* saveStr = str;
while (*str)
{
*str = toupper(*str);
str++;
}
return saveStr;
}
char* dStrlwr(char *str)
{
char* saveStr = str;
while (*str)
{
*str = tolower(*str);
str++;
}
return saveStr;
}
char* dStrchr(char *str, int c)
{
return strchr(str,c);
}
const char* dStrchr(const char *str, int c)
{
return strchr(str,c);
}
const char* dStrrchr(const char *str, int c)
{
return strrchr(str,c);
}
char* dStrrchr(char *str, int c)
{
return strrchr(str,c);
}
dsize_t dStrspn(const char *str, const char *set)
{
return(strspn(str, set));
}
dsize_t dStrcspn(const char *str, const char *set)
{
return strcspn(str, set);
}
char* dStrstr(char *str1, char *str2)
{
return strstr(str1,str2);
}
char* dStrstr(const char *str1, const char *str2)
{
return strstr(str1,str2);
}
char* dStrtok(char *str, const char *sep)
{
return strtok(str, sep);
}
int dAtoi(const char *str)
{
if(!str)
return 0;
return atoi(str);
}
float dAtof(const char *str)
{
if(!str)
return 0;
return atof(str);
}
bool dAtob(const char *str)
{
return !dStricmp(str, "true") || !dStricmp(str, "1") || (0!=dAtoi(str));
}
bool dIsalnum(const char c)
{
return isalnum(c);
}
bool dIsalpha(const char c)
{
return(isalpha(c));
}
bool dIsspace(const char c)
{
return(isspace(c));
}
bool dIsdigit(const char c)
{
return(isdigit(c));
}
void dPrintf(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
}
int dVprintf(const char *format, void *arglist)
{
S32 len = vprintf(format, (char*)arglist);
return (len);
}
int dSprintf(char *buffer, dsize_t bufferSize, const char *format, ...)
{
va_list args;
va_start(args, format);
S32 len = vsnprintf(buffer, bufferSize, format, args);
AssertFatal( len < bufferSize, "dSprintf wrote to more memory than the specified buffer size" );
return (len);
}
int dVsprintf(char *buffer, dsize_t bufferSize, const char *format, void *arglist)
{
S32 len = vsnprintf(buffer, bufferSize, format, (char*)arglist);
AssertFatal( len < bufferSize, "dVsprintf wrote to more memory than the specified buffer size" );
return (len);
}
int dSscanf(const char *buffer, const char *format, ...)
{
va_list args;
va_start(args, format);
return vsscanf(buffer, format, args);
}
int dFflushStdout()
{
return fflush(stdout);
}
int dFflushStderr()
{
return fflush(stderr);
}
void dQsort(void *base, U32 nelem, U32 width, int (QSORT_CALLBACK *fcmp)(const void *, const void *))
{
qsort(base, nelem, width, fcmp);
}

View File

@ -0,0 +1,400 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include <pthread.h>
#include "platform/platformThread.h"
#include "platform/platformSemaphore.h"
#include "platform/platformMutex.h"
#include <stdlib.h>
static U32 Thread::gMainThread = 0;
struct MacThreadData
{
ThreadRunFunction mRunFunc;
S32 mRunArg;
Thread * mThread;
pthread_mutex_t mMutt;
};
//-----------------------------------------------------------------------------
// Function: ThreadRunHandler
// Summary: Calls Thread::run() with the thread's specified run argument.
// Neccesary because Thread::run() is provided as a non-threaded
// way to execute the thread's run function. So we have to keep
// track of the thread's lock here.
static void *ThreadRunHandler(void * arg)
{
MacThreadData * threadData = reinterpret_cast<MacThreadData*>(arg);
threadData->mThread->run(threadData->mRunArg);
pthread_mutex_unlock(&(threadData->mMutt));
// we could delete the Thread here, if it wants to be auto-deleted...
return NULL;
// this is where the created pthread will die.
}
//-----------------------------------------------------------------------------
Thread::Thread(ThreadRunFunction func, S32 arg, bool start_thread)
{
MacThreadData * threadData = new MacThreadData();
threadData->mRunFunc = func;
threadData->mRunArg = arg;
threadData->mThread = this;
pthread_mutex_init(&(threadData->mMutt),NULL);
mData = reinterpret_cast<void*>(threadData);
if (start_thread)
start();
}
Thread::~Thread()
{
join();
MacThreadData * threadData = reinterpret_cast<MacThreadData*>(mData);
pthread_mutex_destroy(&(threadData->mMutt));
delete threadData;
}
void Thread::start()
{
if(isAlive())
return;
MacThreadData * threadData = reinterpret_cast<MacThreadData*>(mData);
// cause start to block out other pthreads from using this Thread, at least until ThreadRunHandler exits.
pthread_mutex_lock(&(threadData->mMutt));
pthread_t threadID;
pthread_create(&threadID, NULL, ThreadRunHandler, mData);
}
bool Thread::join()
{
if(!isAlive())
return(false);
MacThreadData * threadData = reinterpret_cast<MacThreadData*>(mData);
pthread_mutex_lock(&(threadData->mMutt));
pthread_mutex_unlock(&(threadData->mMutt));
return true;
}
void Thread::run(S32 arg)
{
MacThreadData * threadData = reinterpret_cast<MacThreadData*>(mData);
if(threadData->mRunFunc)
threadData->mRunFunc(arg);
}
bool Thread::isAlive()
{
MacThreadData * threadData = reinterpret_cast<MacThreadData*>(mData);
if( pthread_mutex_trylock(&(threadData->mMutt)) == 0 )
{
pthread_mutex_unlock(&(threadData->mMutt));
return false; // we got the lock, it aint alive.
}
else
return true; // we could not get the lock, it must be alive.
}
U32 Thread::getCurrentThreadId()
{
return (U32)pthread_self();
}
bool Thread::compareThreads(U32 thread1, U32 thread2)
{
return pthread_equal(thread1, thread2);
}
U32 Thread::getMainThread()
{
return gMainThread;
}
void Thread::setMainThread()
{
gMainThread = Thread::getCurrentThreadId();
}
bool Thread::isMainThread()
{
#ifdef TORQUE_MULTITHREAD
return Thread::compareThreads(gMainThread, Thread::getCurrentThreadId());
#else
// If we're single threaded we're always in the main thread.
return true;
#endif
}
static void *gMut1 = Mutex::createMutex();
#define TORQUE_MAC_THREAD_TESTS
#if defined(TORQUE_MAC_THREAD_TESTS)
// -- the following is a set of tests to check thread sync facilities.
// the 2 different ways of starting threads, by subclassing Thread,
// and by using a (ThreadRunFunc*)(), are both demonstrated here.
#include "gui/core/guiControl.h"
class TestThread : public Thread
{
public:
volatile bool dienow;
TestThread(ThreadRunFunction func, S32 arg, bool start_thread) : Thread(func,arg,start_thread)
{
dienow = false;
}
virtual void run(S32 arg)
{
int r;
U32 time = Platform::getRealMilliseconds();
Con::printf("thread %i starting",arg);
while(!this->dienow) // will be set true by mothership thread
{
Mutex::lockMutex(gMut1);
GuiControl *ctrl = new GuiControl();
r = ((float)rand() / RAND_MAX ) * 10;
Platform::sleep(r);
delete ctrl;
Mutex::unlockMutex(gMut1);
}
time = Platform::getRealMilliseconds() - time;
Con::printf("thread %i exec time: %i",arg,time);
}
};
void mothership(S32 arg)
{
Con::printf("mothership started with arg %i",arg);
int r;
U32 time = Platform::getRealMilliseconds();
TestThread* thread[arg];
// create some threads, randomly sleep or delete one.
Mutex::lockMutex(gMut1);
for(int i=0; i < arg; i++)
{
Con::printf("starting thread %i",i+1);
thread[i] = new TestThread((ThreadRunFunction)NULL, i+1, true);
r = ((float)rand() / RAND_MAX ) * 10;
Platform::sleep(r);
}
Mutex::unlockMutex(gMut1);
for(int i=0; i < arg; i++)
{
r = ((float)rand() / RAND_MAX ) * 10;
Platform::sleep(r);
thread[i]->dienow=true;
delete thread[i];
}
time = Platform::getRealMilliseconds() - time;
Con::printf("mothership exec time: %i",time);
}
ConsoleFunction(TestThreads,void,1,3,"TestThreads([number of motherships], [number of threads per mothership]); Launches threads, all competing for the same mutex.")
{
int nThreads = 1;
int nMotherships = 1;
if(argc>=2) {
nThreads = dAtoi(argv[1]);
}
if(argc>=3) {
nMotherships = dAtoi(argv[2]);
}
bool semStateTest1, semStateTest2, semIndepTest;
// check whether we can acquire a newly made semaphore
semStateTest1 = true;
void* sem = Semaphore::createSemaphore();
void* sem2 = Semaphore::createSemaphore();
if(Semaphore::acquireSemaphore(sem,false)) {
semStateTest1 = true;
} else
semStateTest1 = false;
if(Semaphore::acquireSemaphore(sem2,false)) {
semStateTest2 = true;
} else
semStateTest2 = false;
// if we failed to acquire new semaphores, test whether semaphores are independant.
semIndepTest = true;
if(!semStateTest1 && !semStateTest2) {
Semaphore::releaseSemaphore(sem); // release one,
if(Semaphore::acquireSemaphore(sem2,false)) // try to acquire the other ( that we know we can't acquire yet )
semIndepTest = false; // we really should not be able to get this semaphore
else
semIndepTest = true;
}
Con::errorf("-------------- Semaphore test Results ------------");
if(!semStateTest1)
Con::errorf("New Semaphore Aqcuire test 1 failed.");
else
Con::printf("New Semaphore Aqcuire test 1 passed.");
if(!semStateTest2)
Con::errorf("New Semaphore Aqcuire test 2 failed.");
else
Con::printf("New Semaphore Aqcuire test 2 passed.");
if(!semIndepTest)
Con::errorf("Semaphores are NOT INDEPENDANT!!! - This is bad.");
else
Con::errorf("Semaphore Independance test passed.");
Con::printf("starting concurrent threads...");
Mutex::lockMutex(gMut1);
for(int i=0; i < nMotherships; i++) {
// memory leak here: because we dont keeep refs to the mothership Threads, we cannot delete them.
Con::printf("starting a mothership");
Thread *t = new Thread((ThreadRunFunction)mothership,nThreads,true);
}
Mutex::unlockMutex(gMut1);
}
class SemTestThread : public Thread
{
static void* mSem;
static void* mMut;
static int mCounter;
static int mThreadCount;
int threadNum;
public:
static void init()
{
mSem = Semaphore::createSemaphore(0);
mMut = Mutex::createMutex();
Mutex::lockMutex(mMut);
mCounter = 0;
mThreadCount = 0;
Mutex::unlockMutex(mMut);
}
static void dump()
{
Mutex::lockMutex(mMut);
Con::printf("Dumping... counter = %i", mCounter);
Con::printf(" nthreads = %i", mThreadCount);
Mutex::unlockMutex(mMut);
}
static void touch()
{
Semaphore::releaseSemaphore(mSem);
}
void run(S32)
{
Mutex::lockMutex(mMut);
threadNum = mThreadCount++;
Mutex::unlockMutex(mMut);
// Con::printf("testsem thread %i started",threadNum);
Semaphore::acquireSemaphore(mSem);
// Con::printf("testsem thread %i got semaphore", threadNum);
Mutex::lockMutex(mMut);
// Con::printf("testsem thread %i got mutex", threadNum);
mCounter++;
// Con::printf("counter = %i, by thread %i", mCounter, threadNum);
Mutex::unlockMutex(mMut);
Semaphore::releaseSemaphore(mSem);
Con::printf("testsem thread %i ending",threadNum);
}
};
void* SemTestThread::mSem;
void* SemTestThread::mMut;
int SemTestThread::mCounter;
int SemTestThread::mThreadCount;
ConsoleFunction( testSemaphores, void, 1, 3, "")
{
int nThreads = 1;
if(argc >= 2)
nThreads = dAtoi(argv[1]);
int nTouches = 0;
if(argc >= 3)
nTouches = dAtoi(argv[2]);
SemTestThread::init();
Con::printf("starting %i threads", nThreads);
for(int i = 0; i < nThreads; i++)
{
new SemTestThread();
}
SemTestThread::dump();
for(int i =0; i<nTouches; i++)
{
SemTestThread::touch();
}
}
ConsoleFunction( touchSem, void, 1,1, "")
{
SemTestThread::touch();
}
ConsoleFunction( dumpSem, void, 1, 1, "")
{
SemTestThread::dump();
}
#endif // TORQUE_MAC_THREAD_TESTS
class PlatformThreadStorage
{
public:
pthread_key_t mThreadKey;
};
ThreadStorage::ThreadStorage()
{
mThreadStorage = (PlatformThreadStorage *) mStorage;
constructInPlace(mThreadStorage);
pthread_key_create(&mThreadStorage->mThreadKey, NULL);
}
ThreadStorage::~ThreadStorage()
{
pthread_key_delete(mThreadStorage->mThreadKey);
}
void *ThreadStorage::get()
{
return pthread_getspecific(mThreadStorage->mThreadKey);
}
void ThreadStorage::set(void *value)
{
pthread_setspecific(mThreadStorage->mThreadKey, value);
}

View File

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/gameInterface.h"
#include <time.h>
#include <unistd.h>
#pragma message("time code needs eval -- may not be accurate in all cases, and might cause performance hit.")
//--------------------------------------
void Platform::getLocalTime(LocalTime &lt)
{
struct tm systime;
time_t long_time;
/// Get time as long integer.
time( &long_time );
/// Convert to local time, thread safe.
localtime_r( &long_time, &systime );
/// Fill the return struct
lt.sec = systime.tm_sec;
lt.min = systime.tm_min;
lt.hour = systime.tm_hour;
lt.month = systime.tm_mon;
lt.monthday = systime.tm_mday;
lt.weekday = systime.tm_wday;
lt.year = systime.tm_year;
lt.yearday = systime.tm_yday;
lt.isdst = systime.tm_isdst;
}
/// Gets the time in seconds since the Epoch
U32 Platform::getTime()
{
time_t epoch_time;
time( &epoch_time );
return epoch_time;
}
/// Gets the time in milliseconds since some epoch. In this case, system start time.
/// Storing milisec in a U32 overflows every 49.71 days
U32 Platform::getRealMilliseconds()
{
// Duration is a S32 value.
// if negative, it is in microseconds.
// if positive, it is in milliseconds.
Duration durTime = AbsoluteToDuration(UpTime());
U32 ret;
if( durTime < 0 )
ret = durTime / -1000;
else
ret = durTime;
return ret;
}
U32 Platform::getVirtualMilliseconds()
{
return platState.currentTime;
}
void Platform::advanceTime(U32 delta)
{
platState.currentTime += delta;
}
/// Asks the operating system to put the process to sleep for at least ms milliseconds
void Platform::sleep(U32 ms)
{
// note: this will overflow if you want to sleep for more than 49 days. just so ye know.
usleep( ms * 1000 );
}
#pragma mark ---- TimeManager ----
//--------------------------------------
static void _MacCarbUpdateSleepTicks()
{
if( platState.backgrounded )
platState.sleepTicks = Platform::getBackgroundSleepTime();
else
platState.sleepTicks = sgTimeManagerProcessInterval;
}
//--------------------------------------
void TimeManager::process()
{
_MacCarbUpdateSleepTicks();
U32 curTime = Platform::getRealMilliseconds(); // GTC returns Milliseconds, FYI.
S32 elapsedTime = curTime - platState.lastTimeTick;
if(elapsedTime <= platState.sleepTicks)
{
Platform::sleep(platState.sleepTicks - elapsedTime);
}
platState.lastTimeTick = Platform::getRealMilliseconds();
TimeEvent event;
event.elapsedTime = elapsedTime;
Game->postEvent(event);
}

View File

@ -0,0 +1,152 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "macCarbUtil.h"
//#include <CoreServices/CoreServices.h>
#include "console/console.h"
//-------------------------------------------------------------------------------
// Function name: LoadPrivateFrameworksBundle
// Summary: Looks in the application's Frameworks folder for a framework,
// and loads it if it finds it.
// Adpoted from a forum post by David 'FenrirWolf' Grace.
//-------------------------------------------------------------------------------
bool LoadPrivateFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr) {
// Looks in the application bundle for a framework, and loads it if it finds it.
CFURLRef privFrameworksURL, bundleURL;
if(!( privFrameworksURL = CFBundleCopyPrivateFrameworksURL( CFBundleGetMainBundle() )) ) {
return false;
}
if(!( bundleURL = CFURLCreateCopyAppendingPathComponent(NULL, privFrameworksURL, framework, false)) ) {
CFRelease(privFrameworksURL);
return false;
}
*bundlePtr = CFBundleCreate(NULL, bundleURL);
if (*bundlePtr && CFBundleLoadExecutable( *bundlePtr ) ) {
//found it under the apps private framework
CFRelease(privFrameworksURL);
CFRelease(bundleURL);
return true;
}
CFRelease(privFrameworksURL);
CFRelease(bundleURL);
if(*bundlePtr != NULL)
CFRelease(*bundlePtr);
return false;
}
//-------------------------------------------------------------------------------
// Function name: LoadFrameworksBundle
// Summary: Looks for a framework first in the app then the system,
// and loads it if it finds it.
// Adopted from Apple "CallMachOFramework" sample application
//-------------------------------------------------------------------------------
bool LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr)
{
const int numLocs = 5;
short vols[numLocs] = {kOnAppropriateDisk,
kOnAppropriateDisk,
kOnAppropriateDisk,
kLocalDomain,
kLocalDomain};
OSType folderType[numLocs] = {kPrivateFrameworksFolderType,
kFrameworksFolderType,
kApplicationSupportFolderType,
kFrameworksFolderType,
kDomainLibraryFolderType};
OSStatus err;
FSRef frameworksFolderRef;
CFURLRef baseURL;
CFURLRef bundleURL;
if (bundlePtr==NULL)
return false;
*bundlePtr = NULL;
// Look in the application's Frameworks folder before looking in the system
if( LoadPrivateFrameworkBundle(framework, bundlePtr) )
return true; // Yay! We found it, so return true...
// Otherwise, fall through to Torques usual bundle load logic
baseURL = NULL;
bundleURL = NULL;
for (int i = 0; i<numLocs; i++)
{
err = FSFindFolder(vols[i], folderType[i], true, &frameworksFolderRef);
if (err == noErr) {
baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef);
if (baseURL == NULL) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false);
if (bundleURL == NULL) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
*bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
if (*bundlePtr == NULL) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
if ( ! CFBundleLoadExecutable( *bundlePtr ) ) {
err = coreFoundationUnknownErr;
}
else // GOT IT!
break;
}
}
// Clean up.
if (err != noErr && *bundlePtr != NULL)
{
CFRelease(*bundlePtr);
*bundlePtr = NULL;
}
if (bundleURL != NULL)
CFRelease(bundleURL);
if (baseURL != NULL)
CFRelease(baseURL);
return err == noErr;
}
//-----------------------------------------------------------------------------
// Converts a QuickDraw displayID to a Core Graphics displayID.
// Different Mac APIs need different displayID types. The conversion is trivial
// on 10.3+, but ugly on 10.2, so we wrap it here.
//-----------------------------------------------------------------------------
CGDirectDisplayID MacCarbGetCGDisplayFromQDDisplay(GDHandle hDisplay)
{
// 10.2 doesn't have QDGetCGDirectDisplayID, so we roll our own.
// this is adapted from http://developer.apple.com/samplecode/glut/listing116.html
#if defined(TORQUE_MAC_HAS_QDGETCGDIRECTDISPLAYID)
CGDirectDisplayID display = QDGetCGDirectDisplayID(hDisplay);
#else
Rect qdRect = (**hDisplay).gdRect;
CGRect cgRect = CGRectMake(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
CGDisplayCount nDisplays;
CGDirectDisplayID displays[32];
CGGetDisplaysWithRect(cgRect, 32, displays, &nDisplays);
// TODO: this may not work well when video mirroring is on. check it & see.
CGDirectDisplayID display = displays[0];
#endif
return display;
}

View File

@ -0,0 +1,22 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _MACCARBUTIL_H_
#define _MACCARBUTIL_H_
#include <ApplicationServices/ApplicationServices.h>
#include <CoreFoundation/CoreFoundation.h>
/// Looks in the app's Frameworks folder for a framework and loads it if it finds it.
bool LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr);
/// Looks for a framework first in the app then the system, and loads it if it finds it.
bool LoadPrivateFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr);
/// Converts a QuickDraw displayID to a Core Graphics displayID.
/// Different Mac APIs need different displayID types. The conversion is trivial
/// on 10.3+, but ugly on 10.2, so we wrap it here.
CGDirectDisplayID MacCarbGetCGDisplayFromQDDisplay(GDHandle hDisplay);
#endif // _MACCARBUTIL_H_

View File

@ -0,0 +1,433 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformMacCarb/platformMacCarb.h"
#include "platform/platformVideo.h"
#include "platformMacCarb/macCarbOGLVideo.h"
#include "platformMacCarb/macCarbConsole.h"
#include "platform/platformInput.h"
#include "platform/gameInterface.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "platformMacCarb/macCarbEvents.h"
#include "platform/platformThread.h"
//------------------------------------------------------------------------------
#pragma mark ---- PlatState ----
MacCarbPlatState platState;
MacCarbPlatState::MacCarbPlatState()
{
hDisplay = NULL;
appWindow = NULL;
captureDisplay = true;
fadeWindows = true;
backgrounded = false;
minimized = false;
quit = false;
ctx = NULL;
headless = false;
// start with something reasonable.
desktopBitsPixel = 16;
desktopWidth = 1024;
desktopHeight = 768;
osVersion = 0;
dStrcpy(appWindowTitle, "Mac Torque Game Engine");
// Semaphore for alerts. We put the app in a modal state by blocking the main
// Torque thread until the RAEL thread allows it to continue.
alertSemaphore = Semaphore::createSemaphore(0);
alertDlg = NULL;
}
#pragma mark ---- Window stuff ----
const U32 kTFullscreenWindowAttrs = kWindowNoShadowAttribute | kWindowStandardHandlerAttribute;
const U32 kTDefaultWindowAttrs = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute;
//------------------------------------------------------------------------------
WindowPtr MacCarbCreateOpenGLWindow( GDHandle hDevice, U32 width, U32 height, bool fullScreen )
{
WindowPtr w = NULL;
Rect rect;
Rect dRect;
// device bounds, eg: [top,left , bottom,right] = [0,0 , 768,1024]
dRect = (**hDevice).gdRect;
// center the window's rect in the display's rect.
rect.top = dRect.top + (dRect.bottom - dRect.top - height) / 2;
rect.left = dRect.left + (dRect.right - dRect.left - width) / 2;
rect.right = rect.left + width;
rect.bottom = rect.top + height;
OSStatus err;
WindowAttributes windAttr = 0L;
WindowClass windClass = kDocumentWindowClass;
if (fullScreen)
{
windAttr = kTFullscreenWindowAttrs;
windClass = kAltPlainWindowClass;
// Overlay windows can be used to make transperant windows,
// which are good for performing all kinds of cool tricks.
// windClass = kOverlayWindowClass;
// windAttr |= kWindowOpaqueForEventsAttribute;
}
else
{
windAttr = kTDefaultWindowAttrs;
}
err = CreateNewWindow(windClass, windAttr, &rect, &w);
AssertISV( err == noErr && w != NULL, "Failed to create a new window.");
// in windowed-fullscreen mode, we set the window's level to be
// in front of the blanking window
if (fullScreen)
{
// create a new group if ours doesn't already exist
if (platState.torqueWindowGroup==NULL)
CreateWindowGroup(NULL, &platState.torqueWindowGroup);
// place window in group
SetWindowGroup(w, platState.torqueWindowGroup);
// set window group level to one higher than blanking window.
SetWindowGroupLevel(platState.torqueWindowGroup, kTFullscreenWindowLevel);
}
platState.minimized = false;
RGBColor black;
dMemset(&black, 0, sizeof(RGBColor));
SetWindowContentColor( w, &black);
return(w);
}
//------------------------------------------------------------------------------
// Fade a window in, asynchronously.
void MacCarbFadeInWindow( WindowPtr window )
{
if(!IsValidWindowPtr(window))
return;
// bump this to the main thread if we're not on the main thread.
if(Thread::getCurrentThreadId() != platState.firstThreadId)
{
MacCarbSendTorqueEventToMain( kEventTorqueFadeInWindow, window );
return;
}
// set state on menubar & mouse cursor.
if(Video::isFullScreen())
{
HideMenuBar();
MacCarbSetHideCursor(true);
}
else
{
ShowMenuBar();
}
SelectWindow(window);
if(platState.fadeWindows)
{
TransitionWindowOptions t;
dMemset(&t, 0, sizeof(t));
TransitionWindowWithOptions( window, kWindowFadeTransitionEffect,
kWindowShowTransitionAction, NULL, true, &t);
}
else
{
ShowWindow(window);
}
}
//------------------------------------------------------------------------------
// Fade a window out, asynchronously. It will be released when the transition finishes.
void MacCarbFadeAndReleaseWindow( WindowPtr window )
{
if(!IsValidWindowPtr(window))
return;
if(Thread::getCurrentThreadId() != platState.firstThreadId && !platState.quit)
{
MacCarbSendTorqueEventToMain( kEventTorqueFadeOutWindow, window );
return;
}
if(platState.fadeWindows)
{
TransitionWindowOptions t;
dMemset(&t, 0, sizeof(t));
TransitionWindowWithOptions( window, kWindowFadeTransitionEffect,
kWindowHideTransitionAction, NULL, false, &t);
}
else
{
MacCarbSendTorqueEventToMain(kEventTorqueReleaseWindow, window);
}
}
//------------------------------------------------------------------------------
// Hide or show the menu bar.
void MacCarbShowMenuBar(bool show)
{
if(Thread::getCurrentThreadId() != platState.firstThreadId && !platState.quit)
{
MacCarbSendTorqueEventToMain( kEventTorqueShowMenuBar, (void*)show );
return;
}
if(show)
ShowMenuBar();
else
HideMenuBar();
}
//------------------------------------------------------------------------------
// DGL, the Gui, and TS use this for various purposes.
const Point2I &Platform::getWindowSize()
{
return platState.windowSize;
}
//------------------------------------------------------------------------------
// save the window size, for DGL's use
void Platform::setWindowSize( U32 newWidth, U32 newHeight )
{
platState.windowSize.set( newWidth, newHeight );
}
//------------------------------------------------------------------------------
// Issue a minimize event. The standard handler will handle it.
void Platform::minimizeWindow()
{
HICommand cmd;
dMemset(&cmd, 0, sizeof(HICommand));
cmd.commandID = kHICommandMinimizeWindow;
ProcessHICommand( &cmd );
}
//------------------------------------------------------------------------------
void Platform::setWindowTitle(const char* title )
{
if(!platState.appWindow)
return;
// set app window's title
CFStringRef cfsTitle = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
SetWindowTitleWithCFString(platState.appWindow, cfsTitle);
CFRelease(cfsTitle);
// save title in platstate
dStrncpy(platState.appWindowTitle, title, getMin((U32)dStrlen(title), sizeof(platState.appWindowTitle)));
}
#pragma mark ---- Init funcs ----
//------------------------------------------------------------------------------
void Platform::init()
{
// Set the platform variable for the scripts
Con::setVariable( "$platform", "macos" );
MacConsole::create();
if(gConsole && platState.headless == true)
gConsole->enable(true);
Input::init();
// allow users to specify whether to capture the display or not when going fullscreen
Con::addVariable("pref::mac::captureDisplay", TypeBool, &platState.captureDisplay);
Con::addVariable("pref::mac::fadeWindows", TypeBool, &platState.fadeWindows);
// create the opengl display device
DisplayDevice *dev = NULL;
Con::printf( "Video Init:" );
Video::init();
dev = OpenGLDevice::create();
if(dev)
Con::printf( " Accelerated OpenGL display device detected." );
else
Con::printf( " Accelerated OpenGL display device not detected." );
// and now we can install the device.
Video::installDevice(dev);
Con::printf( "" );
}
//------------------------------------------------------------------------------
void Platform::shutdown()
{
setWindowLocked( false );
Video::destroy();
Input::destroy();
MacConsole::destroy();
}
//------------------------------------------------------------------------------
// Get the video settings from the prefs.
static void _MacCarbGetInitialRes(U32 &width, U32 &height, U32 &bpp, bool &fullScreen)
{
const char* resString;
char *tempBuf, *s;
// cache the desktop size of the selected screen in platState
Video::getDesktopResolution();
// load pref variables, properly choose windowed / fullscreen
fullScreen = Con::getBoolVariable( "$pref::Video::fullScreen" );
if (fullScreen)
resString = Con::getVariable( "$pref::Video::resolution" );
else
resString = Con::getVariable( "$pref::Video::windowedRes" );
// dStrtok is destructive, work on a copy...
tempBuf = new char[dStrlen( resString ) + 1];
dStrcpy( tempBuf, resString );
// set window size
//DAW: Added min size checks for windowSize
width = dAtoi( dStrtok( tempBuf, " x\0" ) );
if( ! width > 0 ) width = platState.windowSize.x;
height = dAtoi( dStrtok( NULL, " x\0") );
if( ! height > 0 ) height = platState.windowSize.y;
// bit depth
if (fullScreen)
{
s = dAtoi( dStrtok( NULL, "\0" ) );
if( ! bpp > 0 ) bpp = 16;
}
else
bpp = platState.desktopBitsPixel;
delete [] tempBuf;
}
//------------------------------------------------------------------------------
void Platform::initWindow(const Point2I &initialSize, const char *name)
{
dSprintf(platState.appWindowTitle, sizeof(platState.appWindowTitle), name);
// init the default window size
platState.windowSize = initialSize;
if( ! platState.windowSize.x > 0 ) platState.windowSize.x = 640;
if( ! platState.windowSize.y > 0 ) platState.windowSize.y = 480;
DisplayDevice::init();
bool fullScreen;
U32 width, height, bpp;
_MacCarbGetInitialRes(width, height, bpp, fullScreen);
// this will create a rendering context & window
bool ok = Video::setDevice( "OpenGL", width, height, bpp, fullScreen );
if ( ! ok )
{
AssertFatal( false, "Could not find a compatible display device!" );
}
if (platState.appWindow)
{
// install handlers to the given window.
EventTargetRef winTarg = GetWindowEventTarget(platState.appWindow);
}
MacCarbInstallCarbonEventHandlers();
}
#pragma mark ---- Platform utility funcs ----
//--------------------------------------
// Web browser function:
//--------------------------------------
bool Platform::openWebBrowser( const char* webAddress )
{
OSStatus err;
CFURLRef url = CFURLCreateWithBytes(NULL,(UInt8*)webAddress,dStrlen(webAddress),kCFStringEncodingASCII,NULL);
err = LSOpenCFURLRef(url,NULL);
CFRelease(url);
// kick out of fullscreen mode, so we can *see* the webpage!
if(Video::isFullScreen())
Video::toggleFullScreen();
return(err==noErr);
}
#pragma mark -
#pragma mark ---- Tests ----
ConsoleFunction(testWindowLevels,void,1,2,"testWindowLevels([lev to set]);")
{
SInt32 lev;
Con::printf(" Sheilding window level is %x",CGShieldingWindowLevel());
GetWindowGroupLevel(GetWindowGroupOfClass(kUtilityWindowClass),&lev);
Con::printf(" Utility window level is %x", lev);
GetWindowGroupLevel(GetWindowGroupOfClass(kUtilityWindowClass),&lev);
Con::printf(" Floating window level is %x", lev);
GetWindowGroupLevel(GetWindowGroupOfClass(kAlertWindowClass),&lev);
Con::printf(" Alert window level is %x", lev);
lev=1;
if(argc==2)
lev=dAtoi(argv[1]);
SetWindowGroupLevel( GetWindowGroupOfClass(kUtilityWindowClass), lev);
SetWindowGroupLevel( GetWindowGroupOfClass(kFloatingWindowClass), lev);
SetWindowGroupLevel( GetWindowGroupOfClass(kAlertWindowClass), lev);
}
ConsoleFunction( testSetWindowTitle, void, 2,4, "")
{
Platform::setWindowTitle(argv[1]);
}
ConsoleFunction( invertScreenColor, void, 1,1, "")
{
static bool inverted = false;
CGGammaValue reds[1024];
CGGammaValue greens[1024];
CGGammaValue blues[1024];
U32 numTableEntries;
CGGetDisplayTransferByTable( CGMainDisplayID(), 1024, reds, greens, blues, &numTableEntries);
CGGammaValue newReds[numTableEntries];
CGGammaValue newGreens[numTableEntries];
CGGammaValue newBlues[numTableEntries];
for(int i=0; i< numTableEntries; i++)
{
newReds[i] = reds[numTableEntries-1-i];
newGreens[i] = greens[numTableEntries-1-i];
newBlues[i] = blues[numTableEntries-1-i];
}
CGSetDisplayTransferByTable(CGMainDisplayID(), numTableEntries, newReds, newGreens, newBlues);
}
ConsoleFunction(testAsserts, void, 1,1,"")
{
AssertFatal(false,"Monsters in my OATMEAL.");
AssertWarn(false,"Oh sweet mercy, the PAIN... THE PAIN!");
AssertISV(false,"AAaaah! *GARGLE* *SPUTTER* *WET THUD*");
}

View File

@ -0,0 +1,52 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//------------------------------
//maccarb_common_prefix.h
//------------------------------
#define TORQUE_OS_MAC_CARB 1 // always defined right now...
#if defined(TORQUE_OS_MAC_OSX)
//#define Z_PREFIX // OSX comes with zlib, so generate unique symbols.
#endif
// defines for the mac headers to activate proper Carbon codepaths.
#define TARGET_API_MAC_CARBON 1 // apple carbon header flag to take right defpaths.
//#define OTCARBONAPPLICATION 1 // means we can use the old-style funcnames
// determine the OS version we're building on...
// MAC_OS_X_VERSION_MAX_ALLOWED will have the local OSX version,
// or it will have the version of OSX for the sdk we're cross compiling with.
#include <AvailabilityMacros.h>
// Pasteboards were introduced in 10.3, and are not available before 10.3
// QDGetCGDirectDisplayID was introduced in 10.3, but there is a manual workaround.
// There's a bug in some versions of Xcode, such that MAC_OS_X_VERSION_MAX_ALLOWED
// is not set correctly, and so we cannot properly use the weak-linking feature.
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1030
#define TORQUE_MAC_HAS_PASTEBOARD
#define TORQUE_MAC_HAS_QDGETCGDIRECTDISPLAYID
#endif
// Some features we're currently using were introduced in 10.3, or 10.4.
// We'll define them, or provide stubs for them, in the case that we are on 10.2
#if MAC_OS_X_VERSION_MIN_REQUIRED <= 1020
#include "platformMacCarb/macCarbCompat.10.2.h"
#else
#define MacCarbInit1020CompatInit() // stub it out
#endif
// Some features we're currently using were introduced in 10.4.
// for now, it's just a few values, so we define them here.
#if MAC_OS_X_VERSION_MIN_REQUIRED <= 1030
#define gestaltCPU970FX 0x013C
#define gestaltCPUPentium4 'i5iv'
#define CPU_TYPE_X86 ((cpu_type_t) 7)
#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */
#define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64)
#define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64)
#endif

View File

@ -0,0 +1,51 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#import <Cocoa/Cocoa.h>
#include "platform/platform.h"
#include "console/console.h"
bool dPathCopy(const char* source, const char* dest, bool nooverwrite)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *nsource = [manager stringWithFileSystemRepresentation:source length:dStrlen(source)];
NSString *ndest = [manager stringWithFileSystemRepresentation:dest length:dStrlen(dest)];
if(! [manager fileExistsAtPath:nsource])
{
Con::errorf("dPathCopy: no file exists at %s",source);
return false;
}
if( [manager fileExistsAtPath:ndest] )
{
if(nooverwrite)
{
Con::errorf("dPathCopy: file already exists at %s",dest);
return false;
}
Con::warnf("Deleting files at path: %s", dest);
bool deleted = [manager removeFileAtPath:ndest handler:nil];
if(!deleted)
{
Con::errorf("Copy failed! Could not delete files at path: %s", dest);
return false;
}
}
bool ret = [manager copyPath:nsource toPath:ndest handler:nil];
[pool release];
return ret;
}
ConsoleFunction( dPathCopy, void, 3,3, "2 args.")
{
dPathCopy(argv[1], argv[2]);
}

View File

@ -0,0 +1,214 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _PLATFORMGL_H_
#define _PLATFORMGL_H_
/// We use the standard Apple OpenGL framework headers.
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
/// Using aglMacro.h gives us signifigant performance gains in opengl immediate mode ( glVertex etc )
#if !defined(TORQUE_DEBUG)
#define USE_AGL_MACRO
#endif
#if defined(USE_AGL_MACRO)
#if !defined(AGLContext)
typedef struct __AGLContextRec *AGLContext;
#endif
#include <AGL/aglMacro.h>
extern AGLContext agl_ctx;
#endif
/// Allows outline mode drawing via a function pointer swapping trick.
/// Must be included AFTER all the OpenGL headers.
#include "platformMacCarb/macCarbOutlineGL.h"
//------------------------------------------------------------------------------
/// This is legacy stuff for the d3d wrapper layer.
// The code that requires these stubs should probably be ifdef'd out of any non w32 build
//------------------------------------------------------------------------------
extern GLboolean glAvailableVertexBufferEXT();
extern GLint glAllocateVertexBufferEXT(GLsizei size, GLint format, GLboolean preserve);
extern void* glLockVertexBufferEXT(GLint handle, GLsizei size);
extern void glUnlockVertexBufferEXT(GLint handle);
extern void glSetVertexBufferEXT(GLint handle);
extern void glOffsetVertexBufferEXT(GLint handle, GLuint offset);
extern void glFillVertexBufferEXT(GLint handle, GLint first, GLsizei count);
extern void glFreeVertexBufferEXT(GLint handle);
// these are extensions for glAllocateVertexBufferEXT
#define GL_V12MTVFMT_EXT 0x8702
#define GL_V12MTNVFMT_EXT 0x8703
#define GL_V12FTVFMT_EXT 0x8704
#define GL_V12FMTVFMT_EXT 0x8705
//------------------------------------------------------------------------------
// make sure this is defined, as we need to use it when around.
// some versions of OSX only define the SGIS version ( same hexcode )
#ifndef GL_CLAMP_TO_EDGE_EXT
#define GL_CLAMP_TO_EDGE_EXT 0x812F
#endif
/// GL state information.
struct GLState
{
bool suppARBMultitexture;
bool suppEXTblendcolor;
bool suppEXTblendminmax;
bool suppPackedPixels;
bool suppTexEnvAdd;
bool suppLockedArrays;
bool suppTextureEnvCombine;
bool suppVertexArrayRange;
bool suppFogCoord;
bool suppEdgeClamp;
bool suppTextureCompression;
bool suppS3TC;
bool suppFXT1;
bool suppTexAnisotropic;
bool suppPalettedTexture;
bool suppVertexBuffer;
bool suppSwapInterval;
GLint maxFSAASamples;
unsigned int triCount[4];
unsigned int primCount[4];
unsigned int primMode; // 0-3
GLfloat maxAnisotropy;
GLint maxTextureUnits;
bool isDirect3D;
};
extern GLState gGLState;
extern bool gOpenGLDisablePT;
extern bool gOpenGLDisableCVA;
extern bool gOpenGLDisableTEC;
extern bool gOpenGLDisableARBMT;
extern bool gOpenGLDisableFC;
extern bool gOpenGLDisableTCompress;
extern bool gOpenGLNoEnvColor;
extern float gOpenGLGammaCorrection;
extern bool gOpenGLNoDrawArraysAlpha;
//------------------------------------------------------------------------------
/// Inline state getters for dgl
//------------------------------------------------------------------------------
inline void dglSetRenderPrimType(unsigned int type)
{
gGLState.primMode = type;
}
inline void dglClearPrimMetrics()
{
for(int i = 0; i < 4; i++)
gGLState.triCount[i] = gGLState.primCount[i] = 0;
}
inline bool dglDoesSupportPalettedTexture()
{
return gGLState.suppPalettedTexture && (gOpenGLDisablePT == false);
}
inline bool dglDoesSupportCompiledVertexArray()
{
return gGLState.suppLockedArrays && (gOpenGLDisableCVA == false);
}
inline bool dglDoesSupportTextureEnvCombine()
{
return gGLState.suppTextureEnvCombine && (gOpenGLDisableTEC == false);
}
inline bool dglDoesSupportARBMultitexture()
{
return gGLState.suppARBMultitexture && (gOpenGLDisableARBMT == false);
}
inline bool dglDoesSupportEXTBlendColor()
{
return gGLState.suppEXTblendcolor;
}
inline bool dglDoesSupportEXTBlendMinMax()
{
return gGLState.suppEXTblendminmax;
}
inline bool dglDoesSupportVertexArrayRange()
{
return gGLState.suppVertexArrayRange;
}
inline bool dglDoesSupportFogCoord()
{
return gGLState.suppFogCoord && (gOpenGLDisableFC == false);
}
inline bool dglDoesSupportEdgeClamp()
{
return gGLState.suppEdgeClamp;
}
inline bool dglDoesSupportTextureCompression()
{
return gGLState.suppTextureCompression && (gOpenGLDisableTCompress == false);
}
inline bool dglDoesSupportS3TC()
{
return gGLState.suppS3TC;
}
inline bool dglDoesSupportFXT1()
{
return gGLState.suppFXT1;
}
inline bool dglDoesSupportTexEnvAdd()
{
return gGLState.suppTexEnvAdd;
}
inline bool dglDoesSupportTexAnisotropy()
{
return gGLState.suppTexAnisotropic;
}
inline bool dglDoesSupportVertexBuffer()
{
return false;
}
inline GLfloat dglGetMaxAnisotropy()
{
return gGLState.maxAnisotropy;
}
inline GLint dglGetMaxTextureUnits()
{
if (dglDoesSupportARBMultitexture())
return gGLState.maxTextureUnits;
else
return 1;
}
//------------------------------------------------------------------------------
/// For radeon cards we can do fast FSAA mode switching.
/// Sets Full Scene Anti-Aliasing (FSAA) samples ( 1x, 2x, 4x ) via aglSetInteger()
//------------------------------------------------------------------------------
#define ATI_FSAA_LEVEL ((unsigned long)510)
void dglSetFSAASamples(GLint aasamp);
#endif // _PLATFORMGL_H_

View File

@ -0,0 +1,132 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _PLATFORMMACCARB_H_
#define _PLATFORMMACCARB_H_
/// NOTE: Placing system headers before Torque's platform.h will work around the Torque-Redefines-New problems.
#include <Carbon/Carbon.h>
#include <CoreServices/CoreServices.h>
#include <AGL/agl.h>
#include "platform/platform.h"
#include "math/mMath.h"
class MacCarbPlatState
{
public:
GDHandle hDisplay;
CGDirectDisplayID cgDisplay;
bool captureDisplay;
bool fadeWindows;
WindowPtr appWindow;
char appWindowTitle[256];
WindowGroupRef torqueWindowGroup;
bool quit;
AGLContext ctx;
bool ctxNeedsUpdate;
bool headless;
S32 desktopBitsPixel;
S32 desktopWidth;
S32 desktopHeight;
U32 currentTime;
U32 osVersion;
TSMDocumentID tsmDoc;
bool tsmActive;
U32 firstThreadId;
U32 torqueThreadId;
void* alertSemaphore;
S32 alertHit;
DialogRef alertDlg;
EventQueueRef mainEventQueue;
MRandomLCG platRandom;
bool mouseLocked;
bool backgrounded;
bool minimized;
S32 sleepTicks;
S32 lastTimeTick;
Point2I windowSize;
U32 appReturn;
U32 argc;
char** argv;
MacCarbPlatState();
};
/// Global singleton that encapsulates a lot of mac platform state & globals.
extern MacCarbPlatState platState;
/// @name Misc Mac Plat Functions
/// Functions that are used by multiple files in the mac plat, but too trivial
/// to require their own header file.
/// @{
/// Fills gGLState with info about this gl renderer's capabilities.
void getGLCapabilities(void);
/// Creates a new mac window, of a particular size, centered on the screen.
/// If a fullScreen window is requested, then the window is created without
/// decoration, in front of all other normal windows AND BEHIND asian text input methods.
/// This path to a fullScreen window allows asian text input methods to work
/// in full screen mode, because it avoids capturing the display.
WindowPtr MacCarbCreateOpenGLWindow( GDHandle hDevice, U32 width, U32 height, bool fullScreen );
/// Asnychronously fade a window into existence, and set menu bar visibility.
/// The fading can be turned off via the preference $pref::mac::fadeWindows.
/// It also sends itself to the main thread if it is called on any other thread.
void MacCarbFadeInWindow( WindowPtr window );
/// Asnychronously fade a window out of existence. The window will be destroyed
/// when the fade is complete.
/// The fading can be turned off via the preference $pref::mac::fadeWindows.
/// It also sends itself to the main thread if it is called on any other thread.
void MacCarbFadeAndReleaseWindow( WindowPtr window );
/// Manage Mac MenuBar visibility.
/// If show is true the menubar is shown, if false the menubar is hidden.
/// It sends itself to the main thread if it is called on any other thread.
void MacCarbShowMenuBar(bool show);
/// Translates a Mac keycode to a Torque keycode
U8 TranslateOSKeyCode(U8 vcode);
/// @}
/// @name Misc Mac Plat constants
/// @{
/// earlier versions of OSX don't have these convinience macros, so manually stick them here.
#ifndef IntToFixed
#define IntToFixed(a) ((Fixed)(a) <<16)
#define FixedToInt(a) ((short)(((Fixed)(a) + fixed1/2) >> 16))
#endif
/// window level constants
const U32 kTAlertWindowLevel = CGShieldingWindowLevel() - 1;
const U32 kTUtilityWindowLevel = CGShieldingWindowLevel() - 2;
const U32 kTFullscreenWindowLevel = CGShieldingWindowLevel() - 3;
/// mouse wheel sensitivity factor
const S32 kTMouseWheelMagnificationFactor = 25;
/// @}
#endif //_PLATFORMMACCARB_H_

Binary file not shown.