Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

View file

@ -0,0 +1,38 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//
// macApplication.h
// T3D
//
// Created by admin account on 2/1/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface macApplication : NSApplication
{
}
@end

View file

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "macApplication.h"
#include "windowManager/mac/macWindow.h"
#include "windowManager/mac/macView.h"
#include "console/console.h"
@implementation macApplication
- (void)sendEvent:(NSEvent*)theEvent
{
if([theEvent type] == NSKeyUp)
{
if([theEvent modifierFlags] & NSCommandKeyMask)
{
// These will normally be blocked, but we wants them!
[[self delegate] keyUp:theEvent];
return;
}
}
MacWindow* window = [(GGMacView*)[self delegate] torqueWindow];
if(window && window->isFullscreen())
{
switch([theEvent type])
{
case NSLeftMouseDown:
[[self delegate] mouseDown:theEvent];
return;
case NSRightMouseDown:
[[self delegate] rightMouseDown:theEvent];
return;
case NSLeftMouseUp:
[[self delegate] mouseUp:theEvent];
return;
case NSRightMouseUp:
[[self delegate] rightMouseUp:theEvent];
return;
case NSMouseMoved:
[[self delegate] mouseMoved:theEvent];
return;
case NSLeftMouseDragged:
[[self delegate] mouseDragged:theEvent];
return;
case NSRightMouseDragged:
[[self delegate] rightMouseDragged:theEvent];
return;
case NSScrollWheel:
[[self delegate] scrollWheel:theEvent];
return;
default:
break;
}
}
[super sendEvent:theEvent];
}
@end

View file

@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <CoreServices/CoreServices.h>
#include "platform/async/asyncUpdate.h"
AsyncUpdateThread::AsyncUpdateThread( String name, AsyncUpdateList* updateList )
: Parent( 0, 0, false, false ),
mUpdateList( updateList ),
mName( name )
{
MPCreateEvent( ( MPEventID* ) &mUpdateEvent );
}
AsyncUpdateThread::~AsyncUpdateThread()
{
MPDeleteEvent( *( ( MPEventID* ) &mUpdateEvent ) ) ;
}
void AsyncUpdateThread::_waitForEventAndReset()
{
MPWaitForEvent( *( ( MPEventID* ) &mUpdateEvent ), NULL, kDurationForever );
}
void AsyncUpdateThread::triggerUpdate()
{
MPSetEvent( *( ( MPEventID* ) &mUpdateEvent ), 1 );
}
AsyncPeriodicUpdateThread::AsyncPeriodicUpdateThread
( String name, AsyncUpdateList* updateList, U32 intervalMS )
: Parent( name, updateList ),
mIntervalMS( intervalMS )
{
}
AsyncPeriodicUpdateThread::~AsyncPeriodicUpdateThread()
{
}
void AsyncPeriodicUpdateThread::_waitForEventAndReset()
{
MPWaitForEvent( *( ( MPEventID* ) &mUpdateEvent ), NULL, kDurationMillisecond * mIntervalMS );
}

View file

@ -0,0 +1,285 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/machine.h>
#include <math.h>
#include <CoreServices/CoreServices.h>
#include "platformMac/platformMacCarb.h"
#include "platform/platformAssert.h"
#include "console/console.h"
#include "core/stringTable.h"
// Original code by Sean O'Brien (http://www.garagegames.com/community/forums/viewthread/81815).
// Reads sysctl() string value into buffer at DEST with maximum length MAXLEN
// Return: 0 on success, non-zero is error in accordance with stdlib and <errno.h>
int _getSysCTLstring(const char key[], char * dest, size_t maxlen) {
size_t len = 0;
int err;
// Call with NULL for 'dest' to have the required size stored in 'len'. If the 'key'
// doesn't exist, 'err' will be -1 and if all goes well, it will be 0.
err = sysctlbyname(key, NULL, &len, NULL, 0);
if (err == 0) {
AssertWarn((len <= maxlen), ("Insufficient buffer length for SYSCTL() read. Truncating.\n"));
if (len > maxlen)
len = maxlen;
// Call with actual pointers to 'dest' and clamped 'len' fields to perform the read.
err = sysctlbyname(key, dest, &len, NULL, 0);
}
return err;
}
// TEMPLATED Reads sysctl() integer value into variable DEST of type T
// The two predominant types used are unsigned longs and unsiged long longs
// and the size of the argument is on a case-by-case value. As a "guide" the
// resources at Apple claim that any "byte count" or "frequency" values will
// be returned as ULL's and most everything else will be UL's.
// Return: 0 on success, non-zero is error in accordance with stdlib and <errno.h>
template <typename T>
int _getSysCTLvalue(const char key[], T * dest) {
size_t len = 0;
int err;
// Call with NULL for 'dest' to get the size. If the 'key' doesn't exist, the
// 'err' returned will be -1, so 0 indicates success.
err = sysctlbyname(key, NULL, &len, NULL, 0);
if (err == 0) {
AssertFatal((len == sizeof(T)), "Mis-matched destination type for SYSCTL() read.\n");
// We're just double-checking that we're being called with the correct type of
// pointer for 'dest' so we don't clobber anything nearby when writing back.
err = sysctlbyname(key, dest, &len, NULL, 0);
}
return err;
}
Platform::SystemInfo_struct Platform::SystemInfo;
#define BASE_MHZ_SPEED 0
void Processor::init()
{
U32 procflags;
int err, cpufam, cputype, cpusub;
char buf[20];
unsigned long lraw;
unsigned long long llraw;
Con::printf( "System & Processor Information:" );
SInt32 MacVersion;
if( Gestalt( gestaltSystemVersion, &MacVersion ) == noErr )
{
U32 revision = MacVersion & 0xf;
U32 minorVersion = ( MacVersion & 0xf0 ) >> 4;
U32 majorVersion = ( MacVersion & 0xff00 ) >> 8;
Con::printf( " OSX Version: %x.%x.%x", majorVersion, minorVersion, revision );
}
err = _getSysCTLstring("kern.ostype", buf, sizeof(buf));
if (err)
Con::printf( " Unable to determine OS type\n" );
else
Con::printf( " Mac OS Kernel name: %s", buf);
err = _getSysCTLstring("kern.osrelease", buf, sizeof(buf));
if (err)
Con::printf( " Unable to determine OS release number\n" );
else
Con::printf( " Mac OS Kernel version: %s", buf );
err = _getSysCTLvalue<unsigned long long>("hw.memsize", &llraw);
if (err)
Con::printf( " Unable to determine amount of physical RAM\n" );
else
Con::printf( " Physical memory installed: %d MB", (llraw >> 20));
err = _getSysCTLvalue<unsigned long>("hw.usermem", &lraw);
if (err)
Con::printf( " Unable to determine available user address space\n");
else
Con::printf( " Addressable user memory: %d MB", (lraw >> 20));
////////////////////////////////
// Values for the Family Type, CPU Type and CPU Subtype are defined in the
// SDK files for the Mach Kernel ==> mach/machine.h
////////////////////////////////
// CPU Family, Type, and Subtype
cpufam = 0;
cputype = 0;
cpusub = 0;
err = _getSysCTLvalue<unsigned long>("hw.cpufamily", &lraw);
if (err)
Con::printf( " Unable to determine 'family' of CPU\n");
else {
cpufam = (int) lraw;
err = _getSysCTLvalue<unsigned long>("hw.cputype", &lraw);
if (err)
Con::printf( " Unable to determine CPU type\n");
else {
cputype = (int) lraw;
err = _getSysCTLvalue<unsigned long>("hw.cpusubtype", &lraw);
if (err)
Con::printf( " Unable to determine CPU subtype\n");
else
cpusub = (int) lraw;
// If we've made it this far,
Con::printf( " Installed processor ID: Family 0x%08x Type %d Subtype %d",cpufam, cputype,cpusub);
}
}
// The Gestalt version was known to have issues with some Processor Upgrade cards
// but it is uncertain whether this version has similar issues.
err = _getSysCTLvalue<unsigned long long>("hw.cpufrequency", &llraw);
if (err) {
llraw = BASE_MHZ_SPEED;
Con::printf( " Unable to determine CPU Frequency. Defaulting to %d MHz\n", llraw);
} else {
llraw /= 1000000;
Con::printf( " Installed processor clock frequency: %d MHz", llraw);
}
Platform::SystemInfo.processor.mhz = (unsigned int)llraw;
// Here's one that the original version of this routine couldn't do -- number
// of processors (cores)
U32 ncpu = 1;
err = _getSysCTLvalue<unsigned long>("hw.ncpu", &lraw);
if (err)
Con::printf( " Unable to determine number of processor cores\n");
else
{
ncpu = lraw;
Con::printf( " Installed/available processor cores: %d", lraw);
}
// Now use CPUFAM to determine and then store the processor type
// and 'friendly name' in GG-accessible structure. Note that since
// we have access to the Family code, the Type and Subtypes are useless.
//
// NOTE: Even this level of detail is almost assuredly not needed anymore
// and the Optional Capability flags (further down) should be more than enough.
switch(cpufam)
{
case CPUFAMILY_POWERPC_G3:
Platform::SystemInfo.processor.type = CPU_PowerPC_G3;
Platform::SystemInfo.processor.name = StringTable->insert("PowerPC G3");
break;
case CPUFAMILY_POWERPC_G4:
Platform::SystemInfo.processor.type = CPU_PowerPC_G3;
Platform::SystemInfo.processor.name = StringTable->insert("PowerPC G4");
break;
case CPUFAMILY_POWERPC_G5:
Platform::SystemInfo.processor.type = CPU_PowerPC_G3;
Platform::SystemInfo.processor.name = StringTable->insert("PowerPC G5");
break;
case CPUFAMILY_INTEL_6_14:
Platform::SystemInfo.processor.type = CPU_Intel_Core;
if( ncpu == 2 )
Platform::SystemInfo.processor.name = StringTable->insert("Intel Core Duo");
else
Platform::SystemInfo.processor.name = StringTable->insert("Intel Core");
break;
#ifdef CPUFAMILY_INTEL_6_23
case CPUFAMILY_INTEL_6_23:
#endif
case CPUFAMILY_INTEL_6_15:
Platform::SystemInfo.processor.type = CPU_Intel_Core2;
if( ncpu == 4 )
Platform::SystemInfo.processor.name = StringTable->insert("Intel Core 2 Quad");
else
Platform::SystemInfo.processor.name = StringTable->insert("Intel Core 2 Duo");
break;
#ifdef CPUFAMILY_INTEL_6_26
case CPUFAMILY_INTEL_6_26:
Platform::SystemInfo.processor.type = CPU_Intel_Core2;
Platform::SystemInfo.processor.name = StringTable->insert( "Intel 'Nehalem' Core Processor" );
break;
#endif
default:
// explain why we can't get the processor type.
Con::warnf( " Unknown Processor (family, type, subtype): 0x%x\t%d %d", cpufam, cputype, cpusub);
// for now, identify it as an x86 processor, because Apple is moving to Intel chips...
Platform::SystemInfo.processor.type = CPU_X86Compatible;
Platform::SystemInfo.processor.name = StringTable->insert("Unknown Processor, assuming x86 Compatible");
break;
}
// Now we can directly query the system about a litany of "Optional" processor capabilities
// and determine the status by using BOTH the 'err' value and the 'lraw' value. If we request
// a non-existant feature from SYSCTL(), the 'err' result will be -1; 0 denotes it exists
// >>>> BUT <<<<<
// it may not be supported, only defined. Thus we need to check 'lraw' to determine if it's
// actually supported/implemented by the processor: 0 = no, 1 = yes, others are undefined.
procflags = 0;
// Seriously this one should be an Assert()
err = _getSysCTLvalue<unsigned long>("hw.optional.floatingpoint", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_FPU;
// List of chip-specific features
err = _getSysCTLvalue<unsigned long>("hw.optional.mmx", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_MMX;
err = _getSysCTLvalue<unsigned long>("hw.optional.sse", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE;
err = _getSysCTLvalue<unsigned long>("hw.optional.sse2", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE2;
err = _getSysCTLvalue<unsigned long>("hw.optional.sse3", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE3;
err = _getSysCTLvalue<unsigned long>("hw.optional.supplementalsse3", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE3xt;
err = _getSysCTLvalue<unsigned long>("hw.optional.sse4_1", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE4_1;
err = _getSysCTLvalue<unsigned long>("hw.optional.sse4_2", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE4_2;
err = _getSysCTLvalue<unsigned long>("hw.optional.altivec", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_ALTIVEC;
// Finally some architecture-wide settings
err = _getSysCTLvalue<unsigned long>("hw.ncpu", &lraw);
if ((err==0)&&(lraw>1)) procflags |= CPU_PROP_MP;
err = _getSysCTLvalue<unsigned long>("hw.cpu64bit_capable", &lraw);
if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_64bit;
err = _getSysCTLvalue<unsigned long>("hw.byteorder", &lraw);
if ((err==0)&&(lraw==1234)) procflags |= CPU_PROP_LE;
Platform::SystemInfo.processor.properties = procflags;
Con::printf( "%s, %2.2f GHz", Platform::SystemInfo.processor.name, F32( Platform::SystemInfo.processor.mhz ) / 1000.0 );
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_SSE2)
Con::printf( " SSE2 detected");
if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE3)
Con::printf( " SSE3 detected");
if (Platform::SystemInfo.processor.properties & CPU_PROP_ALTIVEC)
Con::printf( " AltiVec detected");
Con::printf( "" );
// Trigger the signal
Platform::SystemInfoReady.trigger();
}

View file

@ -0,0 +1,940 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <utime.h>
#include <sys/time.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
// Get our GL header included before Apple's
#include "platformMac/platformMacCarb.h"
// Don't include Apple's
#define __gl_h_
#include "platform/tmm_off.h"
#include <Cocoa/Cocoa.h>
#include "platform/tmm_on.h"
#include "core/fileio.h"
#include "core/util/tVector.h"
#include "core/stringTable.h"
#include "core/strings/stringFunctions.h"
#include "console/console.h"
#include "platform/profiler.h"
#include "cinterface/cinterface.h";
//TODO: file io still needs some work...
#define MAX_MAC_PATH_LONG 2048
//-----------------------------------------------------------------------------
#if defined(TORQUE_OS_MAC)
#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()
{
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)
{
// if the path exists, we're done.
struct stat statData;
if( stat(file, &statData) == 0 )
{
return true; // exists, rejoice.
}
Con::warnf( "creating path %s", file );
// 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 to create 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;
}
#pragma mark ---- Directories ----
//-----------------------------------------------------------------------------
StringTableEntry Platform::getCurrentDirectory()
{
// get the current directory, the one that would be opened if we did a fopen(".")
char* cwd = getcwd(NULL, 0);
StringTableEntry ret = StringTable->insert(cwd);
free(cwd);
return ret;
}
//-----------------------------------------------------------------------------
bool Platform::setCurrentDirectory(StringTableEntry newDir)
{
return (chdir(newDir) == 0);
}
//-----------------------------------------------------------------------------
void Platform::openFolder(const char* path )
{
// TODO: users can still run applications by calling openfolder on an app bundle.
// this may be a bad thing.
if(!Platform::isDirectory(path))
{
Con::errorf(avar("Error: not a directory: %s",path));
return;
}
const char* arg = avar("open '%s'", path);
U32 ret = system(arg);
if(ret != 0)
Con::printf(strerror(errno));
}
void Platform::openFile(const char* path )
{
if( !Platform::isFile( path ) )
{
Con::errorf( avar( "Error: not a file: %s", path ) );
return;
}
const char* arg = avar( "open '%s'", path );
U32 ret = system( arg );
if( ret != 0 )
Con::printf( strerror( errno ) );
}
// helper func for getWorkingDirectory
bool isMainDotCsPresent(NSString* dir)
{
return [[NSFileManager defaultManager] fileExistsAtPath:[dir stringByAppendingPathComponent:@"main.cs"]] == YES;
}
//-----------------------------------------------------------------------------
/// 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::getExecutablePath()
{
static const char* cwd = NULL;
// this isn't actually being used due to some static constructors at bundle load time
// calling this method (before there is a chance to set it)
// for instance, FMOD sound provider (this should be fixed in FMOD as it is with windows)
if (!cwd && torque_getexecutablepath())
{
// we're in a plugin using the cinterface
cwd = torque_getexecutablepath();
chdir(cwd);
}
else if(!cwd)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//first check the cwd for main.cs
static char buf[4096];
NSString* currentDir = [[NSString alloc ] initWithCString:getcwd(buf,(4096 * sizeof(char))) ];
if (isMainDotCsPresent(currentDir))
{
cwd = buf;
[pool release];
return cwd;
}
NSString* string = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"cs"];
if(!string)
string = [[NSBundle mainBundle] bundlePath];
string = [string stringByDeletingLastPathComponent];
AssertISV(isMainDotCsPresent(string), "Platform::getExecutablePath - Failed to find main.cs!");
cwd = dStrdup([string UTF8String]);
chdir(cwd);
[pool release];
}
return cwd;
}
//-----------------------------------------------------------------------------
StringTableEntry Platform::getExecutableName()
{
static const char* name = NULL;
if(!name)
name = [[[[NSBundle mainBundle] bundlePath] lastPathComponent] UTF8String];
return name;
}
//-----------------------------------------------------------------------------
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)
{
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;
}
// TODO: implement stringToFileTime()
bool Platform::stringToFileTime(const char * string, FileTime * time) { return false;}
// TODO: implement fileTimeToString()
bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) { return false;}
//-----------------------------------------------------------------------------
#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;
const S32 depth = dAtoi(argv[2]);
const bool noBasePath = dAtob(argv[3]);
Platform::addExcludedDirectory(".svn");
Platform::dumpDirectories(argv[1], paths, depth, noBasePath);
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 = Platform::getFileTimes(argv[1], &create, &modify);
Con::printf("%s Platform::getFileTimes %i, %i", ok ? "+OK" : "-FAIL", create, modify);
return ok;
}
#endif

View file

@ -0,0 +1,449 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformMac/macCarbFont.h"
#include "platformMac/platformMacCarb.h"
#include "math/mRect.h"
#include "console/console.h"
#include "core/strings/unicode.h"
#include "core/stringTable.h"
#include "core/strings/stringFunctions.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)
{
String nameStr = name;
nameStr = nameStr.trim();
// 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;
bool isBold = false;
bool isItalic = false;
bool haveModifier;
do
{
haveModifier = false;
if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 )
{
isBold = true;
nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim();
haveModifier = true;
}
if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 )
{
isItalic = true;
nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim();
haveModifier = true;
}
}
while( haveModifier );
// Look up the font. We need it in 2 differnt formats, for differnt Apple APIs.
cfsName = CFStringCreateWithCString( kCFAllocatorDefault, nameStr.c_str(), kCFStringEncodingUTF8);
if(!cfsName)
Con::errorf("Error: could not make a cfstring out of \"%s\" ",nameStr.c_str());
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 );
if( isBold )
{
ATSUAttributeTag tag = kATSUQDBoldfaceTag;
ByteCount size = sizeof( Boolean );
Boolean value = true;
ATSUAttributeValuePtr valuePtr = &value;
ATSUSetAttributes( mStyle, 1, &tag, &size, &valuePtr );
}
if( isItalic )
{
ATSUAttributeTag tag = kATSUQDItalicTag;
ByteCount size = sizeof( Boolean );
Boolean value = true;
ATSUAttributeValuePtr valuePtr = &value;
ATSUSetAttributes( mStyle, 1, &tag, &size, &valuePtr );
}
// 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;
}
void PlatformFont::enumeratePlatformFonts( Vector< StringTableEntry >& fonts, UTF16* fontFamily )
{
if( fontFamily )
{
// Determine the font ID from the family name.
ATSUFontID fontID;
if( ATSUFindFontFromName(
fontFamily,
dStrlen( fontFamily ) * 2,
kFontFamilyName,
kFontMicrosoftPlatform,
kFontNoScriptCode,
kFontNoLanguageCode, &fontID ) != kATSUInvalidFontErr )
{
// Get the number of fonts in the family.
ItemCount numFonts;
ATSUCountFontNames( fontID, &numFonts );
// Read out font names.
U32 bufferSize = 512;
char* buffer = ( char* ) dMalloc( bufferSize );
for( U32 i = 0; i < numFonts; ++ i )
{
for( U32 n = 0; n < 2; ++ n )
{
ByteCount actualNameLength;
FontNameCode fontNameCode;
FontPlatformCode fontPlatformCode;
FontScriptCode fontScriptCode;
FontLanguageCode fontLanguageCode;
if( ATSUGetIndFontName(
fontID,
i,
bufferSize - 2,
buffer,
&actualNameLength,
&fontNameCode,
&fontPlatformCode,
&fontScriptCode,
&fontLanguageCode ) == noErr )
{
*( ( UTF16* ) &buffer[ actualNameLength ] ) = '\0';
char* utf8 = convertUTF16toUTF8( ( UTF16* ) buffer );
fonts.push_back( StringTable->insert( utf8 ) );
delete [] utf8;
break;
}
// Allocate larger buffer.
bufferSize = actualNameLength + 2;
buffer = ( char* ) dRealloc( buffer, bufferSize );
}
}
dFree( buffer );
}
}
else
{
// Get the number of installed fonts.
ItemCount numFonts;
ATSUFontCount( &numFonts );
// Get all the font IDs.
ATSUFontID* fontIDs = new ATSUFontID[ numFonts ];
if( ATSUGetFontIDs( fontIDs, numFonts, &numFonts ) == noErr )
{
U32 bufferSize = 512;
char* buffer = ( char* ) dMalloc( bufferSize );
// Read all family names.
for( U32 i = 0; i < numFonts; ++ i )
{
for( U32 n = 0; n < 2; ++ n )
{
ByteCount actualNameLength;
ItemCount fontIndex;
OSStatus result = ATSUFindFontName(
fontIDs[ i ],
kFontFamilyName,
kFontMicrosoftPlatform,
kFontNoScriptCode,
kFontNoLanguageCode,
bufferSize - 2,
buffer,
&actualNameLength,
&fontIndex );
if( result == kATSUNoFontNameErr )
break;
else if( result == noErr )
{
*( ( UTF16* ) &buffer[ actualNameLength ] ) = '\0';
char* utf8 = convertUTF16toUTF8( ( UTF16* ) buffer );
StringTableEntry name = StringTable->insert( utf8 );
delete [] utf8;
// Avoid duplicates.
bool duplicate = false;
for( U32 i = 0, num = fonts.size(); i < num; ++ i )
if( fonts[ i ] == name )
{
duplicate = true;
break;
}
if( !duplicate )
fonts.push_back( name );
break;
}
// Allocate larger buffer.
bufferSize = actualNameLength + 2;
buffer = ( char* ) dRealloc( buffer, bufferSize );
}
}
dFree( buffer );
}
delete [] fontIDs;
}
}
//-----------------------------------------------------------------------------
// 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,74 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#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;
}

View file

@ -0,0 +1,500 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
#include "platform/platformInput.h"
#include "console/console.h"
#include "core/strings/unicode.h"
#include "core/util/tVector.h"
// Static class variables:
InputManager* Input::smManager;
bool Input::smActive;
U8 Input::smModifierKeys;
InputEvent Input::smInputEvent;
//-----------------------------------------------------------------------------
// Keycode mapping.
struct KeyCode
{
U32 mKeyCode;
UniChar mCharLower;
UniChar mCharUpper;
KeyCode( U32 keyCode )
: mKeyCode( keyCode ) {}
};
static KeyCode sOSToKeyCode[] =
{
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
0, // 0x0A
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
KEY_RALT, // 0x36
KEY_LALT, // 0x37
KEY_LSHIFT, // 0x38
KEY_CAPSLOCK, // 0x39
KEY_MAC_LOPT, // 0x3A
KEY_LCONTROL, // 0x3B
KEY_RSHIFT, // 0x3C
KEY_MAC_ROPT, // 0x3D
KEY_RCONTROL, // 0x3E
0, // 0x3F
0, // 0x40
KEY_DECIMAL, // 0x41
0, // 0x42
KEY_MULTIPLY, // 0x43
0, // 0x44
KEY_ADD, // 0x45
0, // 0x46
KEY_NUMLOCK, // 0x47
0, // 0x48
0, // 0x49
0, // 0x4A
KEY_DIVIDE, // 0x4B
KEY_NUMPADENTER, // 0x4C
0, // 0x4D
KEY_SUBTRACT, // 0x4E
0, // 0x4F
0, // 0x50
KEY_SEPARATOR, // 0x51
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_F13, // 0x69
KEY_F16, // 0x6A
KEY_F14, // 0x6B
0, // 0x6C
KEY_F10, // 0x6D
0, // 0x6E
KEY_F12, // 0x6F
0, // 0x70
KEY_F15, // 0x71
KEY_INSERT, // 0x72
KEY_HOME, // 0x73
KEY_PAGE_UP, // 0x74
KEY_DELETE, // 0x75
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
};
static Vector< U8 > sKeyCodeToOS( __FILE__, __LINE__ );
#define NSShiftKeyMask ( 1 << 17 )
static KeyboardLayoutRef sKeyLayout;
static SInt32 sKeyLayoutKind = -1;
static SInt32 sKeyLayoutID = -1;
static SInt32 sLastKeyLayoutID = -1;
static void GetKeyboardLayout()
{
KLGetCurrentKeyboardLayout( &sKeyLayout );
KLGetKeyboardLayoutProperty( sKeyLayout, kKLKind, ( const void** ) &sKeyLayoutKind );
KLGetKeyboardLayoutProperty( sKeyLayout, kKLIdentifier, ( const void** ) &sKeyLayoutID );
}
static bool KeyboardLayoutHasChanged()
{
GetKeyboardLayout();
return ( sKeyLayoutID != sLastKeyLayoutID );
}
static UniChar OSKeyCodeToUnicode( UInt16 osKeyCode, bool shift = false )
{
// Translate the key code.
UniChar uniChar = 0;
if( sKeyLayoutKind == kKLKCHRKind )
{
// KCHR mapping.
void* KCHRData;
KLGetKeyboardLayoutProperty( sKeyLayout, kKLKCHRData, ( const void** ) & KCHRData );
UInt16 key = ( osKeyCode & 0x7f );
if( shift )
key |= NSShiftKeyMask;
UInt32 keyTranslateState = 0;
UInt32 charCode = KeyTranslate( KCHRData, key, &keyTranslateState );
charCode &= 0xff;
if( keyTranslateState == 0 && charCode )
uniChar = charCode;
}
else
{
// UCHR mapping.
UCKeyboardLayout* uchrData;
KLGetKeyboardLayoutProperty( sKeyLayout, kKLuchrData, ( const void** ) &uchrData );
UInt32 deadKeyState;
UniCharCount actualStringLength;
UniChar unicodeString[ 4 ];
UCKeyTranslate( uchrData,
osKeyCode,
kUCKeyActionDown,
( shift ? 0x02 : 0 ), // Oh yeah... Apple docs are fun...
LMGetKbdType(),
0,
&deadKeyState,
sizeof( unicodeString ) / sizeof( unicodeString[ 0 ] ),
&actualStringLength,
unicodeString );
if( actualStringLength )
uniChar = unicodeString[ 0 ]; // Well, Unicode is something else, but...
}
return uniChar;
}
static void InitKeyCodeMapping()
{
const U32 numOSKeyCodes = sizeof( sOSToKeyCode ) / sizeof( sOSToKeyCode[ 0 ] );
GetKeyboardLayout();
sLastKeyLayoutID = sKeyLayoutID;
U32 maxKeyCode = 0;
for( U32 i = 0; i < numOSKeyCodes; ++ i )
{
sOSToKeyCode[ i ].mCharLower = OSKeyCodeToUnicode( i, false );
sOSToKeyCode[ i ].mCharUpper = OSKeyCodeToUnicode( i, true );
if( sOSToKeyCode[ i ].mKeyCode > maxKeyCode )
maxKeyCode = sOSToKeyCode[ i ].mKeyCode;
}
if( !sKeyCodeToOS.size() )
{
sKeyCodeToOS.setSize( maxKeyCode + 1 );
dMemset( sKeyCodeToOS.address(), 0, sKeyCodeToOS.size() );
for( U32 i = 0; i < numOSKeyCodes; ++ i )
sKeyCodeToOS[ sOSToKeyCode[ i ].mKeyCode ] = i;
}
}
U8 TranslateOSKeyCode(U8 macKeycode)
{
AssertWarn(macKeycode < sizeof(sOSToKeyCode) / sizeof(sOSToKeyCode[0]), avar("TranslateOSKeyCode - could not translate code %i", macKeycode));
if(macKeycode >= sizeof(sOSToKeyCode) / sizeof(sOSToKeyCode[0]))
return KEY_NULL;
return sOSToKeyCode[ macKeycode ].mKeyCode;
}
U8 TranslateKeyCodeToOS( U8 keycode )
{
return sKeyCodeToOS[ keycode ];
}
#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);
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;
}
void Input::init()
{
smManager = NULL;
smActive = false;
InitKeyCodeMapping();
}
U16 Input::getKeyCode( U16 asciiCode )
{
if( KeyboardLayoutHasChanged() )
InitKeyCodeMapping();
for( U32 i = 0; i < ( sizeof( sOSToKeyCode ) / sizeof( sOSToKeyCode[ 0 ] ) ); ++ i )
if( sOSToKeyCode[ i ].mCharLower == asciiCode
|| sOSToKeyCode[ i ].mCharUpper == asciiCode )
return sOSToKeyCode[ i ].mKeyCode;
return 0;
}
U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
{
GetKeyboardLayout();
return OSKeyCodeToUnicode( TranslateKeyCodeToOS( keyCode ), ( keyState == STATE_UPPER ? true : false ) );
}
void Input::destroy()
{
}
bool Input::enable()
{
return true;
}
void Input::disable()
{
}
void Input::activate()
{
}
void Input::deactivate()
{
}
bool Input::isEnabled()
{
return true;
}
bool Input::isActive()
{
return true;
}
void Input::process()
{
}
InputManager* Input::getManager()
{
return smManager;
}
ConsoleFunction( enableMouse, bool, 1, 1, "enableMouse()" )
{
return true;
}
ConsoleFunction( disableMouse, void, 1, 1, "disableMouse()" )
{
}
ConsoleFunction( echoInputState, void, 1, 1, "echoInputState()" )
{
}
ConsoleFunction( toggleInputState, void, 1, 1, "toggleInputState()" )
{
}
ConsoleFunction( isJoystickDetected, bool, 1, 1, "Always false on the MAC." )
{
return false;
}
ConsoleFunction( getJoystickAxes, const char*, 2, 2, "(handle instance)" )
{
return "";
}
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.
}

View file

@ -0,0 +1,123 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformMac/platformMacCarb.h"
#include "platform/platform.h"
#include "console/console.h"
#include "math/mMath.h"
#include "core/strings/stringFunctions.h"
extern void mInstallLibrary_C();
extern void mInstallLibrary_Vec();
extern void mInstall_Library_SSE();
static MRandomLCG sgPlatRandom;
U32 Platform::getMathControlState()
{
return 0;
}
void Platform::setMathControlStateKnown()
{
}
void Platform::setMathControlState(U32 state)
{
}
//--------------------------------------
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 defined(__VEC__)
if (properties & CPU_PROP_ALTIVEC)
{
Con::printf(" Installing Altivec extensions");
mInstallLibrary_Vec();
}
#endif
#ifdef TORQUE_CPU_X86
if( properties & CPU_PROP_SSE )
{
Con::printf( " Installing SSE extensions" );
mInstall_Library_SSE();
}
#endif
Con::printf(" ");
}
//------------------------------------------------------------------------------
F32 Platform::getRandom()
{
return sgPlatRandom.randF();
}

View file

@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include <stdlib.h>
#include <string.h>
#include <mm_malloc.h>
//--------------------------------------
void* dRealMalloc(dsize_t in_size)
{
return malloc(in_size);
}
//--------------------------------------
void dRealFree(void* in_pFree)
{
free(in_pFree);
}
void *dMalloc_aligned(dsize_t in_size, int alignment)
{
return _mm_malloc(in_size, alignment);
}
void dFree_aligned(void* p)
{
return _mm_free(p);
}
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,97 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include "platform/platform.h"
#include "platform/threads/mutex.h"
#include "platform/threads/thread.h"
// TODO: examine & dump errno if pthread_* funcs fail. ( only in debug build )
class PlatformMutexData
{
public:
pthread_mutex_t mMutex;
bool locked;
U32 lockedByThread;
};
Mutex::Mutex(void)
{
int ok;
mData = new PlatformMutexData;
pthread_mutexattr_t attr;
ok = pthread_mutexattr_init(&attr);
ok = pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
ok = pthread_mutex_init(&(mData->mMutex),&attr);
AssertFatal(ok == 0, "Mutex() failed: pthread_mutex_init() failed.");
mData->locked = false;
mData->lockedByThread = 0;
}
Mutex::~Mutex()
{
int ok;
ok = pthread_mutex_destroy( &(mData->mMutex) );
AssertFatal(ok == 0, "~Mutex() failed: pthread_mutex_destroy() failed.");
delete mData;
}
bool Mutex::lock( bool block)
{
int ok;
if(block)
{
ok = pthread_mutex_lock( &(mData->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( &(mData->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.");
}
mData->locked = true;
mData->lockedByThread = ThreadManager::getCurrentThreadId();
return true;
}
void Mutex::unlock()
{
int ok;
ok = pthread_mutex_unlock( &(mData->mMutex) );
AssertFatal( ok == 0, "Mutex::unlockMutex() failed: pthread_mutex_unlock() failed.");
mData->locked = false;
mData->lockedByThread = 0;
}

View file

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformMac/platformMacCarb.h"
#include "platform/event.h"
#include "core/util/journal/process.h"
#include "console/console.h"
void Platform::postQuitMessage(const U32 in_quitVal)
{
Process::requestShutdown();
}
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.
// 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,68 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <CoreServices/CoreServices.h>
#include "platform/platform.h"
#include "platform/threads/semaphore.h"
class PlatformSemaphore
{
public:
MPSemaphoreID mSemaphore;
PlatformSemaphore(S32 initialCount)
{
OSStatus err = MPCreateSemaphore(S32_MAX - 1, initialCount, &mSemaphore);
AssertFatal(err == noErr, "Failed to allocate semaphore!");
}
~PlatformSemaphore()
{
OSStatus err = MPDeleteSemaphore(mSemaphore);
AssertFatal(err == noErr, "Failed to destroy semaphore!");
}
};
Semaphore::Semaphore(S32 initialCount)
{
mData = new PlatformSemaphore(initialCount);
}
Semaphore::~Semaphore()
{
AssertFatal(mData && mData->mSemaphore, "Semaphore::destroySemaphore: invalid semaphore");
delete mData;
}
bool Semaphore::acquire( bool block, S32 timeoutMS )
{
AssertFatal(mData && mData->mSemaphore, "Semaphore::acquireSemaphore: invalid semaphore");
OSStatus err = MPWaitOnSemaphore(mData->mSemaphore, block ? ( timeoutMS == -1 ? kDurationForever : timeoutMS ) : kDurationImmediate);
return(err == noErr);
}
void Semaphore::release()
{
AssertFatal(mData && mData->mSemaphore, "Semaphore::releaseSemaphore: invalid semaphore");
OSStatus err = MPSignalSemaphore(mData->mSemaphore);
AssertFatal(err == noErr, "Failed to release semaphore!");
}

View file

@ -0,0 +1,69 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "core/strings/stringFunctions.h"
char *dStrnew(const char *src)
{
char *buffer = new char[dStrlen(src) + 1];
dStrcpy(buffer, src);
return buffer;
}
char* dStrstr(char *str1, char *str2)
{
return strstr(str1,str2);
}
int dSprintf(char *buffer, dsize_t /*bufferSize*/, const char *format, ...)
{
va_list args;
va_start(args, format);
S32 len = vsprintf(buffer, format, args);
return (len);
}
int dVsprintf(char *buffer, dsize_t /*bufferSize*/, const char *format, void *arglist)
{
S32 len = vsprintf(buffer, format, (char*)arglist);
return (len);
}
int dFflushStdout()
{
return fflush(stdout);
}
int dFflushStderr()
{
return fflush(stderr);
}

View file

@ -0,0 +1,159 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <pthread.h>
#include "platform/threads/thread.h"
#include "platform/threads/semaphore.h"
#include "platform/threads/mutex.h"
#include <stdlib.h>
class PlatformThreadData
{
public:
ThreadRunFunction mRunFunc;
void* mRunArg;
Thread* mThread;
Semaphore mGateway; // default count is 1
U32 mThreadID;
bool mDead;
};
ThreadManager::MainThreadId ThreadManager::smMainThreadId;
//-----------------------------------------------------------------------------
// 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)
{
PlatformThreadData *mData = reinterpret_cast<PlatformThreadData*>(arg);
Thread *thread = mData->mThread;
// mThreadID is filled in twice, once here and once in pthread_create().
// We fill in mThreadID here as well as in pthread_create() because addThread()
// can execute before pthread_create() returns and sets mThreadID.
// The value from pthread_create() and pthread_self() are guaranteed to be equivalent (but not identical)
mData->mThreadID = ThreadManager::getCurrentThreadId();
ThreadManager::addThread(thread);
thread->run(mData->mRunArg);
ThreadManager::removeThread(thread);
bool autoDelete = thread->autoDelete;
mData->mThreadID = 0;
mData->mDead = true;
mData->mGateway.release();
if( autoDelete )
delete thread;
// return value for pthread lib's benefit
return NULL;
// the end of this function is where the created pthread will die.
}
//-----------------------------------------------------------------------------
Thread::Thread(ThreadRunFunction func, void* arg, bool start_thread, bool autodelete)
{
AssertFatal( !start_thread, "Thread::Thread() - auto-starting threads from ctor has been disallowed since the run() method is virtual" );
mData = new PlatformThreadData;
mData->mRunFunc = func;
mData->mRunArg = arg;
mData->mThread = this;
mData->mThreadID = 0;
mData->mDead = false;
autoDelete = autodelete;
}
Thread::~Thread()
{
stop();
if( isAlive() )
join();
delete mData;
}
void Thread::start( void* arg )
{
// cause start to block out other pthreads from using this Thread,
// at least until ThreadRunHandler exits.
mData->mGateway.acquire();
// reset the shouldStop flag, so we'll know when someone asks us to stop.
shouldStop = false;
mData->mDead = false;
if( !mData->mRunArg )
mData->mRunArg = arg;
pthread_create((pthread_t*)(&mData->mThreadID), NULL, ThreadRunHandler, mData);
}
bool Thread::join()
{
// not using pthread_join here because pthread_join cannot deal
// with multiple simultaneous calls.
mData->mGateway.acquire();
AssertFatal( !isAlive(), "Thread::join() - thread not dead after join()" );
mData->mGateway.release();
return true;
}
void Thread::run(void* arg)
{
if(mData->mRunFunc)
mData->mRunFunc(arg);
}
bool Thread::isAlive()
{
return ( !mData->mDead );
}
U32 Thread::getId()
{
return mData->mThreadID;
}
void Thread::_setName( const char* )
{
// Not supported. Wading through endless lists of Thread-1, Thread-2, Thread-3, ... trying to find
// that one thread you are looking for is just so much fun.
}
U32 ThreadManager::getCurrentThreadId()
{
return (U32)pthread_self();
}
bool ThreadManager::compare(U32 threadId_1, U32 threadId_2)
{
return pthread_equal((_opaque_pthread_t*)threadId_1, (_opaque_pthread_t*)threadId_2);
}

View file

@ -0,0 +1,143 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <CoreServices/CoreServices.h>
#include "platform/platformTimer.h"
#include <time.h>
#include <unistd.h>
//--------------------------------------
static U32 sgCurrentTime = 0;
//--------------------------------------
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;
}
String Platform::localTimeToString( const LocalTime &lt )
{
tm systime;
systime.tm_sec = lt.sec;
systime.tm_min = lt.min;
systime.tm_hour = lt.hour;
systime.tm_mon = lt.month;
systime.tm_mday = lt.monthday;
systime.tm_wday = lt.weekday;
systime.tm_year = lt.year;
systime.tm_yday = lt.yearday;
systime.tm_isdst = lt.isdst;
return asctime( &systime );
}
/// 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 milliseconds 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 sgCurrentTime;
}
void Platform::advanceTime(U32 delta)
{
sgCurrentTime += 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 );
}
//----------------------------------------------------------------------------------
PlatformTimer* PlatformTimer::create()
{
return new DefaultPlatformTimer;
}
void Platform::fileToLocalTime(const FileTime & ft, LocalTime * lt)
{
if(!lt)
return;
time_t long_time = ft;
struct tm systime;
/// 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;
}

View file

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <ApplicationServices/ApplicationServices.h>
#include <CoreServices/CoreServices.h>
#include "platform/platform.h"
#include "core/strings/stringFunctions.h"
void Platform::outputDebugString( const char *string, ... )
{
#ifdef TORQUE_DEBUG
char buffer[ 2048 ];
va_list args;
va_start( args, string );
dVsprintf( buffer, sizeof( buffer ), string, args );
va_end( args );
U32 length = strlen( buffer );
if( length == ( sizeof( buffer ) - 1 ) )
length --;
buffer[ length ] = '\n';
buffer[ length + 1 ] = '\0';
fputs( buffer, stderr );
fflush(stderr);
#endif
}
#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);
return(err==noErr);
}

View file

@ -0,0 +1,189 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <CoreServices/CoreServices.h>
#include "platform/platform.h"
#include "platformMac/macCarbVolume.h"
#include "platform/platformVolume.h"
#include "console/console.h"
//#define DEBUG_SPEW
struct MacFileSystemChangeNotifier::Event
{
FSEventStreamRef mStream;
Torque::Path mDir;
bool mHasChanged;
};
static void fsNotifyCallback(
ConstFSEventStreamRef stream,
void* callbackInfo,
size_t numEvents,
void* eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[] )
{
MacFileSystemChangeNotifier::Event* event =
reinterpret_cast< MacFileSystemChangeNotifier::Event* >( callbackInfo );
// Defer handling this to internalProcessOnce() so we stay in
// line with how the volume system expects notifications to
// be reported.
event->mHasChanged = true;
}
//-----------------------------------------------------------------------------
// Change notifications.
//-----------------------------------------------------------------------------
MacFileSystemChangeNotifier::MacFileSystemChangeNotifier( MacFileSystem* fs )
: Parent( fs )
{
VECTOR_SET_ASSOCIATION( mEvents );
}
MacFileSystemChangeNotifier::~MacFileSystemChangeNotifier()
{
for( U32 i = 0, num = mEvents.size(); i < num; ++ i )
{
FSEventStreamStop( mEvents[ i ]->mStream );
FSEventStreamInvalidate( mEvents[ i ]->mStream );
FSEventStreamRelease( mEvents[ i ]->mStream );
SAFE_DELETE( mEvents[ i ] );
}
}
void MacFileSystemChangeNotifier::internalProcessOnce()
{
for( U32 i = 0; i < mEvents.size(); ++ i )
if( mEvents[ i ]->mHasChanged )
{
// Signal the change.
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[MacFileSystemChangeNotifier] Directory %i changed: '%s'",
i + 1, mEvents[ i ]->mDir.getFullPath().c_str() );
#endif
internalNotifyDirChanged( mEvents[ i ]->mDir );
mEvents[i ]->mHasChanged = false;
}
}
bool MacFileSystemChangeNotifier::internalAddNotification( const Torque::Path& dir )
{
// Map the path.
Torque::Path fullFSPath = mFS->mapTo( dir );
String osPath = PathToOS( fullFSPath );
// Create event stream.
Event* event = new Event;
CFStringRef path = CFStringCreateWithCharacters( NULL, osPath.utf16(), osPath.numChars() );
CFArrayRef paths = CFArrayCreate( NULL, ( const void** ) &path, 1, NULL );
FSEventStreamRef stream;
CFAbsoluteTime latency = 3.f;
FSEventStreamContext context;
dMemset( &context, 0, sizeof( context ) );
context.info = event;
stream = FSEventStreamCreate(
NULL,
&fsNotifyCallback,
&context,
paths,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagNone
);
event->mStream = stream;
event->mDir = dir;
event->mHasChanged = false;
mEvents.push_back( event );
// Put it in the run loop and start the stream.
FSEventStreamScheduleWithRunLoop( stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
FSEventStreamStart( stream );
CFRelease( path );
CFRelease( paths );
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[MacFileSystemChangeNotifier] Added change notification %i to '%s' (full path: %s)",
mEvents.size(), dir.getFullPath().c_str(), osPath.c_str() );
#endif
return true;
}
bool MacFileSystemChangeNotifier::internalRemoveNotification( const Torque::Path& dir )
{
for( U32 i = 0, num = mEvents.size(); i < num; ++ i )
if( mEvents[ i ]->mDir == dir )
{
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[MacFileSystemChangeNotifier] Removing change notification %i from '%s'",
i + 1, dir.getFullPath().c_str() );
#endif
FSEventStreamStop( mEvents[ i ]->mStream );
FSEventStreamInvalidate( mEvents[ i ]->mStream );
FSEventStreamRelease( mEvents[ i ]->mStream );
SAFE_DELETE( mEvents[ i ] );
mEvents.erase( i );
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Platform API.
//-----------------------------------------------------------------------------
Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume )
{
return new MacFileSystem( volume );
}
bool Torque::FS::VerifyWriteAccess(const Torque::Path &path)
{
return true;
}

View file

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MACCARBVOLUME_H_
#define _MACCARBVOLUME_H_
#ifndef _POSIXVOLUME_H_
#include "platformPOSIX/posixVolume.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class MacFileSystem;
/// File system change notifications on Mac.
class MacFileSystemChangeNotifier : public Torque::FS::FileSystemChangeNotifier
{
public:
typedef Torque::FS::FileSystemChangeNotifier Parent;
struct Event;
protected:
/// Array of FSEventStream events set up. Uses pointers to dynamically
/// allocated memory rather than allocating inline with the array to be
/// able to pass around pointers without colliding with array reallocation.
Vector< Event* > mEvents;
// FileSystemChangeNotifier.
virtual void internalProcessOnce();
virtual bool internalAddNotification( const Torque::Path& dir );
virtual bool internalRemoveNotification( const Torque::Path& dir );
public:
MacFileSystemChangeNotifier( MacFileSystem* fs );
virtual ~MacFileSystemChangeNotifier();
};
/// Mac filesystem.
class MacFileSystem : public Torque::Posix::PosixFileSystem
{
public:
typedef Torque::Posix::PosixFileSystem Parent;
MacFileSystem( String volume )
: Parent( volume )
{
mChangeNotifier = new MacFileSystemChangeNotifier( this );
}
};
#endif // !_MACCARBVOLUME_H_

View file

@ -0,0 +1,623 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// Get our GL header included before Apple's
#include "platformMac/platformMacCarb.h"
// Don't include Apple's
#define __gl_h_
#include "platform/tmm_off.h"
#include <Cocoa/Cocoa.h>
#include "platform/tmm_on.h"
#include "console/simBase.h"
#include "platform/nativeDialogs/fileDialog.h"
#include "platform/threads/mutex.h"
#include "core/util/safeDelete.h"
#include "math/mMath.h"
#include "core/strings/unicode.h"
#include "console/consoleTypes.h"
#include "platform/threads/thread.h"
#include "platform/threads/semaphore.h"
class FileDialogOpaqueData
{
public:
Semaphore *sem;
FileDialogOpaqueData() { sem = new Semaphore(0); }
~FileDialogOpaqueData() { delete sem; }
};
class FileDialogFileExtList
{
public:
Vector<UTF8*> list;
UTF8* data;
FileDialogFileExtList(const char* exts) { data = dStrdup(exts); }
~FileDialogFileExtList() { SAFE_DELETE(data); }
};
class FileDialogFileTypeList
{
public:
UTF8* filterData;
Vector<UTF8*> names;
Vector<FileDialogFileExtList*> exts;
bool any;
FileDialogFileTypeList(const char* filter) { filterData = dStrdup(filter); any = false;}
~FileDialogFileTypeList()
{
SAFE_DELETE(filterData);
for(U32 i = 0; i < exts.size(); i++)
delete exts[i];
}
};
#undef new
//-----------------------------------------------------------------------------
// PlatformFileDlgData Implementation
//-----------------------------------------------------------------------------
FileDialogData::FileDialogData()
{
// Default Path
//
// Try to provide consistent experience by recalling the last file path
// - else
// Default to Working Directory if last path is not set or is invalid
mDefaultPath = StringTable->insert( Con::getVariable("Tools::FileDialogs::LastFilePath") );
if( mDefaultPath == StringTable->lookup("") || !Platform::isDirectory( mDefaultPath ) )
mDefaultPath = Platform::getCurrentDirectory();
mDefaultFile = StringTable->insert("");
mFilters = StringTable->insert("");
mFile = StringTable->insert("");
mTitle = StringTable->insert("");
mStyle = 0;
mOpaqueData = new FileDialogOpaqueData();
}
FileDialogData::~FileDialogData()
{
delete mOpaqueData;
}
//-----------------------------------------------------------------------------
// FileDialog Implementation
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(FileDialog);
FileDialog::FileDialog() : mData()
{
// Default to File Must Exist Open Dialog style
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST;
mChangePath = false;
}
FileDialog::~FileDialog()
{
}
void FileDialog::initPersistFields()
{
// why is this stuff buried in another class?
addProtectedField("DefaultPath", TypeString, Offset(mData.mDefaultPath, FileDialog), &setDefaultPath, &defaultProtectedGetFn, "Default Path when Dialog is shown");
addProtectedField("DefaultFile", TypeString, Offset(mData.mDefaultFile, FileDialog), &setDefaultFile, &defaultProtectedGetFn, "Default File when Dialog is shown");
addProtectedField("FileName", TypeString, Offset(mData.mFile, FileDialog), &setFile, &defaultProtectedGetFn, "Default File when Dialog is shown");
addProtectedField("Filters", TypeString, Offset(mData.mFilters, FileDialog), &setFilters, &defaultProtectedGetFn, "Default File when Dialog is shown");
addField("Title", TypeString, Offset(mData.mTitle, FileDialog), "Default File when Dialog is shown");
addProtectedField("ChangePath", TypeBool, Offset(mChangePath, FileDialog), &setChangePath, &getChangePath, "True/False whether to set the working directory to the directory returned by the dialog" );
Parent::initPersistFields();
}
static FileDialogFileExtList* _MacCarbGetFileExtensionsFromString(const char* filter)
{
FileDialogFileExtList* list = new FileDialogFileExtList(filter);
char* token = list->data;
char* place = list->data;
for( ; *place; place++)
{
if(*place != ';')
continue;
*place = '\0';
list->list.push_back(token);
++place;
token = place;
}
// last token
list->list.push_back(token);
return list;
}
static FileDialogFileTypeList* _MacCarbGetFileTypesFromString(const char* filter)
{
FileDialogFileTypeList &list = *(new FileDialogFileTypeList(filter));
char* token = list.filterData;
char* place = list.filterData;
// scan the filter list until we hit a null.
// when we see the separator '|', replace it with a null, and save the token
// format is description|extension|description|extension
bool isDesc = true;
for( ; *place; place++)
{
if(*place != '|')
continue;
*place = '\0';
if(isDesc)
list.names.push_back(token);
else
{
// detect *.*
if(dStrstr((const char*)token, "*.*"))
list.any = true;
list.exts.push_back(_MacCarbGetFileExtensionsFromString(token));
}
isDesc = !isDesc;
++place;
token = place;
}
list.exts.push_back(_MacCarbGetFileExtensionsFromString(token));
return &list;
}
static NSArray* _MacCocoaCreateAndRunSavePanel(FileDialogData &mData)
{
NSSavePanel* panel = [NSSavePanel savePanel];
// User freedom niceties
[panel setCanCreateDirectories:YES];
[panel setCanSelectHiddenExtension:YES];
[panel setTreatsFilePackagesAsDirectories:YES];
NSString *initialFile = [[NSString stringWithUTF8String:mData.mDefaultFile] lastPathComponent];
// we only use mDefaultDir if mDefault path is not set.
NSString *dir;
if(dStrlen(mData.mDefaultPath) < 1)
dir = [[NSString stringWithUTF8String:mData.mDefaultFile] stringByDeletingLastPathComponent];
else
dir = [NSString stringWithUTF8String: mData.mDefaultPath];
[panel setDirectory:dir];
// todo: move file type handling to an accessory view.
// parse file types
FileDialogFileTypeList *fTypes = _MacCarbGetFileTypesFromString(mData.mFilters);
// fill an array with the possible file types
NSMutableArray* types = [NSMutableArray arrayWithCapacity:10];
for(U32 i = 0; i < fTypes->exts.size(); i++)
{
for(U32 j = 0; j < fTypes->exts[i]->list.size(); j++)
{
char* ext = fTypes->exts[i]->list[j];
if(ext)
{
if(dStrlen(ext) == 0)
continue;
if(dStrncmp(ext, "*.*", 3) == 0)
continue;
if(dStrncmp(ext, "*.", 2) == 0)
ext+=2;
[types addObject:[NSString stringWithUTF8String:ext]];
}
}
}
if([types count] > 0)
[panel setAllowedFileTypes:types];
// if any file type was *.*, user may select any file type.
[panel setAllowsOtherFileTypes:fTypes->any];
//---------------------------------------------------------------------------
// Display the panel, enter a modal loop. This blocks.
//---------------------------------------------------------------------------
U32 button = [panel runModalForDirectory:dir file:initialFile];
// return the file name
NSMutableArray *array = [NSMutableArray arrayWithCapacity:10];
if(button != NSFileHandlingPanelCancelButton)
[array addObject:[panel filename]];
delete fTypes;
return array;
// TODO: paxorr: show as sheet
// crashes when we try to display the window as a sheet. Not sure why.
// the sheet is instantly dismissed, and crashes as it's dismissing itself.
// here's the code snippet to get an nswindow from our carbon WindowRef
//NSWindow *nsAppWindow = [[NSWindow alloc] initWithWindowRef:platState.appWindow];
}
NSArray* _MacCocoaCreateAndRunOpenPanel(FileDialogData &mData)
{
NSOpenPanel* panel = [NSOpenPanel openPanel];
// User freedom niceties
[panel setCanCreateDirectories:YES];
[panel setCanSelectHiddenExtension:YES];
[panel setTreatsFilePackagesAsDirectories:YES];
[panel setAllowsMultipleSelection:(mData.mStyle & FileDialogData::FDS_MULTIPLEFILES)];
//
bool chooseDir = (mData.mStyle & FileDialogData::FDS_BROWSEFOLDER);
[panel setCanChooseFiles: !chooseDir ];
[panel setCanChooseDirectories: chooseDir ];
if(chooseDir)
{
[panel setPrompt:@"Choose"];
[panel setTitle:@"Choose Folder"];
}
NSString *initialFile = [[NSString stringWithUTF8String:mData.mDefaultFile] lastPathComponent];
// we only use mDefaultDir if mDefault path is not set.
NSString *dir;
if(dStrlen(mData.mDefaultPath) < 1)
dir = [[NSString stringWithUTF8String:mData.mDefaultFile] stringByDeletingLastPathComponent];
else
dir = [NSString stringWithUTF8String: mData.mDefaultPath];
[panel setDirectory:dir];
// todo: move file type handling to an accessory view.
// parse file types
FileDialogFileTypeList *fTypes = _MacCarbGetFileTypesFromString(mData.mFilters);
// fill an array with the possible file types
NSMutableArray* types = [NSMutableArray arrayWithCapacity:10];
for(U32 i = 0; i < fTypes->exts.size(); i++)
{
for(U32 j = 0; j < fTypes->exts[i]->list.size(); j++)
{
char* ext = fTypes->exts[i]->list[j];
if(ext)
{
if(dStrncmp(ext, "*.", 2) == 0)
ext+=2;
[types addObject:[NSString stringWithUTF8String:ext]];
}
}
}
if([types count] > 0)
[panel setAllowedFileTypes:types];
// if any file type was *.*, user may select any file type.
[panel setAllowsOtherFileTypes:fTypes->any];
//---------------------------------------------------------------------------
// Display the panel, enter a modal loop. This blocks.
//---------------------------------------------------------------------------
U32 button = [panel runModalForDirectory:dir file:initialFile ];
// return the file name
NSMutableArray *array = [NSMutableArray arrayWithCapacity:10];
if(button != NSFileHandlingPanelCancelButton)
[array addObject:[panel filename]];
delete fTypes;
return array;
}
void MacCarbShowDialog(void* dialog)
{
FileDialog* d = static_cast<FileDialog*>(dialog);
d->Execute();
}
//
// Execute Method
//
bool FileDialog::Execute()
{
// if(! ThreadManager::isCurrentThread(platState.firstThreadId))
// {
// MacCarbSendTorqueEventToMain(kEventTorqueModalDialog,this);
// mData.mOpaqueData->sem->acquire();
// return;
// }
NSArray* nsFileArray;
if(mData.mStyle & FileDialogData::FDS_OPEN)
nsFileArray = _MacCocoaCreateAndRunOpenPanel(mData);
else if(mData.mStyle & FileDialogData::FDS_SAVE)
nsFileArray = _MacCocoaCreateAndRunSavePanel(mData);
else
{
Con::errorf("Bad File Dialog Setup.");
mData.mOpaqueData->sem->release();
return false;
}
if([nsFileArray count] == 0)
return false;
if(! (mData.mStyle & FileDialogData::FDS_MULTIPLEFILES) && [nsFileArray count] >= 1)
{
const UTF8* f = [(NSString*)[nsFileArray objectAtIndex:0] UTF8String];
mData.mFile = StringTable->insert(f);
}
else
{
for(U32 i = 0; i < [nsFileArray count]; i++)
{
const UTF8* f = [(NSString*)[nsFileArray objectAtIndex:i] UTF8String];
setDataField(StringTable->insert("files"), Con::getIntArg(i), StringTable->insert(f));
}
setDataField(StringTable->insert("fileCount"), NULL, Con::getIntArg([nsFileArray count]));
}
mData.mOpaqueData->sem->release();
return true;
}
ConsoleMethod( FileDialog, Execute, bool, 2, 2, "%fileDialog.Execute();" )
{
return object->Execute();
}
//-----------------------------------------------------------------------------
// Dialog Filters
//-----------------------------------------------------------------------------
bool FileDialog::setFilters(void* obj, const char* index, const char* data)
{
// Will do validate on write at some point.
if( !data )
return true;
return true;
};
//-----------------------------------------------------------------------------
// Default Path Property - String Validated on Write
//-----------------------------------------------------------------------------
bool FileDialog::setDefaultPath(void* obj, const char* index, const char* data)
{
if( !data )
return true;
return true;
};
//-----------------------------------------------------------------------------
// Default File Property - String Validated on Write
//-----------------------------------------------------------------------------
bool FileDialog::setDefaultFile(void* obj, const char* index, const char* data)
{
if( !data )
return true;
return true;
};
//-----------------------------------------------------------------------------
// ChangePath Property - Change working path on successful file selection
//-----------------------------------------------------------------------------
bool FileDialog::setChangePath(void* obj, const char* index, const char* data)
{
bool bChangePath = dAtob( data );
FileDialog *pDlg = static_cast<FileDialog*>( obj );
if( bChangePath )
pDlg->mData.mStyle |= FileDialogData::FDS_CHANGEPATH;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_CHANGEPATH;
return true;
};
const char* FileDialog::getChangePath(void* obj, const char* data)
{
FileDialog *pDlg = static_cast<FileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_CHANGEPATH )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
bool FileDialog::setFile(void* obj, const char* index, const char* data)
{
return false;
};
//-----------------------------------------------------------------------------
// OpenFileDialog Implementation
//-----------------------------------------------------------------------------
OpenFileDialog::OpenFileDialog()
{
// Default File Must Exist
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST;
}
OpenFileDialog::~OpenFileDialog()
{
mMustExist = true;
mMultipleFiles = false;
}
IMPLEMENT_CONOBJECT(OpenFileDialog);
//-----------------------------------------------------------------------------
// Console Properties
//-----------------------------------------------------------------------------
void OpenFileDialog::initPersistFields()
{
addProtectedField("MustExist", TypeBool, Offset(mMustExist, OpenFileDialog), &setMustExist, &getMustExist, "True/False whether the file returned must exist or not" );
addProtectedField("MultipleFiles", TypeBool, Offset(mMultipleFiles, OpenFileDialog), &setMultipleFiles, &getMultipleFiles, "True/False whether multiple files may be selected and returned or not" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// File Must Exist - Boolean
//-----------------------------------------------------------------------------
bool OpenFileDialog::setMustExist(void* obj, const char* index, const char* data)
{
bool bMustExist = dAtob( data );
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
if( bMustExist )
pDlg->mData.mStyle |= FileDialogData::FDS_MUSTEXIST;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_MUSTEXIST;
return true;
};
const char* OpenFileDialog::getMustExist(void* obj, const char* data)
{
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_MUSTEXIST )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
//-----------------------------------------------------------------------------
// Can Select Multiple Files - Boolean
//-----------------------------------------------------------------------------
bool OpenFileDialog::setMultipleFiles(void* obj, const char* index, const char* data)
{
bool bMustExist = dAtob( data );
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
if( bMustExist )
pDlg->mData.mStyle |= FileDialogData::FDS_MULTIPLEFILES;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_MULTIPLEFILES;
return true;
};
const char* OpenFileDialog::getMultipleFiles(void* obj, const char* data)
{
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
//-----------------------------------------------------------------------------
// SaveFileDialog Implementation
//-----------------------------------------------------------------------------
SaveFileDialog::SaveFileDialog()
{
// Default File Must Exist
mData.mStyle = FileDialogData::FDS_SAVE | FileDialogData::FDS_OVERWRITEPROMPT;
mOverwritePrompt = true;
}
SaveFileDialog::~SaveFileDialog()
{
}
IMPLEMENT_CONOBJECT(SaveFileDialog);
//-----------------------------------------------------------------------------
// Console Properties
//-----------------------------------------------------------------------------
void SaveFileDialog::initPersistFields()
{
addProtectedField("OverwritePrompt", TypeBool, Offset(mOverwritePrompt, SaveFileDialog), &setOverwritePrompt, &getOverwritePrompt, "True/False whether the dialog should prompt before accepting an existing file name" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// Prompt on Overwrite - Boolean
//-----------------------------------------------------------------------------
bool SaveFileDialog::setOverwritePrompt(void* obj, const char* index, const char* data)
{
bool bOverwrite = dAtob( data );
SaveFileDialog *pDlg = static_cast<SaveFileDialog*>( obj );
if( bOverwrite )
pDlg->mData.mStyle |= FileDialogData::FDS_OVERWRITEPROMPT;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_OVERWRITEPROMPT;
return true;
};
const char* SaveFileDialog::getOverwritePrompt(void* obj, const char* data)
{
SaveFileDialog *pDlg = static_cast<SaveFileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
//-----------------------------------------------------------------------------
// OpenFolderDialog Implementation
//-----------------------------------------------------------------------------
OpenFolderDialog::OpenFolderDialog()
{
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_OVERWRITEPROMPT | FileDialogData::FDS_BROWSEFOLDER;
mMustExistInDir = "";
}
IMPLEMENT_CONOBJECT(OpenFolderDialog);
void OpenFolderDialog::initPersistFields()
{
addField("fileMustExist", TypeFilename, Offset(mMustExistInDir, OpenFolderDialog), "File that must in selected folder for it to be valid");
Parent::initPersistFields();
}

View file

@ -0,0 +1,239 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
#include <unistd.h>
#include "platform/platform.h"
#include "console/console.h"
#include "core/stringTable.h"
#include "platform/platformInput.h"
#include "platform/threads/thread.h"
#pragma mark ---- Various Directories ----
//-----------------------------------------------------------------------------
const char* Platform::getUserDataDirectory()
{
// application support directory is most in line with the current usages of this function.
// this may change with later usage
// perhaps the user data directory should be pref-controlled?
NSString *nsDataDir = [@"~/Library/Application Support/" stringByStandardizingPath];
return StringTable->insert([nsDataDir UTF8String]);
}
//-----------------------------------------------------------------------------
const char* Platform::getUserHomeDirectory()
{
return StringTable->insert([[@"~/" stringByStandardizingPath] UTF8String]);
}
//-----------------------------------------------------------------------------
StringTableEntry osGetTemporaryDirectory()
{
NSString *tdir = NSTemporaryDirectory();
const char *path = [tdir UTF8String];
return StringTable->insert(path);
}
#pragma mark ---- Administrator ----
//-----------------------------------------------------------------------------
bool Platform::getUserIsAdministrator()
{
// if we can write to /Library, we're probably an admin
// HACK: this is not really very good, because people can chmod Library.
return (access("/Library", W_OK) == 0);
}
#pragma mark ---- Cosmetic ----
//-----------------------------------------------------------------------------
bool Platform::displaySplashWindow()
{
return false;
}
#pragma mark ---- File IO ----
//-----------------------------------------------------------------------------
bool dPathCopy(const char* source, const char* dest, bool nooverwrite)
{
NSFileManager *manager = [NSFileManager defaultManager];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *nsource = [[NSString stringWithUTF8String:source] stringByStandardizingPath];
NSString *ndest = [[NSString stringWithUTF8String:dest] stringByStandardizingPath];
NSString *ndestFolder = [ndest stringByDeletingLastPathComponent];
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;
}
}
if([manager fileExistsAtPath:ndestFolder] == NO)
{
ndestFolder = [ndestFolder stringByAppendingString:@"/"]; // createpath requires a trailing slash
Platform::createPath([ndestFolder UTF8String]);
}
bool ret = [manager copyPath:nsource toPath:ndest handler:nil];
[pool release];
return ret;
}
//-----------------------------------------------------------------------------
bool dFileRename(const char *source, const char *dest)
{
if(source == NULL || dest == NULL)
return false;
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("dFileRename: no file exists at %s",source);
return false;
}
if( [manager fileExistsAtPath:ndest] )
{
Con::warnf("dFileRename: Deleting files at path: %s", dest);
}
bool ret = [manager movePath:nsource toPath:ndest handler:nil];
return ret;
}
#pragma mark -
#pragma mark ---- ShellExecute ----
class ExecuteThread : public Thread
{
const char* zargs;
const char* directory;
const char* executable;
public:
ExecuteThread(const char *_executable, const char *_args /* = NULL */, const char *_directory /* = NULL */) : Thread(0, NULL, false, true)
{
zargs = dStrdup(_args);
directory = dStrdup(_directory);
executable = dStrdup(_executable);
start();
}
virtual void run(void* arg);
};
static char* _unDoubleQuote(char* arg)
{
U32 len = dStrlen(arg);
if(!len)
return arg;
if(arg[0] == '"' && arg[len-1] == '"')
{
arg[len - 1] = '\0';
return arg + 1;
}
return arg;
}
void ExecuteThread::run(void* arg)
{
// 2k should be enough. if it's not, bail.
// char buf[2048];
// U32 len = dSprintf(buf, sizeof(buf), "%s %s -workingDir %s", executable, args, directory);
// if( len >= sizeof(buf))
// {
// Con::errorf("shellExecute(): the command was too long, and won't be run.");
// return;
// }
// // calls sh with the string and blocks until the command returns.
// system(buf);
// FIXME: there is absolutely no error checking in here.
printf("creating nstask\n");
NSTask *aTask = [[NSTask alloc] init];
NSMutableArray *array = [NSMutableArray array];
// scan the args list, breaking it up, space delimited, backslash escaped.
U32 len = dStrlen(zargs);
char args[len+1];
dStrncpy(args, zargs, len+1);
char *lastarg = args;
bool escaping = false;
for(int i = 0; i< len; i++)
{
char c = args[i];
// a backslash escapes the next character
if(escaping)
continue;
if(c == '\\')
escaping = true;
if(c == ' ')
{
args[i] = '\0';
if(*lastarg)
[array addObject:[NSString stringWithUTF8String: _unDoubleQuote(lastarg)]];
lastarg = args + i + 1;
}
}
if(*lastarg)
[array addObject:[NSString stringWithUTF8String: _unDoubleQuote(lastarg)]];
[aTask setArguments: array];
[aTask setCurrentDirectoryPath:[NSString stringWithUTF8String: this->directory]];
[aTask setLaunchPath:[NSString stringWithUTF8String:executable]];
[aTask launch];
[aTask waitUntilExit];
U32 ret = [aTask terminationStatus];
Con::executef("onExecuteDone", Con::getIntArg(ret));
printf("done nstask\n");
}
ConsoleFunction(shellExecute, bool, 2, 4, "(executable, [args], [directory])")
{
ExecuteThread *et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL);
TORQUE_UNUSED(et);
return true; // Bug: BPNC error: need feedback on whether the command was sucessful
}

View file

@ -0,0 +1,78 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/types.h"
#include "platform/platformDlibrary.h"
#include <dlfcn.h>
class MacDLibrary : public DLibrary
{
void* _handle;
public:
MacDLibrary();
~MacDLibrary();
bool open(const char* file);
void close();
void* bind(const char* name);
};
MacDLibrary::MacDLibrary()
{
_handle = NULL;
}
MacDLibrary::~MacDLibrary()
{
close();
}
bool MacDLibrary::open(const char* file)
{
Platform::getExecutablePath();
_handle = dlopen(file, RTLD_LAZY | RTLD_LOCAL);
return _handle != NULL;
}
void* MacDLibrary::bind(const char* name)
{
return _handle ? dlsym(_handle, name) : NULL;
}
void MacDLibrary::close()
{
if(_handle)
dlclose(_handle);
_handle = NULL;
}
DLibraryRef OsLoadLibrary(const char* file)
{
MacDLibrary* library = new MacDLibrary();
if(!library->open(file))
{
delete library;
return NULL;
}
return library;
}

View file

@ -0,0 +1,78 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MACGLUTILS_H_
#define _MACGLUTILS_H_
static Vector<NSOpenGLPixelFormatAttribute> _beginPixelFormatAttributesForDisplay(CGDirectDisplayID display)
{
Vector<NSOpenGLPixelFormatAttribute> attributes;
attributes.reserve(16); // Most attribute lists won't exceed this
attributes.push_back(NSOpenGLPFAScreenMask);
attributes.push_back((NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display));
attributes.push_back(NSOpenGLPFANoRecovery);
attributes.push_back(NSOpenGLPFADoubleBuffer);
attributes.push_back(NSOpenGLPFAAccelerated);
attributes.push_back(NSOpenGLPFAAuxBuffers);
attributes.push_back((NSOpenGLPixelFormatAttribute)1);
return attributes;
}
static void _addColorAlphaDepthStencilAttributes(Vector<NSOpenGLPixelFormatAttribute>& attributes, U32 color, U32 alpha, U32 depth, U32 stencil)
{
attributes.push_back(NSOpenGLPFAColorSize); attributes.push_back((NSOpenGLPixelFormatAttribute)color);
attributes.push_back(NSOpenGLPFAAlphaSize); attributes.push_back((NSOpenGLPixelFormatAttribute)alpha);
attributes.push_back(NSOpenGLPFADepthSize); attributes.push_back((NSOpenGLPixelFormatAttribute)depth);
attributes.push_back(NSOpenGLPFAStencilSize); attributes.push_back((NSOpenGLPixelFormatAttribute)stencil);
}
static void _addFullscreenAttributes(Vector<NSOpenGLPixelFormatAttribute>& attributes)
{
attributes.push_back(NSOpenGLPFAFullScreen);
}
static void _endAttributeList(Vector<NSOpenGLPixelFormatAttribute>& attributes)
{
attributes.push_back((NSOpenGLPixelFormatAttribute)0);
}
static Vector<NSOpenGLPixelFormatAttribute> _createStandardPixelFormatAttributesForDisplay(CGDirectDisplayID display)
{
Vector<NSOpenGLPixelFormatAttribute> attributes = _beginPixelFormatAttributesForDisplay(display);
_addColorAlphaDepthStencilAttributes(attributes, 24, 8, 24, 8);
_endAttributeList(attributes);
return attributes;
}
/// returns an opengl pixel format suitable for creating shared opengl contexts.
static NSOpenGLPixelFormat* _createStandardPixelFormat()
{
Vector<NSOpenGLPixelFormatAttribute> attributes = _createStandardPixelFormatAttributesForDisplay(kCGDirectMainDisplay);
NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes.address()];
return fmt;
}
#endif

View file

@ -0,0 +1,194 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <Cocoa/Cocoa.h>
#include "app/mainLoop.h"
#include "platform/platformInput.h"
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h>
#include "console/console.h"
#include "platform/threads/thread.h"
// TODO: let the mainLoop's sleep time happen via rescheduling the timer every run-through.
extern S32 sgTimeManagerProcessInterval;
@interface MainLoopTimerHandler : NSObject
{
U32 argc;
const char** argv;
NSTimeInterval interval;
}
+(id)startTimerWithintervalMs:(U32)intervalMs argc:(U32)_argc argv:(const char**)_argv;
-(void)firstFire:(NSTimer*)theTimer;
-(void)fireTimer:(NSTimer*)theTimer;
@end
@implementation MainLoopTimerHandler
-(void)firstFire:(NSTimer*)theTimer
{
StandardMainLoop::init();
StandardMainLoop::handleCommandLine(argc, argv);
[NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(fireTimer:) userInfo:nil repeats:YES];
}
-(void)fireTimer:(NSTimer*)theTimer
{
if(!StandardMainLoop::doMainLoop())
{
StandardMainLoop::shutdown();
[theTimer invalidate];
[NSApp setDelegate:nil];
[NSApp terminate:self];
}
// if(!mainLoop || !mainLoop->mainLoop())
// {
// // stop the timer from firing again
// if(mainLoop)
// mainLoop->shutdown();
//
// [theTimer invalidate];
// [NSApp setDelegate:nil];
// [NSApp terminate:self];
// }
}
+(id)startTimerWithintervalMs:(U32)intervalMs argc:(U32)_argc argv:(const char**)_argv
{
MainLoopTimerHandler* handler = [[[MainLoopTimerHandler alloc] init] autorelease];
handler->argc = _argc;
handler->argv = _argv;
handler->interval = intervalMs / 1000.0; // interval in milliseconds
[NSTimer scheduledTimerWithTimeInterval:handler->interval target:handler selector:@selector(firstFire:) userInfo:nil repeats:NO];
return handler;
}
@end
#pragma mark -
#ifndef TORQUE_SHARED
//-----------------------------------------------------------------------------
// main() - the real one - this is the actual program entry point.
//-----------------------------------------------------------------------------
S32 main(S32 argc, const char **argv)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// get command line and text file args, filter them
// now, we prepare to hand off execution to torque & macosx.
U32 appReturn = 0;
printf("installing torque main loop timer\n");
[MainLoopTimerHandler startTimerWithintervalMs:1 argc:argc argv:argv];
printf("starting NSApplicationMain\n");
appReturn = NSApplicationMain(argc, argv);
printf("NSApplicationMain exited\n");
// shut down the engine
[pool release];
return appReturn;
}
#endif
static NSApplication *app = NULL;
static NSAutoreleasePool* pool = NULL;
void torque_mac_engineinit(S32 argc, const char **argv)
{
if (!Platform::getWebDeployment())
{
pool = [[NSAutoreleasePool alloc] init];
app = [NSApplication sharedApplication];
}
}
void torque_mac_enginetick()
{
if (!Platform::getWebDeployment())
{
NSEvent *e = [app nextEventMatchingMask: NSAnyEventMask
untilDate: [NSDate distantPast]
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e)
[app sendEvent: e];
}
}
void torque_mac_engineshutdown()
{
if (!Platform::getWebDeployment())
{
[pool release];
}
}
extern "C" {
//-----------------------------------------------------------------------------
// torque_macmain() - entry point for application using bundle
//-----------------------------------------------------------------------------
S32 torque_macmain(S32 argc, const char **argv)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// get command line and text file args, filter them
// now, we prepare to hand off execution to torque & macosx.
U32 appReturn = 0;
printf("installing torque main loop timer\n");
[MainLoopTimerHandler startTimerWithintervalMs:1 argc:argc argv:argv];
printf("starting NSApplicationMain\n");
appReturn = NSApplicationMain(argc, argv);
printf("NSApplicationMain exited\n");
// shut down the engine
[pool release];
return appReturn;
}
} // extern "C"
#pragma mark ---- Init funcs ----
//------------------------------------------------------------------------------
void Platform::init()
{
// Set the platform variable for the scripts
Con::setVariable( "$platform", "macos" );
Input::init();
}
//------------------------------------------------------------------------------
void Platform::shutdown()
{
Input::destroy();
}

View file

@ -0,0 +1,180 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#import <Cocoa/Cocoa.h>
#include "platform/nativeDialogs/msgBox.h"
#include "console/console.h"
void Platform::AlertOK(const char *windowTitle, const char *message)
{
Platform::messageBox(windowTitle, message, MBOk, MIInformation);
}
//--------------------------------------
bool Platform::AlertOKCancel(const char *windowTitle, const char *message)
{
return ( Platform::messageBox(windowTitle, message, MBOkCancel, MIInformation) == MROk );
}
//--------------------------------------
bool Platform::AlertRetry(const char *windowTitle, const char *message)
{
return ( Platform::messageBox(windowTitle, message, MBRetryCancel, MIInformation) == MRRetry );
}
namespace MsgBoxMac
{
struct _NSStringMap
{
S32 num;
NSString* ok;
NSString* cancel;
NSString* third;
};
static _NSStringMap sgButtonTextMap[] =
{
{ MBOk, @"OK", nil, nil },
{ MBOkCancel, @"OK", @"Cancel", nil },
{ MBRetryCancel, @"Retry", @"Cancel", nil },
{ MBSaveDontSave, @"Yes", @"No", nil },
{ MBSaveDontSaveCancel, @"Yes", @"No", @"Cancel" },
{ -1, nil, nil, nil }
};
struct _NSAlertResultMap
{
S32 num;
S32 ok;
S32 cancel;
S32 third;
};
static _NSAlertResultMap sgAlertResultMap[] =
{
{ MBOk, MROk, 0, 0 },
{ MBOkCancel, MROk, MRCancel, 0 },
{ MBRetryCancel, MRRetry, MRCancel, 0 },
{ MBSaveDontSave, MROk, MRDontSave, 0 },
{ MBSaveDontSaveCancel, MROk, MRDontSave, MRCancel },
{ -1, nil, nil, nil }
};
} // end MsgBoxMac namespace
//-----------------------------------------------------------------------------
S32 Platform::messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons, MBIcons icon)
{
// TODO: put this on the main thread
// determine the button text
NSString *okBtn = nil;
NSString *cancelBtn = nil;
NSString *thirdBtn = nil;
U32 i;
for(i = 0; MsgBoxMac::sgButtonTextMap[i].num != -1; i++)
{
if(MsgBoxMac::sgButtonTextMap[i].num != buttons)
continue;
okBtn = MsgBoxMac::sgButtonTextMap[i].ok;
cancelBtn = MsgBoxMac::sgButtonTextMap[i].cancel;
thirdBtn = MsgBoxMac::sgButtonTextMap[i].third;
break;
}
if(MsgBoxMac::sgButtonTextMap[i].num == -1)
Con::errorf("Unknown message box button set requested. Mac Platform::messageBox() probably needs to be updated.");
// convert title and message to NSStrings
NSString *nsTitle = [NSString stringWithUTF8String:title];
NSString *nsMessage = [NSString stringWithUTF8String:message];
// TODO: ensure that the cursor is the expected shape
// show the alert
S32 result = -2;
NSAlert *alert = [NSAlert alertWithMessageText:nsTitle
defaultButton:okBtn
alternateButton:thirdBtn
otherButton:cancelBtn
informativeTextWithFormat:nsMessage];
switch(icon)
{
// TODO:
// Currently, NSAlert only provides two alert icon options.
// NSWarningAlertStyle and NSInformationalAlertStyle are identical and
// display the application icon, while NSCriticalAlertStyle displays
// a shrunken app icon badge on a yellow-triangle-with-a-bang icon.
// If custom icons were created, they could be used here with the
// message [alert setIcon:foo]
case MIWarning: // MIWarning = 0
case MIQuestion: // MIquestion = 3
[alert setAlertStyle:NSWarningAlertStyle];
break;
case MIInformation: // MIInformation = 1
[alert setAlertStyle:NSInformationalAlertStyle];
break;
case MIStop: // MIStop = 3
[alert setAlertStyle:NSCriticalAlertStyle];
break;
default:
Con::errorf("Unknown message box icon requested. Mac Platform::messageBox() probably needs to be updated.");
}
id appDelegate = [NSApp delegate];
[NSApp setDelegate: nil];
U32 cursorDepth = 0;
while(!CGCursorIsVisible())
{
CGDisplayShowCursor(kCGDirectMainDisplay);
cursorDepth++;
}
CGAssociateMouseAndMouseCursorPosition(true);
result = [alert runModal];
[NSApp setDelegate: appDelegate];
S32 ret = 0;
for(U32 i = 0; MsgBoxMac::sgAlertResultMap[i].num != -1; i++)
{
if(MsgBoxMac::sgAlertResultMap[i].num != buttons)
continue;
switch(result)
{
case NSAlertDefaultReturn:
ret = MsgBoxMac::sgAlertResultMap[i].ok; break;
case NSAlertOtherReturn:
ret = MsgBoxMac::sgAlertResultMap[i].cancel; break;
case NSAlertAlternateReturn:
ret = MsgBoxMac::sgAlertResultMap[i].third; break;
}
}
return ret;
}

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>toggleAutomaticLinkDetection</key>
<string>id</string>
<key>toggleAutomaticQuoteSubstitution</key>
<string>id</string>
<key>toggleGrammarChecking</key>
<string>id</string>
<key>toggleSmartInsertDelete</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>FirstResponder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>629</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../../../../../GameExamples/T3D/buildFiles/Xcode/T3D.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>29</integer>
</array>
<key>IBSystem Version</key>
<string>9F33</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

Binary file not shown.

View file

@ -0,0 +1,303 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformMac/platformMacCarb.h"
#include "platform/menus/menuBar.h"
#include "platform/menus/popupMenu.h"
#include "gui/core/guiCanvas.h"
#include "windowManager/platformWindowMgr.h"
#include "windowManager/platformWindow.h"
class PlatformMenuBarData
{
public:
PlatformMenuBarData() :
mMenuEventHandlerRef(NULL),
mCommandEventHandlerRef(NULL),
mMenuOpenCount( 0 ),
mLastCloseTime( 0 )
{}
EventHandlerRef mMenuEventHandlerRef;
EventHandlerRef mCommandEventHandlerRef;
/// More evil hacking for OSX. There seems to be no way to disable menu shortcuts and
/// they are automatically routed within that Cocoa thing outside of our control. Also,
/// there's no way of telling what triggered a command event and thus no way of knowing
/// whether it was a keyboard shortcut. Sigh.
///
/// So what we do here is monitor the sequence of events leading to a command event. We
/// capture the time the last open menu was closed and then, when we receive a command
/// event (which are dished out after the menus are closed) and keyboard accelerators are
/// disabled, we check whether we are a certain very short time away in the event stream
/// from the menu close event. If so, we figure the event came from clicking in a menu.
///
/// Utterly evil and dirty but seems to do the trick.
U32 mMenuOpenCount;
EventTime mLastCloseTime;
};
//-----------------------------------------------------------------------------
#pragma mark -
#pragma mark ---- menu event handler ----
static OSStatus _OnMenuEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
{
PlatformMenuBarData* mbData = ( PlatformMenuBarData* ) userData;
MenuRef menu;
GetEventParameter(theEvent, kEventParamDirectObject, typeMenuRef, NULL, sizeof(MenuRef), NULL, &menu);
// Count open/close for the sake of hotkey disabling.
UInt32 kind = GetEventKind( theEvent );
if( kind == kEventMenuOpening )
mbData->mMenuOpenCount ++;
else
{
AssertWarn( mbData->mMenuOpenCount > 0, "Unbalanced menu open/close events in _OnMenuEvent" );
if( mbData->mMenuOpenCount )
mbData->mMenuOpenCount --;
// Initial menu closed. Capture time.
if( !mbData->mMenuOpenCount )
mbData->mLastCloseTime = GetEventTime( theEvent );
}
OSStatus err = eventNotHandledErr;
PopupMenu *torqueMenu;
if( CountMenuItems( menu ) > 0 )
{
// I don't know of a way to get the Torque PopupMenu object from a Carbon MenuRef
// other than going through its first menu item
err = GetMenuItemProperty(menu, 1, 'GG2d', 'ownr', sizeof(PopupMenu*), NULL, &torqueMenu);
if( err == noErr && torqueMenu != NULL )
{
torqueMenu->onMenuSelect();
}
}
return err;
}
//-----------------------------------------------------------------------------
#pragma mark -
#pragma mark ---- menu command event handler ----
static bool MacCarbHandleMenuCommand( void* hiCommand, PlatformMenuBarData* mbData )
{
HICommand *cmd = (HICommand*)hiCommand;
if(cmd->commandID != kHICommandTorque)
return false;
MenuRef menu = cmd->menu.menuRef;
MenuItemIndex item = cmd->menu.menuItemIndex;
// Run the command handler.
PopupMenu* torqueMenu;
OSStatus err = GetMenuItemProperty(menu, item, 'GG2d', 'ownr', sizeof(PopupMenu*), NULL, &torqueMenu);
AssertFatal(err == noErr, "Could not resolve the PopupMenu stored on a native menu item");
UInt32 command;
err = GetMenuItemRefCon(menu, item, &command);
AssertFatal(err == noErr, "Could not find the tag of a native menu item");
if(!torqueMenu->canHandleID(command))
Con::errorf("menu claims it cannot handle that id. how odd.");
// un-highlight currently selected menu
HiliteMenu( 0 );
return torqueMenu->handleSelect(command,NULL);
}
//-----------------------------------------------------------------------------
#pragma mark -
#pragma mark ---- Command Events ----
static OSStatus _OnCommandEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
PlatformMenuBarData* mbData = ( PlatformMenuBarData* ) userData;
HICommand commandStruct;
OSStatus result = eventNotHandledErr;
GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct);
// pass menu command events to a more specific handler.
if(commandStruct.attributes & kHICommandFromMenu)
{
bool handleEvent = true;
// Do menu-close check hack.
PlatformWindow* window = PlatformWindowManager::get()->getFocusedWindow();
if( !window || !window->getAcceleratorsEnabled() )
{
F32 deltaTime = mFabs( GetEventTime( theEvent ) - mbData->mLastCloseTime );
if( deltaTime > 0.1f )
handleEvent = false;
}
if( handleEvent && MacCarbHandleMenuCommand(&commandStruct, mbData) )
result = noErr;
}
return result;
}
//-----------------------------------------------------------------------------
// MenuBar Methods
//-----------------------------------------------------------------------------
void MenuBar::createPlatformPopupMenuData()
{
mData = new PlatformMenuBarData;
}
void MenuBar::deletePlatformPopupMenuData()
{
SAFE_DELETE(mData);
}
//-----------------------------------------------------------------------------
void MenuBar::attachToCanvas(GuiCanvas *owner, S32 pos)
{
if(owner == NULL || isAttachedToCanvas())
return;
mCanvas = owner;
// Add the items
for(S32 i = 0;i < size();++i)
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
if(mnu == NULL)
{
Con::warnf("MenuBar::attachToMenuBar - Non-PopupMenu object in set");
continue;
}
if(mnu->isAttachedToMenuBar())
mnu->removeFromMenuBar();
mnu->attachToMenuBar(owner, pos + i, mnu->getBarTitle());
}
// register as listener for menu opening events
static EventTypeSpec menuEventTypes[ 2 ];
menuEventTypes[ 0 ].eventClass = kEventClassMenu;
menuEventTypes[ 0 ].eventKind = kEventMenuOpening;
menuEventTypes[ 1 ].eventClass = kEventClassMenu;
menuEventTypes[ 1 ].eventKind = kEventMenuClosed;
EventHandlerUPP menuEventHandlerUPP;
menuEventHandlerUPP = NewEventHandlerUPP(_OnMenuEvent);
InstallEventHandler(GetApplicationEventTarget(), menuEventHandlerUPP, 2, menuEventTypes, mData, &mData->mMenuEventHandlerRef);
// register as listener for process command events
static EventTypeSpec comEventTypes[1];
comEventTypes[0].eventClass = kEventClassCommand;
comEventTypes[0].eventKind = kEventCommandProcess;
EventHandlerUPP commandEventHandlerUPP;
commandEventHandlerUPP = NewEventHandlerUPP(_OnCommandEvent);
InstallEventHandler(GetApplicationEventTarget(), commandEventHandlerUPP, 1, comEventTypes, mData, &mData->mCommandEventHandlerRef);
}
//-----------------------------------------------------------------------------
void MenuBar::removeFromCanvas()
{
if(mCanvas == NULL || ! isAttachedToCanvas())
return;
if(mData->mCommandEventHandlerRef != NULL)
RemoveEventHandler( mData->mCommandEventHandlerRef );
mData->mCommandEventHandlerRef = NULL;
if(mData->mMenuEventHandlerRef != NULL)
RemoveEventHandler( mData->mMenuEventHandlerRef );
mData->mMenuEventHandlerRef = NULL;
// Add the items
for(S32 i = 0;i < size();++i)
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
if(mnu == NULL)
{
Con::warnf("MenuBar::removeFromMenuBar - Non-PopupMenu object in set");
continue;
}
mnu->removeFromMenuBar();
}
mCanvas = NULL;
}
//-----------------------------------------------------------------------------
void MenuBar::updateMenuBar(PopupMenu* menu)
{
if(! isAttachedToCanvas())
return;
menu->removeFromMenuBar();
SimSet::iterator itr = find(begin(), end(), menu);
if(itr == end())
return;
// Get the item currently at the position we want to add to
S32 pos = itr - begin();
S16 posID = 0;
PopupMenu *nextMenu = NULL;
for(S32 i = pos + 1; i < size(); i++)
{
PopupMenu *testMenu = dynamic_cast<PopupMenu *>(at(i));
if (testMenu && testMenu->isAttachedToMenuBar())
{
nextMenu = testMenu;
break;
}
}
if(nextMenu)
posID = GetMenuID(nextMenu->mData->mMenu);
menu->attachToMenuBar(mCanvas, posID, menu->mBarTitle);
}

View file

@ -0,0 +1,439 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformMac/platformMacCarb.h"
#include "platform/menus/popupmenu.h"
#include "core/util/safeDelete.h"
#include "gui/core/guiCanvas.h"
void PopupMenu::createPlatformPopupMenuData()
{
mData = new PlatformPopupMenuData;
}
void PopupMenu::deletePlatformPopupMenuData()
{
SAFE_DELETE(mData);
}
void PopupMenu::createPlatformMenu()
{
OSStatus err = CreateNewMenu( mData->tag, kMenuAttrAutoDisable,&(mData->mMenu));
CFRetain(mData->mMenu);
AssertFatal(err == noErr, "Could not create Carbon MenuRef");
}
static int _getModifierMask(const char* accel)
{
int ret = 0;
if(dStrstr(accel, "ctrl"))
ret |= kMenuControlModifier;
if(dStrstr(accel, "shift"))
ret |= kMenuShiftModifier;
if(dStrstr(accel, "alt"))
ret |= kMenuOptionModifier;
if(!(dStrstr(accel, "cmd") || dStrstr(accel, "command")))
ret |= kMenuNoCommandModifier;
return ret;
}
static void _assignCommandKeys(const char* accel, MenuRef menu, MenuItemIndex item)
{
if(!(accel && *accel))
return;
// get the modifier keys
String _accel = String::ToLower( accel );
int mods = _getModifierMask(_accel);
// accel is space or dash delimted.
// the modifier key is either the last token in accel, or the first char in accel.
const char* key = dStrrchr(_accel, ' ');
if(!key)
key = dStrrchr(_accel, '-');
if(!key)
key = _accel;
else
key++;
if(dStrlen(key) <= 1)
{
char k = dToupper( key[0] );
SetMenuItemCommandKey( menu, item, false, k );
}
else
{
SInt16 glyph = kMenuNullGlyph;
//*** A lot of these mappings came from a listing at http://developer.apple.com/releasenotes/Carbon/HIToolboxOlderNotes.html
if(!dStricmp(key, "DELETE"))
glyph = kMenuDeleteRightGlyph;
else if(!dStricmp(key, "HOME"))
glyph = kMenuNorthwestArrowGlyph;
else if(!dStricmp(key, "END"))
glyph = kMenuSoutheastArrowGlyph;
else if(!dStricmp(key, "BACKSPACE"))
glyph = kMenuDeleteLeftGlyph;
else if(!dStricmp(key, "TAB"))
glyph = kMenuTabRightGlyph;
else if(!dStricmp(key, "RETURN"))
glyph = kMenuReturnGlyph;
else if(!dStricmp(key, "ENTER"))
glyph = kMenuEnterGlyph;
else if(!dStricmp(key, "PG UP"))
glyph = kMenuPageUpGlyph;
else if(!dStricmp(key, "PG DOWN"))
glyph = kMenuPageDownGlyph;
else if(!dStricmp(key, "ESC"))
glyph = kMenuEscapeGlyph;
else if(!dStricmp(key, "LEFT"))
glyph = kMenuLeftArrowGlyph;
else if(!dStricmp(key, "RIGHT"))
glyph = kMenuRightArrowGlyph;
else if(!dStricmp(key, "UP"))
glyph = kMenuUpArrowGlyph;
else if(!dStricmp(key, "DOWN"))
glyph = kMenuDownArrowGlyph;
else if(!dStricmp(key, "SPACE"))
glyph = kMenuSpaceGlyph;
else if(!dStricmp(key, "F1"))
glyph = kMenuF1Glyph;
else if(!dStricmp(key, "F2"))
glyph = kMenuF2Glyph;
else if(!dStricmp(key, "F3"))
glyph = kMenuF3Glyph;
else if(!dStricmp(key, "F4"))
glyph = kMenuF4Glyph;
else if(!dStricmp(key, "F5"))
glyph = kMenuF5Glyph;
else if(!dStricmp(key, "F6"))
glyph = kMenuF6Glyph;
else if(!dStricmp(key, "F7"))
glyph = kMenuF7Glyph;
else if(!dStricmp(key, "F8"))
glyph = kMenuF8Glyph;
else if(!dStricmp(key, "F9"))
glyph = kMenuF9Glyph;
else if(!dStricmp(key, "F10"))
glyph = kMenuF10Glyph;
else if(!dStricmp(key, "F11"))
glyph = kMenuF11Glyph;
else if(!dStricmp(key, "F12"))
glyph = kMenuF12Glyph;
else if(!dStricmp(key, "F13"))
glyph = kMenuF13Glyph;
else if(!dStricmp(key, "F14"))
glyph = kMenuF14Glyph;
else if(!dStricmp(key, "F15"))
glyph = kMenuF15Glyph;
SetMenuItemKeyGlyph(menu, item, glyph);
}
SetMenuItemModifiers(menu, item, mods);
}
S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accel)
{
MenuItemIndex item;
CFStringRef cftitle;
MenuItemAttributes attr = 0;
bool needRelease = false;
if(title && *title)
{
cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8);
needRelease = true;
}
else
{
cftitle = CFSTR("-");
attr = kMenuItemAttrSeparator;
}
InsertMenuItemTextWithCFString(mData->mMenu, cftitle, pos, attr, kHICommandTorque + 1);
if( needRelease )
CFRelease( cftitle );
// ensure that we have the correct index for the new menu item
MenuRef outref;
GetIndMenuItemWithCommandID(mData->mMenu, kHICommandTorque+1, 1, &outref, &item);
SetMenuItemCommandID(mData->mMenu, item, kHICommandTorque);
// save a ref to the PopupMenu that owns this item.
PopupMenu* thisMenu = this;
SetMenuItemProperty(mData->mMenu, item, 'GG2d', 'ownr', sizeof(PopupMenu*), &thisMenu);
// construct the accelerator keys
_assignCommandKeys(accel, mData->mMenu, item);
S32 tag = PlatformPopupMenuData::getTag();
SetMenuItemRefCon(mData->mMenu, item, tag);
return tag;
}
S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu)
{
for(S32 i = 0;i < mSubmenus->size();i++)
{
if(submenu == (*mSubmenus)[i])
{
Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice");
return -1;
}
}
CFStringRef cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8);
InsertMenuItemTextWithCFString(mData->mMenu, cftitle, pos, 0, kHICommandTorque + 1);
CFRelease( cftitle );
// ensure that we have the correct index for the new menu item
MenuRef outref;
MenuItemIndex item;
GetIndMenuItemWithCommandID(mData->mMenu, kHICommandTorque+1, 1, &outref, &item);
SetMenuItemCommandID(mData->mMenu, item, 0);
S32 tag = PlatformPopupMenuData::getTag();
SetMenuItemRefCon( mData->mMenu, item, tag);
// store a pointer to the PopupMenu this item represents. See PopupMenu::removeItem()
SetMenuItemProperty(mData->mMenu, item, 'GG2d', 'subm', sizeof(PopupMenu*), submenu);
SetMenuItemHierarchicalMenu( mData->mMenu, item, submenu->mData->mMenu);
mSubmenus->addObject(submenu);
return tag;
}
void PopupMenu::removeItem(S32 itemPos)
{
PopupMenu* submenu;
itemPos++; // adjust torque -> mac menu index
OSStatus err = GetMenuItemProperty(mData->mMenu, itemPos, 'GG2d', 'subm', sizeof(PopupMenu*),NULL,&submenu);
if(err == noErr)
mSubmenus->removeObject(submenu);
// deleting the item decrements the ref count on the mac submenu.
DeleteMenuItem(mData->mMenu, itemPos);
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::enableItem(S32 pos, bool enable)
{
pos++; // adjust torque -> mac menu index.
if(enable)
EnableMenuItem(mData->mMenu, pos);
else
DisableMenuItem(mData->mMenu, pos);
}
void PopupMenu::checkItem(S32 pos, bool checked)
{
pos++;
CheckMenuItem(mData->mMenu, pos, checked);
}
void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos)
{
// uncheck items
for(int i = firstPos; i <= lastPos; i++)
checkItem( i, false);
// check the selected item
checkItem( checkPos, true);
}
bool PopupMenu::isItemChecked(S32 pos)
{
CharParameter mark;
GetItemMark(mData->mMenu, pos, &mark);
return (mark == checkMark);
}
//////////////////////////////////////////////////////////////////////////
// this method really isn't necessary for the mac implementation
bool PopupMenu::canHandleID(U32 iD)
{
for(S32 i = 0;i < mSubmenus->size();i++)
{
PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
if(subM == NULL)
continue;
if(subM->canHandleID(iD))
return true;
}
UInt32 refcon;
U32 nItems = CountMenuItems(mData->mMenu);
for(int i = 1; i <= nItems; i++)
{
GetMenuItemRefCon(mData->mMenu, i, &refcon);
if(refcon == iD)
return true;
}
return false;
}
bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */)
{
// [tom, 8/20/2006] Pass off to a sub menu if it's for them
for(S32 i = 0;i < mSubmenus->size();i++)
{
PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
if(subM == NULL)
continue;
if(subM->canHandleID(command))
{
return subM->handleSelect(command, text);
}
}
// ensure that this menu actually has an item with the specificed command / refcon.
// this is not strictly necessary, we're just doing it here to keep the behavior
// in line with the windows implementation.
UInt32 refcon;
U32 nItems = CountMenuItems(mData->mMenu);
S32 pos = -1;
for(int i = 1; i <= nItems; i++)
{
GetMenuItemRefCon(mData->mMenu, i, &refcon);
if(refcon == command)
pos = i;
}
if(pos == -1)
{
Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command);
return false;
}
char textbuf[1024];
if(!text)
{
CFStringRef cfstr;
CopyMenuItemTextAsCFString(mData->mMenu, pos, &cfstr);
CFStringGetCString(cfstr,textbuf,sizeof(textbuf) - 1,kCFStringEncodingUTF8);
CFRelease( cfstr );
text = textbuf;
}
// [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script
return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(pos - 1), text ? text : ""));
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::showPopup(GuiCanvas* canvas, S32 x /* = -1 */, S32 y /* = -1 */)
{
if(x < 0 || y < 0)
{
Point2I p = canvas->getCursorPos();
x = p.x;
y = p.y;
}
PopUpMenuSelect(mData->mMenu, y, x, 0);
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::attachToMenuBar(GuiCanvas* canvas, S32 pos, const char *title)
{
CFStringRef cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8);
SetMenuTitleWithCFString(mData->mMenu, cftitle);
CFRelease( cftitle );
InsertMenu(mData->mMenu, pos);
onAttachToMenuBar(canvas, pos, title);
}
void PopupMenu::removeFromMenuBar()
{
DeleteMenu(mData->tag);
onRemoveFromMenuBar(mCanvas);
}
U32 PopupMenu::getItemCount()
{
return CountMenuItems( mData->mMenu );
}
bool PopupMenu::setItem(S32 pos, const char *title, const char *accelerator)
{
//TODO: update accelerator?
pos += 1; // Torque to mac index
CFStringRef cftitle = CFStringCreateWithCString( NULL, title, kCFStringEncodingUTF8 );
SetMenuItemTextWithCFString( mData->mMenu, pos, cftitle );
CFRelease( cftitle );
return true;
}
S32 PopupMenu::getPosOnMenuBar()
{
return -1;
}
void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos)
{
}

View file

@ -0,0 +1,175 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#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 "platform/platform.h"
#include "math/mMath.h"
#include "gfx/gl/ggl/ggl.h"
#define __gl_h_
#include <AGL/agl.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;
S32 desktopBitsPixel;
S32 desktopWidth;
S32 desktopHeight;
U32 currentTime;
bool isFullScreen;
U32 osVersion;
TSMDocumentID tsmDoc;
bool tsmActive;
U32 firstThreadId;
void* alertSemaphore;
S32 alertHit;
DialogRef alertDlg;
EventQueueRef mainEventQueue;
MRandomLCG platRandom;
bool mouseLocked;
bool backgrounded;
U32 sleepTicks;
Point2I windowSize;
U32 appReturn;
U32 argc;
char** argv;
U32 lastTimeTick;
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 );
/// 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;
/// Torque Menu Command ID
const U32 kHICommandTorque = 'TORQ';
/// @}
//-----------------------------------------------------------------------------
// Platform Menu Data
//-----------------------------------------------------------------------------
class PlatformPopupMenuData
{
public:
// We assign each new menu item an arbitrary integer tag.
static S32 getTag()
{
static S32 lastTag = 'TORQ';
return ++lastTag;
}
MenuRef mMenu;
S32 tag;
PlatformPopupMenuData()
{
mMenu = NULL;
tag = getTag();
}
~PlatformPopupMenuData()
{
if(mMenu)
CFRelease(mMenu);
mMenu = NULL;
}
};
#endif //_PLATFORMMACCARB_H_

Binary file not shown.