2487 lines
78 KiB
C++
Executable File
2487 lines
78 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/types.h"
|
|
#if defined(TORQUE_OS_MAC_OSX)
|
|
// AAAAAAAAARGGGH.
|
|
// OSX Frameworks version of AGL.h includes CoreServices, which hits the
|
|
// OpenTransport-has-a-new-but-platform.h-#defs-new-to-something-nonstandard
|
|
// issue, so we need to pre-include OT here...
|
|
#include <OpenTransport.h>
|
|
#endif
|
|
|
|
#include "platformMacCarb/platformMacCarb.h"
|
|
#include "platformMacCarb/platformGL.h"
|
|
#include "platform/platform.h"
|
|
#include "platform/platformVideo.h"
|
|
#include "platformMacCarb/maccarbOGLVideo.h"
|
|
#include "platform/event.h"
|
|
#include "console/console.h"
|
|
#include "platformMacCarb/maccarbConsole.h"
|
|
#include "platform/platformInput.h"
|
|
//#include "platformMacCarb/maccarbInput.h"
|
|
#include "platform/gameInterface.h"
|
|
#include "math/mRandom.h"
|
|
#include "core/fileStream.h"
|
|
#include "game/resource.h"
|
|
#include "platformMacCarb/maccarbFileio.h"
|
|
#include "core/fileio.h"
|
|
#include "platform/platform.h"
|
|
|
|
//-------------------------------------- Mac stuff
|
|
|
|
#if defined(TORQUE_OS_MAC_CARB) //&& (CARBON_VERSION>=0x0120) // !!!TBD -- ASSUMES WE'RE ALWAYS RECENT CARBON LIB.
|
|
|
|
// !!!!TBD these could get eliminated if we're stable on the new event mgmt & event loop systems.
|
|
#define TARG_MACCARB_EVENTS 1
|
|
#define CARBEVENTS_RAEL (1 && TARG_MACCARB_EVENTS)
|
|
|
|
static MRandomLCG sgPlatRandom;
|
|
|
|
bool carbonEventsReady = false;
|
|
bool carbEventsRAEL = false;
|
|
|
|
// for OSX window layering control.
|
|
//#include "CGWindowLevel.h" // only if we reference the consts or funcs directly...
|
|
#if defined(TORQUE_OS_MAC_OSX)
|
|
#include <Carbon/Carbon.h>
|
|
#include <DrawSprocket/DrawSprocket.h>
|
|
#else
|
|
#include <MacWindows.h>
|
|
#include <DrawSprocket.h>
|
|
#endif
|
|
WindowGroupRef masterGroup = NULL;
|
|
|
|
#else // OS8-legacy-stuff.
|
|
|
|
#include <DrawSprocket.h>
|
|
|
|
#endif // MAC_CARB ifdef
|
|
|
|
#include <AGL/agl.h>
|
|
#include <stdio.h>
|
|
|
|
#if USE_SIOUX
|
|
#include <SIOUX.h>
|
|
#include <Gestalt.h>
|
|
#endif
|
|
|
|
//-------------------------------------- Resource Includes
|
|
#include "dgl/gBitmap.h"
|
|
#include "dgl/gFont.h"
|
|
|
|
extern void createFontInit();
|
|
extern void createFontShutdown();
|
|
|
|
void installCarbonEventHandlers(void);
|
|
void removeCarbonEventHandlers(void);
|
|
|
|
EventHandlerRef gWinEventHandlerRef, gAppEventHandlerRef, gTextEventHandlerRef;
|
|
|
|
bool gWindowCreated = false;
|
|
|
|
#if 0
|
|
char c;
|
|
#define TEST_STAGE(instr) printf(">> %s\n", instr); c = getchar();
|
|
#else
|
|
#define TEST_STAGE(s)
|
|
#endif
|
|
|
|
|
|
MacCarbPlatState platState;
|
|
|
|
MacCarbPlatState::MacCarbPlatState()
|
|
{
|
|
hDisplay = NULL;
|
|
appWindow = NULL;
|
|
|
|
quit = false;
|
|
|
|
ctx = NULL;
|
|
|
|
// start with something reasonable.
|
|
desktopBitsPixel = 16;
|
|
desktopWidth = 1024;
|
|
desktopHeight = 768;
|
|
|
|
osVersion = 0;
|
|
osX = false;
|
|
|
|
dStrcpy(appWindowTitle, "MacOS Torque Game Engine");
|
|
}
|
|
|
|
// THIS IS A GLOBAL! MUST BE CHANGED WHEN AFFECTING CURSOR VISIBILITY!
|
|
int cursorHidden = 0;
|
|
|
|
static bool windowLocked = false;
|
|
|
|
static U8 keyboardState[256];
|
|
static bool mouseButtonState[8];
|
|
static bool capsLockDown = false;
|
|
static S32 modifierKeys = 0;
|
|
static bool windowActive = true;
|
|
|
|
static Point2I lastCursorPos(0,0);
|
|
static Point2I windowSize;
|
|
//static bool sgDoubleByteEnabled = false;
|
|
|
|
static const char *getKeyName(S32 vkCode);
|
|
|
|
#define TICKS_FRONT 0L
|
|
#define TICKS_FRONT_X 1L
|
|
#define TICKS_BACK Platform::getBackgroundSleepTime()
|
|
#define TICKS_BACK_X Platform::getBackgroundSleepTime()
|
|
static bool gBackgrounded = false;
|
|
static long gSleepTicks = 1L; // start with something safe...
|
|
|
|
//--------------------------------------
|
|
static const char *getMessageName(S32 msg)
|
|
{
|
|
switch(msg)
|
|
{
|
|
// case WM_KEYDOWN:
|
|
// return "WM_KEYDOWN";
|
|
// case WM_KEYUP:
|
|
// return "WM_KEYUP";
|
|
// case WM_SYSKEYUP:
|
|
// return "WM_SYSKEYUP";
|
|
// case WM_SYSKEYDOWN:
|
|
// return "WM_SYSKEYDOWN";
|
|
default:
|
|
return "Unknown!!";
|
|
}
|
|
}
|
|
|
|
#if defined(TORQUE_OS_MAC_CARB)
|
|
//--------------------------------------
|
|
// helper function to load fnptrs on OSX
|
|
#include <CFBundle.h>
|
|
#include <Folders.h>
|
|
|
|
//-------------------------------------------------------------------------------
|
|
// Function name: LoadPrivateFrameworksBundle
|
|
// Summary: Looks in the application's Frameworks folder for a framework,
|
|
// and loads it if it finds it.
|
|
// Adpoted from a forum post by David 'FenrirWolf' Grace.
|
|
//-------------------------------------------------------------------------------
|
|
bool LoadPrivateFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr) {
|
|
// Looks in the application bundle for a framework, and loads it if it finds it.
|
|
|
|
CFURLRef privFrameworksURL, bundleURL;
|
|
if(!( privFrameworksURL = CFBundleCopyPrivateFrameworksURL( CFBundleGetMainBundle() )) ) {
|
|
return false;
|
|
}
|
|
|
|
|
|
if(!( bundleURL = CFURLCreateCopyAppendingPathComponent(NULL, privFrameworksURL, framework, false)) ) {
|
|
CFRelease(privFrameworksURL);
|
|
return false;
|
|
}
|
|
|
|
*bundlePtr = CFBundleCreate(NULL, bundleURL);
|
|
if (*bundlePtr && CFBundleLoadExecutable( *bundlePtr ) ) {
|
|
//found it under the apps private framework
|
|
CFRelease(privFrameworksURL);
|
|
CFRelease(bundleURL);
|
|
return true;
|
|
}
|
|
|
|
CFRelease(privFrameworksURL);
|
|
CFRelease(bundleURL);
|
|
if(*bundlePtr != NULL)
|
|
CFRelease(*bundlePtr);
|
|
return false;
|
|
}
|
|
|
|
|
|
// this function was adopted from Apple "CallMachOFramework" sample application
|
|
OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr)
|
|
{
|
|
const int numLocs = 5;
|
|
int maxLocs = numLocs - ((platState.osX)?0:1);
|
|
short vols[numLocs] = {kOnAppropriateDisk,
|
|
kOnAppropriateDisk,
|
|
kOnAppropriateDisk,
|
|
kLocalDomain,
|
|
kLocalDomain};
|
|
OSType folderType[numLocs] = {kPrivateFrameworksFolderType,
|
|
kFrameworksFolderType,
|
|
kApplicationSupportFolderType,
|
|
kFrameworksFolderType,
|
|
kDomainLibraryFolderType}; // OSX only searchpath.
|
|
OSStatus err;
|
|
FSRef frameworksFolderRef;
|
|
CFURLRef baseURL;
|
|
CFURLRef bundleURL;
|
|
|
|
if (bundlePtr==NULL) return(-1);
|
|
|
|
*bundlePtr = NULL;
|
|
|
|
// Look in the application's Frameworks folder before looking in the system
|
|
if( LoadPrivateFrameworkBundle(framework, bundlePtr) )
|
|
return noErr ; // Yay! We found it, so return with no error..
|
|
// Otherwise, fall through to Torques usual bundle load logic
|
|
|
|
|
|
baseURL = NULL;
|
|
bundleURL = NULL;
|
|
|
|
for (int i = 0; i<maxLocs; i++)
|
|
{
|
|
err = FSFindFolder(vols[i], folderType[i], true, &frameworksFolderRef);
|
|
if (err == noErr) {
|
|
baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef);
|
|
if (baseURL == NULL) {
|
|
err = coreFoundationUnknownErr;
|
|
}
|
|
}
|
|
if (err == noErr) {
|
|
bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false);
|
|
if (bundleURL == NULL) {
|
|
err = coreFoundationUnknownErr;
|
|
}
|
|
}
|
|
if (err == noErr) {
|
|
*bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
|
|
if (*bundlePtr == NULL) {
|
|
err = coreFoundationUnknownErr;
|
|
}
|
|
}
|
|
if (err == noErr) {
|
|
if ( ! CFBundleLoadExecutable( *bundlePtr ) ) {
|
|
err = coreFoundationUnknownErr;
|
|
}
|
|
else // GOT IT!
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Clean up.
|
|
if (err != noErr && *bundlePtr != NULL)
|
|
{
|
|
CFRelease(*bundlePtr);
|
|
*bundlePtr = NULL;
|
|
}
|
|
if (bundleURL != NULL)
|
|
CFRelease(bundleURL);
|
|
if (baseURL != NULL)
|
|
CFRelease(baseURL);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
// typedefs for the fns we need:
|
|
#include <CGDirectDisplay.h>
|
|
typedef CGDisplayErr (*CGGDWPPtr) (CGPoint p, CGDisplayCount m, CGDirectDisplayID *d, CGDisplayCount *c);
|
|
typedef CGRect (*CGDBPtr) (CGDirectDisplayID d);
|
|
typedef CGDisplayErr (*CGDMCTPPtr) (CGDirectDisplayID d, CGPoint p);
|
|
|
|
static CGGDWPPtr cgddGetDisplay = NULL;
|
|
static CGDBPtr cgddBounds = NULL;
|
|
static CGDMCTPPtr cgddTeleportMouse = NULL;
|
|
|
|
static OSStatus LoadCGDDFunctions()
|
|
{
|
|
CFBundleRef cgBundle;
|
|
OSStatus err = cfragNoSymbolErr;
|
|
|
|
if (!platState.osX) return(err);
|
|
|
|
// err = LoadFrameworkBundle(CFSTR("CoreGraphics.framework"), &cgBundle);
|
|
err = LoadFrameworkBundle(CFSTR("ApplicationServices.framework"), &cgBundle);
|
|
if (err!=noErr) return(err);
|
|
|
|
cgddGetDisplay = (CGGDWPPtr) CFBundleGetFunctionPointerForName( cgBundle, CFSTR("CGGetDisplaysWithPoint") );
|
|
if (cgddGetDisplay == NULL)
|
|
err = cfragNoSymbolErr;
|
|
cgddBounds = (CGDBPtr) CFBundleGetFunctionPointerForName( cgBundle, CFSTR("CGDisplayBounds") );
|
|
if (cgddBounds == NULL)
|
|
err = cfragNoSymbolErr;
|
|
cgddTeleportMouse = (CGDMCTPPtr) CFBundleGetFunctionPointerForName( cgBundle, CFSTR("CGDisplayMoveCursorToPoint") );
|
|
if (cgddTeleportMouse == NULL)
|
|
err = cfragNoSymbolErr;
|
|
|
|
return(err);
|
|
}
|
|
#endif //TARG_MACCARB
|
|
|
|
|
|
void Platform::AlertOK(const char *windowTitle, const char *message)
|
|
{
|
|
/*
|
|
S16 hit;
|
|
Str255 title, desc;
|
|
str2p(windowTitle, title);
|
|
str2p(message, desc);
|
|
|
|
Input::deactivate();
|
|
|
|
StandardAlert(0, title, desc, NULL, &hit);
|
|
*/
|
|
S16 hit;
|
|
AlertStdAlertParamRec param;
|
|
Str255 title, desc;
|
|
str2p(windowTitle, title);
|
|
str2p(message, desc);
|
|
param.movable = true;
|
|
param.helpButton = false;
|
|
param.filterProc = NULL;
|
|
param.defaultText = (StringPtr)kAlertDefaultOKText;
|
|
param.cancelText = NULL;
|
|
param.otherText = NULL;
|
|
param.defaultButton = kAlertStdAlertOKButton;
|
|
param.cancelButton = 0;
|
|
param.position = kWindowDefaultPosition;
|
|
// BUG: when in full screen, the alert doesnt show. The best fix for this is new mac platf.
|
|
if( Video::isFullScreen()) {
|
|
Con::printf("Cannot display alerts in full screen mode. Dumping message to console...");
|
|
Con::errorf("%s, %s",windowTitle,message);
|
|
return;
|
|
}
|
|
|
|
removeCarbonEventHandlers(); /// stop capturing all events
|
|
ShowCursor(); /// give user a mouse cursor with which to click buttons
|
|
StandardAlert(0, title, desc, ¶m, &hit); /// show the alert box
|
|
installCarbonEventHandlers(); /// capture events again.
|
|
HideCursor(); /// hide system cursor, so we use torque's cursor again
|
|
return; // (hit==kAlertStdAlertOKButton);
|
|
}
|
|
|
|
bool Platform::AlertOKCancel(const char *windowTitle, const char *message)
|
|
{
|
|
S16 hit;
|
|
AlertStdAlertParamRec param;
|
|
Str255 title, desc;
|
|
str2p(windowTitle, title);
|
|
str2p(message, desc);
|
|
param.movable = true;
|
|
param.helpButton = false;
|
|
param.filterProc = NULL;
|
|
param.defaultText = (StringPtr)kAlertDefaultOKText;
|
|
param.cancelText = (StringPtr)kAlertDefaultCancelText;
|
|
param.otherText = NULL;
|
|
param.defaultButton = kAlertStdAlertOKButton;
|
|
param.cancelButton = kAlertStdAlertCancelButton;
|
|
param.position = kWindowDefaultPosition;
|
|
// BUG: when in full screen, the alert doesnt show. The best fix for this is new mac platf.
|
|
if( Video::isFullScreen()) {
|
|
Con::printf("Cannot display alerts in full screen mode. Dumping message to console...");
|
|
Con::errorf("%s, %s",windowTitle,message);
|
|
return true; // always return "OK".
|
|
}
|
|
|
|
removeCarbonEventHandlers(); /// stop capturing all events
|
|
ShowCursor(); /// give user a mouse cursor with which to click buttons
|
|
StandardAlert(0, title, desc, ¶m, &hit); /// show the alert box
|
|
installCarbonEventHandlers(); /// capture events again.
|
|
HideCursor(); /// hide system cursor, so we use torque's cursor again
|
|
return (hit==kAlertStdAlertOKButton);
|
|
}
|
|
|
|
bool Platform::AlertRetry(const char *windowTitle, const char *message)
|
|
{
|
|
S16 hit;
|
|
AlertStdAlertParamRec param;
|
|
Str255 title, desc;
|
|
Str255 retryStr;
|
|
str2p(windowTitle, title);
|
|
str2p(message, desc);
|
|
str2p("Retry", retryStr);
|
|
param.movable = true;
|
|
param.helpButton = false;
|
|
param.filterProc = NULL;
|
|
param.defaultText = retryStr;
|
|
param.cancelText = (StringPtr)kAlertDefaultCancelText;
|
|
param.otherText = NULL;
|
|
param.defaultButton = kAlertStdAlertOKButton;
|
|
param.cancelButton = kAlertStdAlertCancelButton;
|
|
param.position = kWindowDefaultPosition;
|
|
// BUG: when in full screen, the alert doesnt show. The best fix for this is new mac platf.
|
|
if( Video::isFullScreen()) {
|
|
Con::printf("Cannot display alerts in full screen mode. Dumping message to console...");
|
|
Con::errorf("%s, %s",windowTitle,message);
|
|
return false; // always return 'cancel', as asserts use alertRetry, and constantly retrying would be bad.
|
|
}
|
|
|
|
removeCarbonEventHandlers(); /// stop capturing all events
|
|
ShowCursor(); /// give user a mouse cursor with which to click buttons
|
|
StandardAlert(0, title, desc, ¶m, &hit); /// show the alert box
|
|
installCarbonEventHandlers(); /// capture events again.
|
|
HideCursor(); /// hide system cursor, so we use torque's cursor again
|
|
return (hit==kAlertStdAlertOKButton);
|
|
}
|
|
|
|
//--------------------------------------
|
|
static void InitInput()
|
|
{
|
|
dMemset( keyboardState, 0, 256 );
|
|
dMemset( mouseButtonState, 0, sizeof( mouseButtonState ) );
|
|
|
|
if (platState.osX)
|
|
{
|
|
OSStatus err = LoadCGDDFunctions();
|
|
}
|
|
}
|
|
|
|
static Point& ClientToScreen( WindowPtr wnd, Point pt )
|
|
{
|
|
static Point screen;
|
|
screen = pt;
|
|
|
|
GrafPtr savePort;
|
|
GetPort( &savePort );
|
|
SetPortWindowPort(platState.appWindow);
|
|
LocalToGlobal( &screen );
|
|
SetPort( savePort );
|
|
|
|
return screen;
|
|
}
|
|
|
|
|
|
void GetWindowRect( WindowPtr wnd, RectI *pRect )
|
|
{
|
|
// note - I should probably add a check for title bar, and borders to simulate Windows completely.
|
|
if ( pRect && wnd )
|
|
{
|
|
GrafPtr port;
|
|
Rect r;
|
|
GetPort( &port );
|
|
SetPortWindowPort(wnd);
|
|
GetWindowPortBounds(wnd, &r);
|
|
SetPort( port );
|
|
|
|
pRect->point.x = r.left;
|
|
pRect->point.y = r.top;
|
|
pRect->extent.x = r.right - r.left;
|
|
pRect->extent.y = r.bottom - r.top;
|
|
}
|
|
}
|
|
|
|
|
|
static void RecenterCapturedMouse(void)
|
|
{
|
|
if (!platState.appWindow || !windowLocked)
|
|
return;
|
|
|
|
Rect r;
|
|
Point wndCenter;
|
|
GrafPtr savePort;
|
|
GetPort( &savePort );
|
|
SetPortWindowPort(platState.appWindow);
|
|
GetWindowPortBounds(platState.appWindow, &r);
|
|
wndCenter.h = r.left + (r.right-r.left)>>1;
|
|
wndCenter.v = r.top + (r.bottom-r.top)>>1;
|
|
LocalToGlobal( &wndCenter );
|
|
|
|
if (!platState.osX)
|
|
{
|
|
}
|
|
#if defined(TORQUE_OS_MAC_CARB)
|
|
else // on OSX, we use CGDD functions.
|
|
if (cgddGetDisplay!=NULL) // then we have the funcs we need.
|
|
{
|
|
CGDirectDisplayID cgdid;
|
|
CGDisplayCount cgdc;
|
|
CGPoint cgp;
|
|
cgp.x = wndCenter.h;
|
|
cgp.y = wndCenter.v;
|
|
cgddGetDisplay(cgp, 1, &cgdid, &cgdc);
|
|
if (cgdc) // had better be non-zero!!!
|
|
{
|
|
CGRect cgr = cgddBounds(cgdid);
|
|
cgp.x = wndCenter.h - cgr.origin.x;
|
|
cgp.y = wndCenter.v - cgr.origin.y;
|
|
cgddTeleportMouse(cgdid, cgp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SetPort( savePort );
|
|
}
|
|
|
|
|
|
static void GetMousePos(Point *pt)
|
|
{
|
|
GrafPtr savePort;
|
|
GetPort( &savePort );
|
|
SetPortWindowPort( platState.appWindow );
|
|
GetMouse(pt);
|
|
SetPort( savePort );
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static bool HandleUpdate(WindowPtr w)
|
|
{
|
|
if (w && w == platState.appWindow)
|
|
{
|
|
#if TARG_MACCARB_EVENTS
|
|
if (!carbonEventsReady)
|
|
#endif
|
|
BeginUpdate(w);
|
|
Game->refreshWindow();
|
|
#if TARG_MACCARB_EVENTS
|
|
if (!carbonEventsReady)
|
|
#endif
|
|
EndUpdate(w);
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static void HandleActivate(WindowPtr w, bool winActive, bool appActive)
|
|
{
|
|
if (w && w==platState.appWindow)
|
|
{
|
|
if (appActive)
|
|
{
|
|
ShowWindow(w);
|
|
SelectWindow(w);
|
|
BringToFront(w);
|
|
|
|
cursorHidden = 0;
|
|
Input::activate();
|
|
|
|
// Video::reactivate();
|
|
}
|
|
else
|
|
{
|
|
// Video::deactivate();
|
|
|
|
// if any input captured -- release all input states so we don't errantly keep them locked...
|
|
Input::deactivate();
|
|
InitCursor(); // reset cursor state.
|
|
cursorHidden = 0;
|
|
}
|
|
|
|
if (winActive)
|
|
{
|
|
ShowWindow(w);
|
|
SelectWindow(w);
|
|
BringToFront(w);
|
|
|
|
cursorHidden = 0;
|
|
Input::activate();
|
|
}
|
|
else
|
|
{
|
|
// if any input captured -- release all input states so we don't errantly keep them locked...
|
|
Input::deactivate();
|
|
InitCursor(); // reset cursor state.
|
|
cursorHidden = 0;
|
|
}
|
|
|
|
HandleUpdate(w);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------
|
|
void Platform::setWindowLocked(bool locked)
|
|
{
|
|
bool noChange = (windowLocked==locked);
|
|
|
|
if (!noChange)
|
|
{
|
|
windowLocked = locked;
|
|
|
|
// do anything needed on state change. like mouse recentering...
|
|
if (windowLocked)
|
|
{
|
|
// reset the mouse cursor to center
|
|
RecenterCapturedMouse();
|
|
|
|
// need to do this for sanity.
|
|
//HideMenuBar();
|
|
///tbd this needs to funnel, so fullscreen & windowed share state tracking...
|
|
}
|
|
else
|
|
{
|
|
//ShowMenuBar();
|
|
///tbd this needs to funnel, so fullscreen & windowed share state tracking...
|
|
}
|
|
// setMouseClipping();
|
|
}
|
|
}
|
|
|
|
|
|
#if TARG_MACCARB_EVENTS
|
|
//--------------------------------------
|
|
// the following are the two event handlers
|
|
// we custom install.
|
|
//--------------------------------------
|
|
|
|
static bool inWinHandlerAlready = false;
|
|
|
|
//--------------------------------------
|
|
pascal OSStatus DoWinCarbonEventHandling(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
|
|
{
|
|
if (inWinHandlerAlready)
|
|
return(eventNotHandledErr);
|
|
|
|
inWinHandlerAlready = true;
|
|
|
|
OSStatus result = noErr; // Function result
|
|
|
|
UInt32 eventClass = GetEventClass(theEvent);
|
|
UInt32 eventKind = GetEventKind(theEvent);
|
|
|
|
InputEvent vev;
|
|
|
|
// clear the event value. make sure to not clear the Event initialized fields!!!
|
|
dMemset(((char*)&vev)+sizeof(Event), 0, sizeof(vev)-sizeof(Event));
|
|
|
|
/* Your preprocessing here */
|
|
|
|
// Do preprocessing
|
|
/*
|
|
if (eventClass==kEventClassMouse && eventKind==kEventMouseDown)
|
|
result = -1;
|
|
if (eventClass==kEventClassKeyboard && eventKind==kEventRawKeyDown)
|
|
result = -2;
|
|
*/
|
|
|
|
// we used to only process if the next handler up the chain handled the event, or did not handle it.
|
|
// we would only skip handling event if there was some error.
|
|
// propogation has been moved down below.
|
|
|
|
/* Your postprocessing here */
|
|
result = noErr; // clear result state.
|
|
UInt32 keyMods = 0;
|
|
GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32,
|
|
NULL, sizeof(UInt32), NULL, &keyMods);
|
|
|
|
modifierKeys = 0;
|
|
if (keyMods & shiftKey) modifierKeys |= SI_LSHIFT;
|
|
if (keyMods & rightShiftKey) modifierKeys |= SI_RSHIFT;
|
|
if (keyMods & cmdKey) modifierKeys |= SI_LALT;
|
|
if (keyMods & optionKey) modifierKeys |= SI_MAC_LOPT;
|
|
if (keyMods & rightOptionKey) modifierKeys |= SI_MAC_ROPT;
|
|
if (keyMods & controlKey) modifierKeys |= SI_LCTRL;
|
|
if (keyMods & rightControlKey) modifierKeys |= SI_RCTRL;
|
|
|
|
// Do postprocessing
|
|
switch(eventClass)
|
|
{
|
|
case kEventClassKeyboard:
|
|
{
|
|
bool makeBeforeBreak = false;
|
|
UInt32 keyCode = 0;
|
|
char keyChar = 0;
|
|
|
|
GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32,
|
|
NULL, sizeof(UInt32), NULL, &keyCode);
|
|
GetEventParameter (theEvent, kEventParamKeyMacCharCodes, typeChar,
|
|
NULL, sizeof(char), NULL, &keyChar);
|
|
|
|
switch(eventKind)
|
|
{
|
|
case kEventRawKeyModifiersChanged:
|
|
break; // until we figure out just what this does...
|
|
|
|
case kEventRawKeyDown:
|
|
case kEventRawKeyUp:
|
|
case kEventRawKeyRepeat:
|
|
{
|
|
vev.deviceType = KeyboardDeviceType;
|
|
vev.objType = SI_KEY;
|
|
vev.objInst = TranslateOSKeyCode(keyCode);
|
|
vev.modifier = modifierKeys;
|
|
vev.ascii = 0;
|
|
|
|
switch(eventKind)
|
|
{
|
|
case kEventRawKeyDown:
|
|
vev.action = SI_MAKE;
|
|
vev.fValue = 1.0f;
|
|
keyboardState[keyCode] = 1; // track state.
|
|
break;
|
|
case kEventRawKeyUp:
|
|
vev.action = SI_BREAK;
|
|
vev.fValue = 0.0f;
|
|
keyboardState[keyCode] = 0; // track state.
|
|
break;
|
|
case kEventRawKeyRepeat:
|
|
vev.action = SI_REPEAT;
|
|
vev.fValue = 1.0f;
|
|
keyboardState[keyCode] = 2; // track state.
|
|
break;
|
|
case kEventRawKeyModifiersChanged:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// HACK HACK HACK
|
|
// directly check for command-Q to force exit immediately!!!!!!TBD
|
|
if ((vev.objInst == KEY_Q) && (keyMods & cmdKey))
|
|
Platform::postQuitMessage(0);
|
|
|
|
Game->postEvent(vev);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case kEventClassMouse:
|
|
{
|
|
switch(eventKind)
|
|
{
|
|
case kEventMouseDown:
|
|
case kEventMouseUp:
|
|
{
|
|
EventMouseButton whatButton;
|
|
GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton,
|
|
NULL, sizeof(EventMouseButton), NULL, &whatButton);
|
|
|
|
vev.deviceType = MouseDeviceType;
|
|
vev.objType = SI_BUTTON;
|
|
vev.objInst = KEY_BUTTON0 + whatButton - 1; // we assume the KEY_BUTTON values are in order, in seq.
|
|
vev.modifier = modifierKeys;
|
|
vev.ascii = 0;
|
|
vev.action = (eventKind==kEventMouseDown)?SI_MAKE:SI_BREAK;
|
|
vev.fValue = (eventKind==kEventMouseDown)?1.0:0.0;
|
|
Game->postEvent(vev);
|
|
mouseButtonState[whatButton-1] = (eventKind==kEventMouseDown)?1:0;
|
|
break;
|
|
}
|
|
|
|
case kEventMouseMoved:
|
|
case kEventMouseDragged:
|
|
{
|
|
GrafPtr savePort;
|
|
GetPort( &savePort );
|
|
|
|
Point where;
|
|
GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint,
|
|
NULL, sizeof(Point), NULL, &where);
|
|
|
|
if (platState.appWindow)
|
|
{
|
|
Rect r;
|
|
|
|
// gee, almost forgot about this -- value is global, we want it window-local.
|
|
SetPortWindowPort(platState.appWindow);
|
|
GlobalToLocal(&where);
|
|
GetWindowPortBounds(platState.appWindow, &r);
|
|
SetPort(savePort);
|
|
|
|
// if mouse is in our window, make sure it is hidden. if not, it should be visible.
|
|
bool inWindow = PtInRect(where, &r);
|
|
if (cursorHidden && !inWindow)
|
|
{
|
|
ShowCursor();
|
|
cursorHidden = 0;
|
|
}
|
|
else
|
|
if (!cursorHidden && inWindow)
|
|
{
|
|
HideCursor();
|
|
cursorHidden = 1;
|
|
}
|
|
}
|
|
|
|
// this is a hack workaround to manage the fact that the game wants delta values...
|
|
static bool reset = true;
|
|
static Point lastMouseLoc = {-11,-11}; // something unlikely...
|
|
if (windowLocked && !platState.osX)
|
|
if (!windowActive || gBackgrounded) //!!!!TBD windowActive/Locked should go false if bg.
|
|
{
|
|
reset = true;
|
|
break; // out of this case.
|
|
}
|
|
|
|
static int wasLocked = 0;
|
|
if (!windowLocked)
|
|
{ // then we want the raw, global mouse position.
|
|
MouseMoveEvent mme;
|
|
mme.xPos = where.h; // horizontal position of cursor
|
|
mme.yPos = where.v; // vertical position of cursor
|
|
mme.modifier = modifierKeys;
|
|
Game->postEvent(mme);
|
|
// if we care, we can check locked state flipping back.
|
|
// for now, just set flag.
|
|
wasLocked = 0;
|
|
}
|
|
else
|
|
{
|
|
vev.deviceType = MouseDeviceType;
|
|
vev.objInst = 0;
|
|
vev.modifier = modifierKeys;
|
|
vev.ascii = 0;
|
|
vev.action = SI_MOVE;
|
|
|
|
#if defined(TORQUE_OS_MAC_OSX)
|
|
// we don't actually use the event data.
|
|
// we grab the raw delta from coregraphics.
|
|
CGMouseDelta mdx, mdy;
|
|
CGGetLastMouseDelta(&mdx, &mdy);
|
|
#else
|
|
// HACK for input control in-game when CFM on X, or when ISp temp disabled.
|
|
// There needs to be Input:: takeover of user input
|
|
// when the window is locked (gameplay)
|
|
int mdx, mdy;
|
|
if (platState.osX)
|
|
{ // try to use carbon system for retrieving delta...
|
|
GetEventParameter (theEvent, kEventParamMouseDelta, typeQDPoint,
|
|
NULL, sizeof(Point), NULL, &where);
|
|
mdx = where.h;
|
|
mdy = where.v;
|
|
}
|
|
else
|
|
{ // try to determine delta ourselves, and not use carbon system...
|
|
// where already contains curr position...
|
|
if (reset)
|
|
{
|
|
reset = false;
|
|
lastMouseLoc = where;
|
|
break; // this case.
|
|
}
|
|
|
|
mdx = where.h - lastMouseLoc.h;
|
|
mdy = where.v - lastMouseLoc.v;
|
|
lastMouseLoc = where;
|
|
}
|
|
#endif
|
|
|
|
if (wasLocked>1) // ignore first two events, so we skip errant behaviors...
|
|
{ // post the event(s), possibly clamped a bit...
|
|
const int MAX_MOUSE_MOVE = 256;
|
|
if (mdx)
|
|
{
|
|
if (mdx > MAX_MOUSE_MOVE)
|
|
mdx = MAX_MOUSE_MOVE;
|
|
else if (mdx < -MAX_MOUSE_MOVE)
|
|
mdx = -MAX_MOUSE_MOVE;
|
|
vev.objType = SI_XAXIS;
|
|
vev.fValue = F32(mdx);
|
|
Game->postEvent(vev);
|
|
}
|
|
if (mdy)
|
|
{
|
|
if (mdy > MAX_MOUSE_MOVE)
|
|
mdy = MAX_MOUSE_MOVE;
|
|
else if (mdy < -MAX_MOUSE_MOVE)
|
|
mdy = -MAX_MOUSE_MOVE;
|
|
vev.objType = SI_YAXIS;
|
|
vev.fValue = F32(mdy);
|
|
Game->postEvent(vev);
|
|
}
|
|
}
|
|
else
|
|
wasLocked++; // else drop the events ON THE FLOOR.
|
|
|
|
// reset the mouse cursor to center
|
|
RecenterCapturedMouse();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case kEventMouseWheelMoved:
|
|
{
|
|
long dWheel = 0;
|
|
GetEventParameter (theEvent, kEventParamMouseWheelDelta, typeLongInteger,
|
|
NULL, sizeof(long), NULL, &dWheel);
|
|
|
|
vev.deviceType = MouseDeviceType;
|
|
vev.objType = SI_ZAXIS;
|
|
vev.objInst = 0;
|
|
vev.modifier = 0;
|
|
vev.ascii = 0;
|
|
vev.action = SI_MOVE;
|
|
vev.fValue = dWheel; // !!!!TBD -- multiplication factor!!!!????
|
|
Game->postEvent(vev);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kEventClassWindow:
|
|
{
|
|
WindowRef theWind;
|
|
GetEventParameter (theEvent, kEventParamDirectObject, typeWindowRef,
|
|
NULL, sizeof(WindowRef), NULL, &theWind);
|
|
|
|
switch(eventKind)
|
|
{
|
|
case kEventWindowDrawContent:
|
|
{
|
|
// !!!!TBD. Do we ned to bother checking theWind == platState.appWindow?
|
|
Game->refreshWindow();
|
|
break;
|
|
}
|
|
|
|
case kEventWindowActivated:
|
|
case kEventWindowDeactivated:
|
|
{
|
|
// !!!!TBD. Do we ned to bother checking theWind == platState.appWindow?
|
|
HandleActivate(theWind, eventKind==kEventWindowActivated, !gBackgrounded);
|
|
Game->refreshWindow();
|
|
// mark that we didn't handle, so hopefully someone else will do the right thing.
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
|
|
|
|
#define PROPAGATE_EVENTS 1
|
|
#if PROPAGATE_EVENTS // whether to propagate...
|
|
// Now propagate the event to the next handler (in this case the standard window handler)
|
|
if (nextHandler)
|
|
{
|
|
result = CallNextEventHandler (nextHandler, theEvent);
|
|
}
|
|
#endif
|
|
|
|
|
|
inWinHandlerAlready = false; // ending.
|
|
|
|
return result; // Report result
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
pascal OSStatus DoAppCarbonEventHandling(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
|
|
{
|
|
OSStatus result = noErr; // Function result
|
|
|
|
UInt32 eventClass = GetEventClass(theEvent);
|
|
UInt32 eventKind = GetEventKind(theEvent);
|
|
|
|
InputEvent vev;
|
|
|
|
// clear the event value. make sure to not clear the Event initialized fields!!!
|
|
dMemset(((char*)&vev)+sizeof(Event), 0, sizeof(vev)-sizeof(Event));
|
|
|
|
/* Your preprocessing here */
|
|
|
|
#if PROPAGATE_EVENTS // whether to propagate...
|
|
// Now propagate the event to the next handler (in this case the standard window handler)
|
|
if (nextHandler)
|
|
result = CallNextEventHandler (nextHandler, theEvent);
|
|
#endif
|
|
if (result == noErr
|
|
|| result == eventNotHandledErr) // Did it succeed or was unhandled?
|
|
{
|
|
/* Your postprocessing here */
|
|
result = noErr; // clear result state.
|
|
|
|
// Do postprocessing
|
|
switch(eventClass)
|
|
{
|
|
case kEventClassAppleEvent:
|
|
{
|
|
AEEventID aeType;
|
|
GetEventParameter (theEvent, kEventParamAEEventID, typeType,
|
|
NULL, sizeof(OSType), NULL, &aeType);
|
|
|
|
switch(aeType)
|
|
{
|
|
case kAEQuitApplication:
|
|
{
|
|
Platform::postQuitMessage(0);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
// AEProcessAppleEvent(&msg?????);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case kEventClassApplication:
|
|
{
|
|
switch(eventKind)
|
|
{
|
|
case kEventAppQuit:
|
|
{
|
|
// !!!!!TBD -- we assume this only happens AFTER we posted a quit event...
|
|
break;
|
|
}
|
|
|
|
case kEventAppActivated:
|
|
case kEventAppDeactivated:
|
|
{
|
|
bool activating = (eventKind==kEventAppActivated);
|
|
|
|
HandleActivate(platState.appWindow, activating, activating);
|
|
gBackgrounded = !activating;
|
|
if (gBackgrounded)
|
|
gSleepTicks = TICKS_BACK_X;
|
|
else
|
|
gSleepTicks = TICKS_FRONT_X;
|
|
/*
|
|
Cursor arrow;
|
|
GetQDGlobalsArrow(&arrow);
|
|
if (gBackgrounded) // just suspended
|
|
SetCursor(&arrow);
|
|
else // just resumed
|
|
SetCursor(&arrow);
|
|
*/
|
|
// mark it so that others will try to 'do the right thing' for us.
|
|
result = eventNotHandledErr;
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case kEventClassKeyboard:
|
|
{
|
|
switch(eventKind)
|
|
{
|
|
case kEventRawKeyModifiersChanged: // until we figure out just what this does...
|
|
case kEventRawKeyDown:
|
|
case kEventRawKeyUp:
|
|
case kEventRawKeyRepeat:
|
|
{
|
|
// HACK HACK HACK HACK
|
|
// this should really be dispatched through the right
|
|
// dispatching mechanism to the main window...
|
|
if (!inWinHandlerAlready)
|
|
result = DoWinCarbonEventHandling(NULL, theEvent, userData);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kEventClassMouse:
|
|
{
|
|
switch(eventKind)
|
|
{
|
|
case kEventMouseDown:
|
|
case kEventMouseUp:
|
|
case kEventMouseMoved:
|
|
case kEventMouseDragged:
|
|
case kEventMouseWheelMoved:
|
|
{
|
|
// HACK HACK HACK HACK
|
|
// this should really be dispatched through the right
|
|
// dispatching mechanism to the main window...
|
|
if (!inWinHandlerAlready)
|
|
result = DoWinCarbonEventHandling(NULL, theEvent, userData);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
result = eventNotHandledErr;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return result; // Report result
|
|
}
|
|
//--------------------------------------
|
|
#endif // targ_maccarb_events
|
|
|
|
//--------------------------------------
|
|
static Point lastPos = {-11,-11}; // something unlikely...
|
|
static void CheckCursorPos()
|
|
{
|
|
static bool reset = true;
|
|
|
|
if (!windowActive || gBackgrounded) //!!!!TBD windowActive/Locked should go false if bg.
|
|
{
|
|
reset = true;
|
|
return;
|
|
}
|
|
|
|
InputEvent event;
|
|
Point mousePos;
|
|
GetMousePos(&mousePos);
|
|
|
|
if (!windowLocked)
|
|
{
|
|
// since we're in Poll mode, we need to post a mousemove event for the cursor to be updated.
|
|
MouseMoveEvent mme;
|
|
mme.xPos = mousePos.h; // horizontal position of cursor
|
|
mme.yPos = mousePos.v; // vertical position of cursor
|
|
mme.modifier = modifierKeys;
|
|
Game->postEvent(mme);
|
|
}
|
|
else
|
|
{
|
|
if (reset)
|
|
reset = false;
|
|
else
|
|
if (mousePos.h!=lastPos.h
|
|
&& mousePos.v!=lastPos.v) // mouse moved.
|
|
{
|
|
if(mousePos.h != lastPos.h)
|
|
{
|
|
event.deviceInst = 0;
|
|
event.deviceType = MouseDeviceType;
|
|
event.objType = SI_XAXIS;
|
|
event.objInst = 0;
|
|
event.action = SI_MOVE;
|
|
event.modifier = modifierKeys;
|
|
event.ascii = 0;
|
|
event.fValue = F32(mousePos.h - lastPos.h);
|
|
Game->postEvent(event);
|
|
// Con::printf( "EVENT: Mouse move (%.1f, 0.0).\n", event.fValue );
|
|
}
|
|
|
|
if(mousePos.v != lastPos.v)
|
|
{
|
|
event.deviceInst = 0;
|
|
event.deviceType = MouseDeviceType;
|
|
event.objType = SI_YAXIS;
|
|
event.objInst = 0;
|
|
event.action = SI_MOVE;
|
|
event.modifier = modifierKeys;
|
|
event.ascii = 0;
|
|
event.fValue = F32(mousePos.v - lastPos.v);
|
|
Game->postEvent(event);
|
|
// Con::printf( "EVENT: Mouse move (0.0, %.1f).\n", event.fValue );
|
|
}
|
|
|
|
}
|
|
|
|
lastPos = mousePos; // copy over last position.
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#define WINDOW_RESTRICT_EVENTS 0
|
|
|
|
static void ProcessKeyboard( EventRecord &msg )
|
|
{
|
|
#if WINDOW_RESTRICT_EVENTS
|
|
WindowPtr which;
|
|
FindWindow( msg.where, &which );
|
|
if (which != platState.appWindow)
|
|
return;
|
|
#endif
|
|
|
|
#if ALLOW_MENU_PROCESSING
|
|
unsigned char c;
|
|
c = (msg.message & charCodeMask);
|
|
if ((msg.modifiers & cmdKey) != 0)
|
|
{
|
|
AdjustMenus();
|
|
HandleMenuKey(c, msg.modifiers);
|
|
}
|
|
#endif
|
|
|
|
InputEvent event;
|
|
event.deviceInst = 0;
|
|
event.deviceType = KeyboardDeviceType;
|
|
event.objType = SI_KEY;
|
|
event.objInst = TranslateOSKeyCode( (msg.message & keyCodeMask) >> 8 );
|
|
event.ascii = 0;
|
|
|
|
switch(msg.what)
|
|
{
|
|
case keyDown:
|
|
event.action = SI_MAKE;
|
|
event.fValue = 1.0f;
|
|
//Con::printf("keyDN ( %02x )", (msg.message & keyCodeMask) >> 8);
|
|
break;
|
|
|
|
case autoKey:
|
|
event.action = SI_REPEAT;
|
|
event.fValue = 1.0f;
|
|
break;
|
|
|
|
case keyUp:
|
|
event.action = SI_BREAK;
|
|
event.fValue = 0.0f;
|
|
//Con::printf("keyUP ( %02x )", (msg.message & keyCodeMask) >> 8);
|
|
break;
|
|
}
|
|
|
|
event.modifier = 0;
|
|
if (msg.modifiers & shiftKey) event.modifier |= SI_LSHIFT;
|
|
if (msg.modifiers & rightShiftKey) event.modifier |= SI_RSHIFT;
|
|
if (msg.modifiers & cmdKey) event.modifier |= SI_LALT;
|
|
if (msg.modifiers & optionKey) event.modifier |= SI_MAC_LOPT;
|
|
if (msg.modifiers & rightOptionKey) event.modifier |= SI_MAC_ROPT;
|
|
if (msg.modifiers & controlKey) event.modifier |= SI_LCTRL;
|
|
if (msg.modifiers & rightControlKey) event.modifier |= SI_RCTRL;
|
|
|
|
// handle command-Q exit
|
|
if ((event.objInst == KEY_Q) && (msg.modifiers & cmdKey))
|
|
Platform::postQuitMessage(0);
|
|
|
|
Game->postEvent(event);
|
|
}
|
|
|
|
|
|
static void ProcessMouse( EventRecord &msg )
|
|
{
|
|
WindowPtr which;
|
|
U16 where = FindWindow( msg.where, &which );
|
|
#if WINDOW_RESTRICT_EVENTS
|
|
if (which != platState.appWindow)
|
|
return;
|
|
#endif
|
|
|
|
SelectWindow(platState.appWindow);
|
|
|
|
// handle a little window maintence
|
|
switch (where)
|
|
{
|
|
#if !TARGET_API_MAC_CARBON
|
|
case inSysWindow:
|
|
SystemClick ( &msg, platState.appWindow );
|
|
return;
|
|
#endif
|
|
|
|
case inDrag:
|
|
{
|
|
RgnHandle rgn = GetGrayRgn();
|
|
Rect r;
|
|
GetRegionBounds(rgn, &r);
|
|
SetPortWindowPort(platState.appWindow);
|
|
DragWindow(platState.appWindow, msg.where, &r);
|
|
if (platState.ctx)
|
|
aglUpdateContext((AGLContext)platState.ctx);
|
|
HandleUpdate(platState.appWindow);
|
|
return;
|
|
}
|
|
|
|
case inContent:
|
|
// will handle below.
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
|
|
InputEvent event;
|
|
event.deviceInst = 0;
|
|
event.deviceType = MouseDeviceType;
|
|
event.objType = SI_BUTTON;
|
|
event.objInst = KEY_BUTTON0; // always button 0 for now
|
|
event.modifier = 0;
|
|
event.ascii = 0;
|
|
if (msg.what == mouseDown)
|
|
event.action = SI_MAKE;
|
|
else
|
|
event.action = SI_BREAK;
|
|
event.fValue = event.action==SI_MAKE?1.0:0.0;
|
|
Game->postEvent(event);
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
#define TICK_WAIT_FOR_WNE 4
|
|
static bool ProcessMessages()
|
|
{
|
|
EventRecord msg;
|
|
bool done = false;
|
|
static U32 lastTick = 0;
|
|
U32 newTick = TickCount();
|
|
|
|
if (platState.quit) return false;
|
|
|
|
#if CARBEVENTS_RAEL
|
|
if (platState.osX && carbEventsRAEL)
|
|
{
|
|
EventRef carbonEvent;
|
|
EventTargetRef theTarget;
|
|
OSStatus theErr;
|
|
|
|
theTarget = GetEventDispatcherTarget();
|
|
do
|
|
{
|
|
theErr = ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &carbonEvent);
|
|
if (theErr == noErr && carbonEvent != NULL)
|
|
{
|
|
bool handled = false;
|
|
|
|
// make a normal Mac event.
|
|
// !!!!TBD
|
|
|
|
// not sure if this is needed under OSX.
|
|
// !!!!TBD.
|
|
extern bool gDSpActive;
|
|
if (gDSpActive)
|
|
{
|
|
// pass to DSp.
|
|
Boolean dspdid;
|
|
DSpProcessEvent(&msg, &dspdid);
|
|
if (dspdid) handled = true;
|
|
}
|
|
|
|
// !!!!TBD
|
|
// if (!handled)
|
|
// handled = (gConsole && gConsole->handleEvent(&msg));
|
|
|
|
if (!handled)
|
|
theErr = SendEventToEventTarget(carbonEvent, theTarget);
|
|
ReleaseEvent(carbonEvent);
|
|
}
|
|
}
|
|
while (theErr == noErr);
|
|
}
|
|
else
|
|
#endif
|
|
while(!done)
|
|
{
|
|
if (!windowLocked || platState.osX) // since WNE seems the only proper thing to do on X...
|
|
{
|
|
done = (false==WaitNextEvent(everyEvent, &msg, gSleepTicks, NULL));
|
|
}
|
|
else // windowLocked && !osx...
|
|
{
|
|
if (newTick-lastTick < TICK_WAIT_FOR_WNE)
|
|
done = (false==GetNextEvent(everyEvent, &msg));
|
|
else
|
|
done = (false==WaitNextEvent(everyEvent, &msg, gSleepTicks, NULL));
|
|
lastTick = newTick;
|
|
}
|
|
// not sure if this is needed under OSX.
|
|
// !!!!TBD.
|
|
extern bool gDSpActive;
|
|
if (gDSpActive)
|
|
{
|
|
Boolean dspdid;
|
|
DSpProcessEvent(&msg, &dspdid);
|
|
if (dspdid) continue;
|
|
}
|
|
|
|
bool handled = (gConsole && gConsole->handleEvent(&msg));
|
|
if (!handled)
|
|
switch(msg.what)
|
|
{
|
|
case nullEvent:
|
|
//AdjustCursor(msg.where, ((msg.modifiers&optionKey)!=0), cursorRgn);
|
|
done = true;
|
|
break;
|
|
|
|
case keyDown:
|
|
// case autoKey:
|
|
case keyUp:
|
|
ProcessKeyboard(msg);
|
|
break;
|
|
|
|
case mouseDown:
|
|
case mouseUp:
|
|
ProcessMouse(msg);
|
|
break;
|
|
|
|
case activateEvt:
|
|
HandleActivate((WindowPtr)(msg.message), (msg.modifiers & activeFlag) != 0, !gBackgrounded);
|
|
break;
|
|
|
|
case updateEvt:
|
|
HandleUpdate((WindowPtr)(msg.message));
|
|
break;
|
|
|
|
case osEvt:
|
|
{
|
|
switch ((unsigned long)(msg.message >> 24)) // osEvt msg is in the high byte.
|
|
{
|
|
case mouseMovedMessage: // this is it moved from the region passed to WNE
|
|
{
|
|
// AdjustCursor(msg.where, ((msg.modifiers&optionKey)!=0), cursorRgn);
|
|
break;
|
|
}
|
|
|
|
case suspendResumeMessage:
|
|
{
|
|
gBackgrounded = (msg.message & resumeFlag) == 0;
|
|
HandleActivate(platState.appWindow, !gBackgrounded, !gBackgrounded);
|
|
if (gBackgrounded)
|
|
gSleepTicks = TICKS_BACK_X;
|
|
else
|
|
gSleepTicks = TICKS_FRONT_X;
|
|
|
|
/*
|
|
Cursor arrow;
|
|
GetQDGlobalsArrow(&arrow);
|
|
if (gBackgrounded) // just suspended
|
|
SetCursor(&arrow);
|
|
else // just resumed
|
|
SetCursor(&arrow);
|
|
*/
|
|
break;
|
|
}/* end case suspend/resume evt */
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case kHighLevelEvent:
|
|
{
|
|
// the type of message is stored in the where Point... so cast it
|
|
U32 hlWhat = *((U32*)(&msg.where));
|
|
if ( hlWhat == kAEQuitApplication )
|
|
{
|
|
Platform::postQuitMessage(0);
|
|
return false;
|
|
}
|
|
else
|
|
AEProcessAppleEvent(&msg);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// Con::printf("%d %08x", msg.what, msg.what);
|
|
break;
|
|
}
|
|
|
|
#if TARG_MACCARB_EVENTS
|
|
if (platState.osX) // then we one do one WNE per Process call.
|
|
done = true;
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
void Platform::process()
|
|
{
|
|
extern bool gMouseActive;
|
|
if (!Input::isActive() || !gMouseActive)
|
|
#if TARG_MACCARB_EVENTS
|
|
if (!carbonEventsReady) // we don't poll mouse if we have CarbonEvents working...
|
|
#endif
|
|
CheckCursorPos();
|
|
|
|
/*
|
|
#if 0//TARG_MACCARB_EVENTS
|
|
// the moment we activate this code block, we lose all base WNE handling,
|
|
// so we'd need to convert to using a carbon timer or something to process the main loop.
|
|
if (carbonEventsReady)
|
|
{
|
|
RunApplicationEventLoop();
|
|
}
|
|
else
|
|
#endif
|
|
*/
|
|
if(!ProcessMessages())
|
|
{
|
|
// generate a quit event
|
|
Event quitEvent;
|
|
quitEvent.type = QuitEventType;
|
|
|
|
Game->postEvent(quitEvent);
|
|
}
|
|
|
|
|
|
Input::process();
|
|
}
|
|
|
|
extern U32 calculateCRC(void * buffer, S32 len, U32 crcVal );
|
|
|
|
#if defined(TORQUE_DEBUG) || defined(INTERNAL_RELEASE)
|
|
static U32 stubCRC = 0;
|
|
#else
|
|
static U32 stubCRC = 0xEA63F56C;
|
|
#endif
|
|
|
|
//--------------------------------------
|
|
/*
|
|
static void InitWindowClass()
|
|
{
|
|
WNDCLASS wc;
|
|
dMemset(&wc, 0, sizeof(wc));
|
|
|
|
wc.style = CS_OWNDC;
|
|
wc.lpfnWndProc = WindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = winState.appInstance;
|
|
wc.hIcon = LoadIcon(winState.appInstance, MAKEINTRESOURCE(IDI_ICON2));
|
|
wc.hCursor = LoadCursor (NULL,IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
|
wc.lpszMenuName = 0;
|
|
wc.lpszClassName = windowClassName;
|
|
RegisterClass( &wc );
|
|
|
|
// Curtain window class:
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
|
|
wc.lpszClassName = "Curtain";
|
|
RegisterClass( &wc );
|
|
}
|
|
*/
|
|
|
|
//--------------------------------------
|
|
static void GetDesktopState()
|
|
{
|
|
// desired display was already set inside Video::Init.
|
|
//platState.hDisplay = GetMainDevice();
|
|
|
|
Rect r = (*(platState.hDisplay))->gdRect;
|
|
platState.desktopWidth = r.right - r.left;
|
|
platState.desktopHeight = r.bottom - r.top;
|
|
platState.desktopBitsPixel = (*(*(platState.hDisplay))->gdPMap)->pixelSize;
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------
|
|
WindowPtr CreateOpenGLWindow( GDHandle hDevice, U32 width, U32 height, bool fullScreen )
|
|
{
|
|
WindowPtr w = NULL;
|
|
int offset = 144; // some nice starting offset.
|
|
bool noConsoleOffset = true;
|
|
|
|
// this rect should really be based off the device coords... !!!!!TBD
|
|
Rect rect, dRect;
|
|
// note: OpenGL is expecting a window so it can enumerate the devices it spans,
|
|
// center window in our context's gdevice
|
|
dRect = (**hDevice).gdRect;
|
|
if (fullScreen || noConsoleOffset)
|
|
{
|
|
// not sure all the casting is needed, but...
|
|
rect.top = (short) (dRect.top + (dRect.bottom - dRect.top) / 2); // h center
|
|
rect.top -= (height / 2);
|
|
rect.left = (short) (dRect.left + (dRect.right - dRect.left) / 2); // v center
|
|
rect.left -= (width / 2);
|
|
rect.right = (short) (rect.left + width);
|
|
rect.bottom = (short) (rect.top + height);
|
|
}
|
|
else
|
|
{
|
|
// do an offset window, based on device bounds.
|
|
SetRect( &rect, dRect.left+offset+48, dRect.top+offset, dRect.left+width+offset+48, dRect.top+height+offset);
|
|
}
|
|
|
|
if (platState.osX)
|
|
{
|
|
OSStatus stat;
|
|
WindowAttributes windAttr = 0L;
|
|
WindowClass windClass = kDocumentWindowClass;
|
|
if (fullScreen)
|
|
{
|
|
windAttr |= kWindowNoShadowAttribute; // no shadow when fullscreen window.
|
|
windClass = kAltPlainWindowClass;//kMovableAlertWindowClass; //kPlainWindowClass; //kOverlayWindowClass;
|
|
if (windClass==kOverlayWindowClass)
|
|
windAttr |= kWindowOpaqueForEventsAttribute;
|
|
}
|
|
else
|
|
windAttr |= kWindowCollapseBoxAttribute;
|
|
|
|
stat = CreateNewWindow(windClass, windAttr, &rect, &w);
|
|
if (stat!=noErr || w==NULL)
|
|
return(NULL); // !!!! ASSERT?????
|
|
|
|
if (fullScreen)
|
|
{ // move the thing into the proper level, in front of the blanking window.
|
|
// get blanking window level.
|
|
// create a new group if ours doesn't already exist
|
|
if (masterGroup==NULL)
|
|
{
|
|
WindowGroupAttributes grpAttr = 0L; // no special atts to start.
|
|
stat = CreateWindowGroup(grpAttr, &masterGroup);
|
|
// get the window's current group
|
|
// TBD!!!!!
|
|
// walk up the parent hierarchy, to find the topmost group.
|
|
// TBD!!!!!
|
|
// set the parent of our group to the topmost group.
|
|
// TBD!!!!!
|
|
}
|
|
// place window in group
|
|
stat = SetWindowGroup(w, masterGroup);
|
|
if (stat!=noErr)
|
|
{
|
|
int errCode1 = stat;
|
|
}
|
|
// set window level to one higher than blanking window.
|
|
// for the moment, we'll hack this.
|
|
stat = SetWindowGroupLevel(masterGroup, 1010); // above screen saver...
|
|
if (stat!=noErr)
|
|
{
|
|
int errCode2 = stat;
|
|
}
|
|
//#define kCGScreenSaverWindowLevel CGWindowLevelForKey(kCGScreenSaverWindowLevelKey) /* 1000 */
|
|
//#define kCGMaximumWindowLevel CGWindowLevelForKey(kCGMaximumWindowLevelKey) /* LONG_MAX - kCGNumReservedWindowLevels */
|
|
}
|
|
}
|
|
else
|
|
{ // old WMgr func. this will become outdated...
|
|
w = NewCWindow(NULL,
|
|
&rect, // bounding rect
|
|
str2p(platState.appWindowTitle), // window title
|
|
false, // is visible
|
|
fullScreen?kWindowPlainDialogProc:kWindowDocumentProc, // window type
|
|
(WindowPtr) -1L, // top most window
|
|
false, // has a close box
|
|
0L); // reference constant
|
|
}
|
|
|
|
if (w != NULL)
|
|
{
|
|
GrafPtr savegp;
|
|
RGBColor savergb;
|
|
Rect bounds;
|
|
const RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 };
|
|
|
|
ShowWindow(w);
|
|
SelectWindow(w);
|
|
BringToFront(w);
|
|
|
|
// paint back ground black before fade in to avoid white background flash
|
|
GetPort (&savegp);
|
|
SetPortWindowPort (w);
|
|
GetForeColor (&savergb);
|
|
RGBForeColor (&rgbBlack);
|
|
GetWindowPortBounds (w, &bounds);
|
|
PaintRect (&bounds);
|
|
RGBForeColor (&savergb); // ensure color is reset for proper blitting
|
|
SetPort (savegp);
|
|
}
|
|
|
|
return(w);
|
|
}
|
|
|
|
//--------------------------------------
|
|
WindowPtr CreateCurtain( U32 width, U32 height )
|
|
{
|
|
WindowPtr w=NULL;
|
|
/*
|
|
w = CreateWindow(
|
|
"Curtain",
|
|
"",
|
|
( WS_POPUP | WS_MAXIMIZE | WS_VISIBLE ),
|
|
0, 0,
|
|
width, height,
|
|
NULL, NULL,
|
|
winState.appInstance,
|
|
NULL );
|
|
*/
|
|
return(w);
|
|
}
|
|
|
|
|
|
/*
|
|
//--------------------------------------
|
|
void CreatePixelFormat( PIXELFORMATDESCRIPTOR *pPFD, S32 colorBits, S32 depthBits, S32 stencilBits, bool stereo )
|
|
{
|
|
PIXELFORMATDESCRIPTOR src =
|
|
{
|
|
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
|
|
1, // version number
|
|
PFD_DRAW_TO_WINDOW | // support window
|
|
PFD_SUPPORT_OPENGL | // support OpenGL
|
|
PFD_DOUBLEBUFFER, // double buffered
|
|
PFD_TYPE_RGBA, // RGBA type
|
|
colorBits, // color depth
|
|
0, 0, 0, 0, 0, 0, // color bits ignored
|
|
0, // no alpha buffer
|
|
0, // shift bit ignored
|
|
0, // no accumulation buffer
|
|
0, 0, 0, 0, // accum bits ignored
|
|
depthBits, // z-buffer
|
|
stencilBits, // stencil buffer
|
|
0, // no auxiliary buffer
|
|
PFD_MAIN_PLANE, // main layer
|
|
0, // reserved
|
|
0, 0, 0 // layer masks ignored
|
|
};
|
|
|
|
if ( stereo )
|
|
{
|
|
//ri.Printf( PRINT_ALL, "...attempting to use stereo\n" );
|
|
src.dwFlags |= PFD_STEREO;
|
|
//glConfig.stereoEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
//glConfig.stereoEnabled = qfalse;
|
|
}
|
|
*pPFD = src;
|
|
}
|
|
|
|
//--------------------------------------
|
|
enum { MAX_PFDS = 256 };
|
|
|
|
S32 ChooseBestPixelFormat(HDC hDC, PIXELFORMATDESCRIPTOR *pPFD)
|
|
{
|
|
PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1];
|
|
S32 i;
|
|
S32 bestMatch = 0;
|
|
|
|
S32 maxPFD = qwglDescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), &pfds[0]);
|
|
if(maxPFD > MAX_PFDS)
|
|
maxPFD = MAX_PFDS;
|
|
|
|
bool accelerated = false;
|
|
|
|
for(i = 1; i <= maxPFD; i++)
|
|
{
|
|
qwglDescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfds[i]);
|
|
|
|
// make sure this has hardware acceleration:
|
|
if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
|
|
continue;
|
|
|
|
// verify pixel type
|
|
if ( pfds[i].iPixelType != PFD_TYPE_RGBA )
|
|
continue;
|
|
|
|
// verify proper flags
|
|
if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags )
|
|
continue;
|
|
|
|
accelerated = !(pfds[i].dwFlags & PFD_GENERIC_FORMAT);
|
|
|
|
//
|
|
// selection criteria (in order of priority):
|
|
//
|
|
// PFD_STEREO
|
|
// colorBits
|
|
// depthBits
|
|
// stencilBits
|
|
//
|
|
if ( bestMatch )
|
|
{
|
|
// check stereo
|
|
if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
|
|
if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
|
|
// check color
|
|
if ( pfds[bestMatch].cColorBits != pPFD->cColorBits )
|
|
{
|
|
// prefer perfect match
|
|
if ( pfds[i].cColorBits == pPFD->cColorBits )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
// otherwise if this PFD has more bits than our best, use it
|
|
else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// check depth
|
|
if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits )
|
|
{
|
|
// prefer perfect match
|
|
if ( pfds[i].cDepthBits == pPFD->cDepthBits )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
// otherwise if this PFD has more bits than our best, use it
|
|
else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// check stencil
|
|
if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits )
|
|
{
|
|
// prefer perfect match
|
|
if ( pfds[i].cStencilBits == pPFD->cStencilBits )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
// otherwise if this PFD has more bits than our best, use it
|
|
else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) &&
|
|
( pPFD->cStencilBits > 0 ) )
|
|
{
|
|
bestMatch = i;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bestMatch = i;
|
|
}
|
|
}
|
|
|
|
if ( !bestMatch )
|
|
return 0;
|
|
|
|
else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED )
|
|
{
|
|
// MCD
|
|
}
|
|
else
|
|
{
|
|
// ICD
|
|
}
|
|
|
|
*pPFD = pfds[bestMatch];
|
|
|
|
return bestMatch;
|
|
}
|
|
*/
|
|
|
|
|
|
//--------------------------------------
|
|
const Point2I &Platform::getWindowSize()
|
|
{
|
|
return windowSize;
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
void Platform::setWindowSize( U32 newWidth, U32 newHeight )
|
|
{
|
|
windowSize.set( newWidth, newHeight );
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
// !!!!!TBD!!!!! what should this do on the Mac.
|
|
void Platform::minimizeWindow()
|
|
{
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static void InitWindow(const Point2I &initialSize)
|
|
{
|
|
windowSize = initialSize;
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static void InitOpenGL()
|
|
{
|
|
DisplayDevice::init();
|
|
|
|
// Get the video settings from the prefs.
|
|
const char* resString;
|
|
U32 height, width, bpp;
|
|
char *tempBuf, *s;
|
|
bool fullScreen = Con::getBoolVariable( "$pref::Video::fullScreen" );
|
|
// properly grab fullscreen vs windowed resolution.
|
|
if (fullScreen)
|
|
resString = Con::getVariable( "$pref::Video::resolution" );
|
|
else
|
|
resString = Con::getVariable( "$pref::Video::windowedRes" );
|
|
|
|
tempBuf = new char[dStrlen( resString ) + 1];
|
|
dStrcpy( tempBuf, resString );
|
|
|
|
s = dStrtok( tempBuf, " x\0" );
|
|
width = ( s ? dAtoi( s ) : (windowSize.x > 0 ? windowSize.x : 640) ); //DAW: Added check for windowSize
|
|
|
|
s = dStrtok( NULL, " x\0" );
|
|
height = ( s ? dAtoi( s ) : (windowSize.y > 0 ? windowSize.y : 480) ); //DAW: Added check for windowSize
|
|
|
|
if (fullScreen)
|
|
{
|
|
s = dStrtok( NULL, "\0" );
|
|
bpp = ( s ? dAtoi( s ) : 16 );
|
|
}
|
|
else
|
|
bpp = platState.desktopBitsPixel;
|
|
|
|
delete [] tempBuf;
|
|
|
|
if ( !Video::setDevice( Con::getVariable( "$pref::Video::displayDevice" ), width, height, bpp, fullScreen ) )
|
|
{
|
|
// Next, try the default OpenGL device:
|
|
if ( !Video::setDevice( "OpenGL", width, height, bpp, fullScreen ) )
|
|
{
|
|
AssertFatal( false, "Could not find a compatible display device!" );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
ConsoleFunction( getDesktopResolution, const char*, 1, 1, "getDesktopResolution()" )
|
|
{
|
|
argc; argv;
|
|
char buffer[256];
|
|
dSprintf( buffer, sizeof( buffer ), "%d %d %d", platState.desktopWidth, platState.desktopHeight, platState.desktopBitsPixel );
|
|
char* returnString = Con::getReturnBuffer( dStrlen( buffer ) + 1 );
|
|
dStrcpy( returnString, buffer );
|
|
return( returnString );
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
void Platform::init()
|
|
{
|
|
// Set the platform variable for the scripts
|
|
Con::setVariable( "$platform", "macos" );
|
|
|
|
MacConsole::create();
|
|
if ( !MacConsole::isEnabled() )
|
|
Input::init();
|
|
InitInput(); // as internal windowing input handles different stuff from Input object.
|
|
|
|
// not sure why this is here on mac, but so be it.
|
|
DisplayDevice *dev = NULL;
|
|
Con::printf( "Video Init:" );
|
|
Video::init();
|
|
dev = OpenGLDevice::create();
|
|
// now safe to grab the right desktop size.
|
|
GetDesktopState();
|
|
// and now we can install the device.
|
|
if ( Video::installDevice(dev) )
|
|
Con::printf( " Accelerated OpenGL display device detected." );
|
|
else
|
|
Con::printf( " Accelerated OpenGL display device not detected." );
|
|
Con::printf( "" );
|
|
|
|
// sgDoubleByteEnabled = true; // !!!!! this right?
|
|
// sgQueueEvents = true;
|
|
}
|
|
|
|
//--------------------------------------
|
|
void Platform::shutdown()
|
|
{
|
|
// sgQueueEvents = false;
|
|
|
|
// if(gMutexHandle)
|
|
// CloseHandle(gMutexHandle);
|
|
setWindowLocked( false );
|
|
Video::destroy();
|
|
Input::destroy();
|
|
MacConsole::destroy();
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
static U32 lastTimeTick;
|
|
static S32 passargc = 0;
|
|
static char **passargv = NULL;
|
|
static S32 appReturn = 0;
|
|
|
|
//--------------------------------------
|
|
static S32 run()
|
|
{
|
|
createFontInit();
|
|
windowSize.set(0,0);
|
|
|
|
lastTimeTick = Platform::getRealMilliseconds();
|
|
|
|
int ret = Game->main(passargc, (const char **)passargv);
|
|
|
|
createFontShutdown();
|
|
|
|
return ret;
|
|
}
|
|
|
|
//--------------------------------------
|
|
void Platform::initWindow(const Point2I &initialSize, const char *name)
|
|
{
|
|
/* // on the mac, video is currently initalized earlier...
|
|
DisplayDevice *dev = NULL;
|
|
Con::printf( "Video Init:" );
|
|
Video::init();
|
|
dev = OpenGLDevice::create();
|
|
// now safe to grab the right desktop size.
|
|
GetDesktopState();
|
|
// and now we can install the device.
|
|
if ( Video::installDevice(dev) )
|
|
Con::printf( " Accelerated OpenGL display device detected." );
|
|
else
|
|
Con::printf( " Accelerated OpenGL display device not detected." );
|
|
Con::printf( "" );
|
|
*/
|
|
|
|
dSprintf(platState.appWindowTitle, sizeof(platState.appWindowTitle), name);
|
|
InitWindow(initialSize); // this doesn't create the window -- gl subcode it.
|
|
InitOpenGL();
|
|
|
|
if (platState.appWindow)
|
|
{
|
|
gWindowCreated = true; // if we get in here, we really have a secondary window, not just a console...
|
|
#if TARG_MACCARB_EVENTS
|
|
// install handlers to the given window.
|
|
EventTargetRef winTarg = GetWindowEventTarget(platState.appWindow);
|
|
#define DEFAULT_HANDLERS 1
|
|
#if DEFAULT_HANDLERS
|
|
InstallStandardEventHandler(winTarg);
|
|
InstallStandardEventHandler(GetApplicationEventTarget());
|
|
#endif
|
|
installCarbonEventHandlers();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
#if CARBEVENTS_RAEL
|
|
//--------------------------------------
|
|
pascal void DoRAELLoop(EventLoopTimerRef theTimer, void *userData)
|
|
{
|
|
appReturn = run();
|
|
QuitApplicationEventLoop();
|
|
}
|
|
#endif
|
|
|
|
|
|
//--------------------------------------
|
|
S32 main(S32 argc, const char **argv)
|
|
{
|
|
bool optOutFromCmdline = false;
|
|
|
|
// set up starting values.
|
|
passargc = argc;
|
|
passargv = (char**)argv;
|
|
|
|
// Get rid of the -psn argument
|
|
for(int j=0; j<passargc; j++)
|
|
if(!strncmp(passargv[j], "-psn", 4))
|
|
passargv[j] = "";
|
|
|
|
#if !TARGET_API_MAC_CARBON
|
|
InitGraf(&qd.thePort); // init QuickDraw -- 'qd' is a Mac Global
|
|
InitFonts(); // init the Font Manager
|
|
InitWindows(); // init the Window Manager
|
|
InitMenus();
|
|
TEInit();
|
|
InitDialogs( NULL );
|
|
#endif
|
|
|
|
InitCursor();
|
|
|
|
FlushEvents( everyEvent, 0 );
|
|
SetEventMask(everyEvent);
|
|
|
|
// save away OS version info into platState.
|
|
if (Gestalt(gestaltSystemVersion, (SInt32 *) &(platState.osVersion)) == noErr)
|
|
{
|
|
platState.osX = false;
|
|
if (platState.osVersion >= 0x01000)
|
|
platState.osX = true;
|
|
}
|
|
|
|
// Update the current working directory.
|
|
Platform::getWorkingDirectory();
|
|
|
|
// this is yucky, but the easiest way to ignore the cmdline text file:
|
|
#define KEYISDOWN(key) ((((unsigned char *)currKeyState)[key>>3] >> (key & 7)) & 1)
|
|
KeyMap currKeyState;
|
|
GetKeys(currKeyState);
|
|
if (KEYISDOWN(0x38)) // check shift key -- actually LShift.
|
|
optOutFromCmdline = true;
|
|
|
|
// mac does not support command line arguments. well, OSX does, but not easily for the average user...
|
|
// it may be possible to get the comment field from the file
|
|
// see DesktopManager and DTGetComment
|
|
// BUT, for the moment, try to open a file "maccmdline.txt" and use first line.
|
|
#define MAX_CMDLINE 2048
|
|
static char cmdline[2048]; // make it 2K just in case...
|
|
static const char *cmdargv[32]; // 32 max commands?
|
|
if (!optOutFromCmdline)
|
|
if (1) // used to check and only override if there were no args. now, we merge them.
|
|
{
|
|
File cmdfile;
|
|
S32 cmdargc=0;
|
|
U32 cmdlen;
|
|
File::Status cmderr;
|
|
cmderr = cmdfile.open("maccmdline.txt", cmdfile.Read);
|
|
if (cmderr == File::Ok)
|
|
{
|
|
cmderr = cmdfile.read(MAX_CMDLINE-1, cmdline, &cmdlen);
|
|
if ((cmderr == File::Ok || cmderr == File::EOS) && cmdlen>1) // if empty file, don't bother...
|
|
{
|
|
cmdline[cmdlen] = 0; // null terminate.
|
|
cmdlen++; // add the null to len.
|
|
int i = 0;
|
|
|
|
// first, reset the max length if there's a CR or LF.
|
|
while (i<cmdlen)
|
|
{
|
|
if (cmdline[i]=='\n' || cmdline[i]=='\r')
|
|
{
|
|
cmdline[i] = 0; // null terminate.
|
|
cmdlen = i+1; // set new length.
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
// now build args.
|
|
// arg[0] should be exe name/path.
|
|
// on OSX, already there. under 10.1, looks like there's a psn serial number as arg 2. pain in the ass.
|
|
// on OS9, we could put in the real thing, but I toss in a fake string for now. !!!!TBD
|
|
// !!!!TBD. mac can't call getWorkingDir here, as it's not yet inited properly.
|
|
|
|
// Exclude the magic -psn process id parameter
|
|
if(strncmp(argv[0], "-psn", 4))
|
|
cmdargv[cmdargc++] = argv[0];
|
|
|
|
// arg[1] will be the first thing in the cmdline, if anything is there...
|
|
cmdargv[cmdargc] = cmdline;
|
|
|
|
i = 0;
|
|
while (i<cmdlen)
|
|
{
|
|
if (cmdline[i]==0 || cmdline[i]==' ') // on nulls or spaces.
|
|
{
|
|
cmdline[i] = 0;
|
|
cmdargc++; // we hit a command, so inc the count.
|
|
// set up for next command
|
|
cmdargv[cmdargc] = &(cmdline[i+1]); // start at char past null.
|
|
}
|
|
i++;
|
|
}
|
|
|
|
// now, tack on any other parameters from the real commandline.
|
|
for (i=1; i<argc; i++)
|
|
{
|
|
// Exclude the magic -psn process id parameter
|
|
if(strncmp(argv[i], "-psn", 4))
|
|
cmdargv[cmdargc++] = argv[i];
|
|
}
|
|
|
|
// if (cmdargc>1) // since app path always there... // for now, always used.
|
|
{ // copy/set the vars.
|
|
passargc = cmdargc;
|
|
passargv = (char**)cmdargv;
|
|
}
|
|
}
|
|
cmdfile.close();
|
|
}
|
|
}
|
|
|
|
appReturn = 0;
|
|
|
|
#if TARG_MACCARB_EVENTS
|
|
#if CARBEVENTS_RAEL
|
|
carbEventsRAEL = true;
|
|
#endif
|
|
|
|
if (carbEventsRAEL)
|
|
{
|
|
EventLoopTimerRef theTimer;
|
|
// Install a one-shot timer to run the game, then call RAEL to install
|
|
// the default application handler (which can't be called directly).
|
|
InstallEventLoopTimer(GetCurrentEventLoop(), 0, 0, NewEventLoopTimerUPP(DoRAELLoop), NULL, &theTimer);
|
|
RunApplicationEventLoop();
|
|
}
|
|
else
|
|
#endif
|
|
appReturn = run(); // use the statics/globals rather than actually passing as params.
|
|
|
|
InitCursor(); // don't leave it in a screwy state...
|
|
|
|
return(appReturn);
|
|
}
|
|
|
|
//--------------------------------------
|
|
void TimeManager::process()
|
|
{
|
|
U32 curTime = Platform::getRealMilliseconds(); // GTC returns Milliseconds, FYI.
|
|
TimeEvent event;
|
|
event.elapsedTime = curTime - lastTimeTick;
|
|
if(event.elapsedTime > sgTimeManagerProcessInterval)
|
|
{
|
|
lastTimeTick = curTime;
|
|
Game->postEvent(event);
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
F32 Platform::getRandom()
|
|
{
|
|
return sgPlatRandom.randF();
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
// Web browser function:
|
|
//--------------------------------------
|
|
#if !defined(TORQUE_OS_MAC_OSX)
|
|
#include <InternetConfig.h>
|
|
#endif
|
|
bool Platform::openWebBrowser( const char* webAddress )
|
|
{
|
|
OSStatus err;
|
|
ICInstance ic;
|
|
long start, end, len;
|
|
|
|
#define kAppSignature 'GGTe'
|
|
err = ICStart(&ic, kAppSignature); // TBD!!!!!! replace with a const for the app signature.
|
|
if (err==noErr)
|
|
{
|
|
#if !TARGET_API_MAC_CARBON && !defined(TORQUE_OS_MAC_OSX)
|
|
// this function is apparently outdated and not needed under Carbon.
|
|
err = ICFindConfigFile(ic, 0, NULL);
|
|
if (err==noErr)
|
|
#endif
|
|
{
|
|
len = dStrlen(webAddress);
|
|
start = 0;
|
|
end = len;
|
|
err = ICLaunchURL(ic, (const unsigned char*)"\0", webAddress, len, &start, &end);
|
|
}
|
|
ICStop(ic);
|
|
}
|
|
|
|
return(err==noErr);
|
|
}
|
|
|
|
//--------------------------------------
|
|
void Platform::enableKeyboardTranslation(void)
|
|
{
|
|
ActivateTSMDocument(platState.tsmDoc);
|
|
platState.tsmActive=true;
|
|
Con::printf("tsm doc should be active");
|
|
}
|
|
|
|
//--------------------------------------
|
|
void Platform::disableKeyboardTranslation(void)
|
|
{
|
|
DeactivateTSMDocument(platState.tsmDoc);
|
|
platState.tsmActive=false;
|
|
Con::printf("tsm doc Deactivated");
|
|
}
|
|
|
|
//--------------------------------------
|
|
OSStatus DoTextInputCarbonEventHandling(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
|
|
{
|
|
UniChar *text = NULL;
|
|
UInt32 textLen;
|
|
U32 charCount = 0;
|
|
|
|
// be sure we have the right event type for this handler.
|
|
U32 eventClass = GetEventClass(theEvent);
|
|
U32 eventKind = GetEventKind(theEvent);
|
|
|
|
if( eventClass != kEventClassTextInput )
|
|
return eventNotHandledErr;
|
|
|
|
switch(eventKind)
|
|
{
|
|
case kEventTextInputUnicodeForKeyEvent:
|
|
{
|
|
// get the input string from the text input event
|
|
// first get the number of bytes required
|
|
GetEventParameter( theEvent, kEventParamTextInputSendText, typeUnicodeText, NULL, 0, &textLen, NULL);
|
|
charCount = textLen / sizeof(UniChar);
|
|
text = new UniChar[charCount];
|
|
|
|
// now that we've allocated space, get the buffer of text from the input method or keyboard.
|
|
GetEventParameter( theEvent, kEventParamTextInputSendText, typeUnicodeText, NULL, textLen, NULL, text);
|
|
|
|
break;
|
|
}
|
|
default: // if we somehow get the wrong kind of event, let someone else handle it.
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
|
|
// make torque events to enter that string.
|
|
InputEvent torqueEvent;
|
|
torqueEvent.deviceType = KeyboardDeviceType;
|
|
torqueEvent.deviceInst = 0;
|
|
torqueEvent.objType = SI_TEXT;
|
|
torqueEvent.objInst = 0;
|
|
torqueEvent.modifier = 0;
|
|
torqueEvent.action = SI_MAKE;
|
|
torqueEvent.fValue = 1.0f;
|
|
|
|
for( int i=0; i < charCount; i++)
|
|
{
|
|
torqueEvent.ascii = text[i];
|
|
// evil temporary hack. I mean it. Temporary.
|
|
// gets rid of the stray backquote in the console
|
|
// switching to new event model may help, or bring up other prob.
|
|
if(text[i] == '`' && charCount == 1)
|
|
continue;
|
|
Game->postEvent(torqueEvent);
|
|
}
|
|
|
|
return noErr;
|
|
}
|
|
|
|
//--------------------------------------
|
|
void installCarbonEventHandlers()
|
|
{
|
|
EventTargetRef winTarg = GetWindowEventTarget(platState.appWindow);
|
|
// set up the events we want to be notified of for our event handler.
|
|
const int NUM_WIN_EVENT_TYPES = 3 + 5 + 3; // kb+mouse+wind
|
|
static EventTypeSpec winEventTypes[NUM_WIN_EVENT_TYPES];
|
|
static EventHandlerUPP winHandlerUPP = NULL;
|
|
|
|
const int NUM_APP_EVENT_TYPES = 1 + 3 + 3 + 5; // AE+app+dupmouse/kb
|
|
static EventTypeSpec appEventTypes[NUM_APP_EVENT_TYPES];
|
|
static EventHandlerUPP appHandlerUPP = NULL;
|
|
|
|
const int NUM_TEXT_EVENT_TYPES = 1;
|
|
static EventTypeSpec textEventTypes[NUM_TEXT_EVENT_TYPES];
|
|
static EventHandlerUPP textHandlerUPP = NULL;
|
|
|
|
int c = 0;
|
|
|
|
if(textHandlerUPP==NULL)
|
|
{
|
|
c = 0;
|
|
textEventTypes[c].eventClass = kEventClassTextInput;
|
|
textEventTypes[c++].eventKind = kEventTextInputUnicodeForKeyEvent;
|
|
|
|
textHandlerUPP = NewEventHandlerUPP(DoTextInputCarbonEventHandling);
|
|
}
|
|
|
|
|
|
if (winHandlerUPP==NULL)
|
|
{
|
|
c = 0;
|
|
winEventTypes[c].eventClass = kEventClassKeyboard;
|
|
winEventTypes[c++].eventKind = kEventRawKeyDown;
|
|
winEventTypes[c].eventClass = kEventClassKeyboard;
|
|
winEventTypes[c++].eventKind = kEventRawKeyUp;
|
|
winEventTypes[c].eventClass = kEventClassKeyboard;
|
|
winEventTypes[c++].eventKind = kEventRawKeyRepeat;
|
|
// don't do this until we decide we really need it to get raw modifier keys...
|
|
// eventTypes[c].eventClass = kEventClassKeyboard;
|
|
// eventTypes[c++].eventKind = kEventRawKeyModifiersChanged;
|
|
|
|
winEventTypes[c].eventClass = kEventClassMouse;
|
|
winEventTypes[c++].eventKind = kEventMouseDown;
|
|
winEventTypes[c].eventClass = kEventClassMouse;
|
|
winEventTypes[c++].eventKind = kEventMouseUp;
|
|
winEventTypes[c].eventClass = kEventClassMouse;
|
|
winEventTypes[c++].eventKind = kEventMouseMoved;
|
|
winEventTypes[c].eventClass = kEventClassMouse;
|
|
winEventTypes[c++].eventKind = kEventMouseDragged;
|
|
winEventTypes[c].eventClass = kEventClassMouse;
|
|
winEventTypes[c++].eventKind = kEventMouseWheelMoved;
|
|
|
|
// don't need this unless we want special handling.
|
|
winEventTypes[c].eventClass = kEventClassWindow;
|
|
winEventTypes[c++].eventKind = kEventWindowDrawContent;
|
|
winEventTypes[c].eventClass = kEventClassWindow;
|
|
winEventTypes[c++].eventKind = kEventWindowActivated;
|
|
winEventTypes[c].eventClass = kEventClassWindow;
|
|
winEventTypes[c++].eventKind = kEventWindowDeactivated;
|
|
|
|
winHandlerUPP = NewEventHandlerUPP(DoWinCarbonEventHandling);
|
|
}
|
|
|
|
if (appHandlerUPP==NULL)
|
|
{
|
|
c = 0;
|
|
appEventTypes[c].eventClass = kEventClassAppleEvent;
|
|
appEventTypes[c++].eventKind = kEventAppleEvent; // it's always an appleevent
|
|
|
|
appEventTypes[c].eventClass = kEventClassApplication;
|
|
appEventTypes[c++].eventKind = kEventAppQuit;
|
|
appEventTypes[c].eventClass = kEventClassApplication;
|
|
appEventTypes[c++].eventKind = kEventAppActivated;
|
|
appEventTypes[c].eventClass = kEventClassApplication;
|
|
appEventTypes[c++].eventKind = kEventAppDeactivated;
|
|
|
|
// since if in windowed mode, these can all occur outside window bounds, we need to
|
|
// force-propagate the msg.
|
|
appEventTypes[c].eventClass = kEventClassKeyboard;
|
|
appEventTypes[c++].eventKind = kEventRawKeyDown;
|
|
appEventTypes[c].eventClass = kEventClassKeyboard;
|
|
appEventTypes[c++].eventKind = kEventRawKeyUp;
|
|
appEventTypes[c].eventClass = kEventClassKeyboard;
|
|
appEventTypes[c++].eventKind = kEventRawKeyRepeat;
|
|
// don't do this until we decide we really need it to get raw modifier keys...
|
|
// eventTypes[c].eventClass = kEventClassKeyboard;
|
|
// eventTypes[c++].eventKind = kEventRawKeyModifiersChanged;
|
|
|
|
appEventTypes[c].eventClass = kEventClassMouse;
|
|
appEventTypes[c++].eventKind = kEventMouseDown;
|
|
appEventTypes[c].eventClass = kEventClassMouse;
|
|
appEventTypes[c++].eventKind = kEventMouseUp;
|
|
appEventTypes[c].eventClass = kEventClassMouse;
|
|
appEventTypes[c++].eventKind = kEventMouseMoved;
|
|
appEventTypes[c].eventClass = kEventClassMouse;
|
|
appEventTypes[c++].eventKind = kEventMouseDragged;
|
|
appEventTypes[c].eventClass = kEventClassMouse;
|
|
appEventTypes[c++].eventKind = kEventMouseWheelMoved;
|
|
|
|
appHandlerUPP = NewEventHandlerUPP(DoAppCarbonEventHandling);
|
|
}
|
|
|
|
// this installs into the window -- window doesn't always get all events, such as App events
|
|
InstallEventHandler (winTarg, winHandlerUPP, NUM_WIN_EVENT_TYPES, winEventTypes, NULL, &gWinEventHandlerRef);
|
|
// this installs at the app level.
|
|
InstallEventHandler (GetApplicationEventTarget(), appHandlerUPP, NUM_APP_EVENT_TYPES, appEventTypes, NULL, &gAppEventHandlerRef);
|
|
// this installs a handler for unicode text input
|
|
InstallEventHandler (GetApplicationEventTarget(), textHandlerUPP, NUM_TEXT_EVENT_TYPES, textEventTypes, NULL, &gTextEventHandlerRef);
|
|
|
|
carbonEventsReady = true;
|
|
}
|
|
|
|
void removeCarbonEventHandlers()
|
|
{
|
|
RemoveEventHandler(gWinEventHandlerRef);
|
|
RemoveEventHandler(gAppEventHandlerRef);
|
|
}
|
|
|
|
ConsoleFunction(testFloatingWindowLevel,void,1,1,"")
|
|
{
|
|
Con::printf(" Sheilding window level is %x",CGShieldingWindowLevel());
|
|
SetWindowGroupLevel( GetWindowGroupOfClass(kUtilityWindowClass), 1031);
|
|
SetWindowGroupLevel( GetWindowGroupOfClass(kFloatingWindowClass), 1030);
|
|
SetWindowGroupLevel( GetWindowGroupOfClass(kAlertWindowClass), 1031);
|
|
}
|