mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 06:34:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
BIN
Engine/source/platformMac/cursors/resizeNESW.png
Normal file
BIN
Engine/source/platformMac/cursors/resizeNESW.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 630 B |
BIN
Engine/source/platformMac/cursors/resizeNWSE.png
Normal file
BIN
Engine/source/platformMac/cursors/resizeNWSE.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 707 B |
BIN
Engine/source/platformMac/cursors/resizeall.png
Normal file
BIN
Engine/source/platformMac/cursors/resizeall.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 307 B |
38
Engine/source/platformMac/macApplication.h
Normal file
38
Engine/source/platformMac/macApplication.h
Normal 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
|
||||
79
Engine/source/platformMac/macApplication.mm
Normal file
79
Engine/source/platformMac/macApplication.mm
Normal 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
|
||||
64
Engine/source/platformMac/macCarbAsync.cpp
Normal file
64
Engine/source/platformMac/macCarbAsync.cpp
Normal 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 );
|
||||
}
|
||||
285
Engine/source/platformMac/macCarbCPUInfo.cpp
Normal file
285
Engine/source/platformMac/macCarbCPUInfo.cpp
Normal 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();
|
||||
}
|
||||
940
Engine/source/platformMac/macCarbFileio.mm
Normal file
940
Engine/source/platformMac/macCarbFileio.mm
Normal 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
|
||||
449
Engine/source/platformMac/macCarbFont.cpp
Normal file
449
Engine/source/platformMac/macCarbFont.cpp
Normal 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);
|
||||
74
Engine/source/platformMac/macCarbFont.h
Normal file
74
Engine/source/platformMac/macCarbFont.h
Normal 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;
|
||||
}
|
||||
500
Engine/source/platformMac/macCarbInput.cpp
Normal file
500
Engine/source/platformMac/macCarbInput.cpp
Normal 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.
|
||||
}
|
||||
123
Engine/source/platformMac/macCarbMath.cpp
Normal file
123
Engine/source/platformMac/macCarbMath.cpp
Normal 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();
|
||||
}
|
||||
|
||||
73
Engine/source/platformMac/macCarbMemory.cpp
Normal file
73
Engine/source/platformMac/macCarbMemory.cpp
Normal 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));
|
||||
}
|
||||
97
Engine/source/platformMac/macCarbMutex.cpp
Normal file
97
Engine/source/platformMac/macCarbMutex.cpp
Normal 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;
|
||||
}
|
||||
65
Engine/source/platformMac/macCarbProcessControl.cpp
Normal file
65
Engine/source/platformMac/macCarbProcessControl.cpp
Normal 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);
|
||||
}
|
||||
68
Engine/source/platformMac/macCarbSemaphore.cpp
Normal file
68
Engine/source/platformMac/macCarbSemaphore.cpp
Normal 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!");
|
||||
}
|
||||
69
Engine/source/platformMac/macCarbStrings.cpp
Normal file
69
Engine/source/platformMac/macCarbStrings.cpp
Normal 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);
|
||||
}
|
||||
|
||||
159
Engine/source/platformMac/macCarbThread.cpp
Normal file
159
Engine/source/platformMac/macCarbThread.cpp
Normal 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);
|
||||
}
|
||||
143
Engine/source/platformMac/macCarbTime.cpp
Normal file
143
Engine/source/platformMac/macCarbTime.cpp
Normal 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 <)
|
||||
{
|
||||
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 < )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
65
Engine/source/platformMac/macCarbUtil.cpp
Normal file
65
Engine/source/platformMac/macCarbUtil.cpp
Normal 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);
|
||||
}
|
||||
|
||||
189
Engine/source/platformMac/macCarbVolume.cpp
Normal file
189
Engine/source/platformMac/macCarbVolume.cpp
Normal 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;
|
||||
}
|
||||
79
Engine/source/platformMac/macCarbVolume.h
Normal file
79
Engine/source/platformMac/macCarbVolume.h
Normal 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_
|
||||
623
Engine/source/platformMac/macCocoaDialogs.mm
Normal file
623
Engine/source/platformMac/macCocoaDialogs.mm
Normal 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();
|
||||
}
|
||||
239
Engine/source/platformMac/macCocoaPlatform.mm
Normal file
239
Engine/source/platformMac/macCocoaPlatform.mm
Normal 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
|
||||
}
|
||||
78
Engine/source/platformMac/macDLibrary.cpp
Normal file
78
Engine/source/platformMac/macDLibrary.cpp
Normal 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;
|
||||
}
|
||||
78
Engine/source/platformMac/macGLUtils.h
Normal file
78
Engine/source/platformMac/macGLUtils.h
Normal 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
|
||||
194
Engine/source/platformMac/macMain.mm
Normal file
194
Engine/source/platformMac/macMain.mm
Normal 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();
|
||||
}
|
||||
180
Engine/source/platformMac/macMsgBox.mm
Normal file
180
Engine/source/platformMac/macMsgBox.mm
Normal 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;
|
||||
}
|
||||
30
Engine/source/platformMac/menus/mainMenu.nib/classes.nib
generated
Normal file
30
Engine/source/platformMac/menus/mainMenu.nib/classes.nib
generated
Normal 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>
|
||||
20
Engine/source/platformMac/menus/mainMenu.nib/info.nib
generated
Normal file
20
Engine/source/platformMac/menus/mainMenu.nib/info.nib
generated
Normal 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>
|
||||
BIN
Engine/source/platformMac/menus/mainMenu.nib/keyedobjects.nib
generated
Normal file
BIN
Engine/source/platformMac/menus/mainMenu.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
303
Engine/source/platformMac/menus/menuBarMac.cpp
Normal file
303
Engine/source/platformMac/menus/menuBarMac.cpp
Normal 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);
|
||||
}
|
||||
439
Engine/source/platformMac/menus/popupMenu.cpp
Normal file
439
Engine/source/platformMac/menus/popupMenu.cpp
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
175
Engine/source/platformMac/platformMacCarb.h
Normal file
175
Engine/source/platformMac/platformMacCarb.h
Normal 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_
|
||||
|
||||
BIN
Engine/source/platformMac/torqueDemo.icns
Normal file
BIN
Engine/source/platformMac/torqueDemo.icns
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue