tenebrae2/macosx/sys_osx.m

1872 lines
63 KiB
Mathematica
Raw Normal View History

2003-01-17 21:18:53 +00:00
//___________________________________________________________________________________________________________nFO
// "sys_osx.m" - MacOS X system functions.
//
// Written by: Axel "awe" Wefers [mailto:awe@fruitz-of-dojo.de].
// (C)2001-2002 Fruitz Of Dojo [http://www.fruitz-of-dojo.de].
//
// Quake<EFBFBD> is copyrighted by id software [http://www.idsoftware.com].
//
// Version History:
//
// TenebraeQuake:
// v1.0: Initial release.
//
// Standard Quake branch [pre-TenebraeQuake]:
// v1.0.8: Extended the GLQuake and GLQuakeWorld startup dialog with "FSAA Samples" option.
// v1.0.7: From now on you will only be asked to locate the "id1" folder, if the application is not
// installed inside the same folder which holds the "id1" folder.
// v1.0.6: Fixed cursor vsisibility on system error.
// Fixed loss of mouse control after CMD-TABing with the software renderer.
// Fixed a glitch with the Quake/QuakeWorld options menu [see "menu.c"].
// Reworked keypad code [is now hardware dependent] to avoid "sticky" keys.
// Moved fixing of linebreaks to "cmd.c".
// v1.0.5: Added support for numeric keypad.
// Added support for paste [CMD-V and "Edit/Paste" menu].
// Added support for up to 5 mouse buttons.
// Added "Connect To Server" service.
// Added support for CMD-M, CMD-H and CMD-Q.
// Added web link to the help menu.
// Fixed keyboard repeat after application quit.
// v1.0.4: Changed all vsprintf () calls to vsnprintf (). vsnprintf () is cleaner.
// v1.0.3: Fixed the broken "drag MOD onto Quake icon" method [blame me].
// Enabled Num. Lock Key.
// v1.0.2: Reworked settings dialog.
// Reenabled keyboard repeat and mousescaling.
// Some internal changes.
// v1.0.1: Added support for GLQuake.
// Obscure characters within the base pathname [like '<EFBFBD>'...] are now allowed.
// Better support for case sensitive file systems.
// FIX: "mkdir" commmand [for QuakeWorld].
// FIX: The "id1" folder had to be lower case. Can now be upper or lower case.
// v1.0: Initial release.
//______________________________________________________________________________________________________iNCLUDES
#pragma mark =Includes=
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <errno.h>
#import <drivers/event_status_driver.h>
#include "quakedef.h"
#pragma mark -
//_______________________________________________________________________________________________________dEFINES
#pragma mark =Defines=
#define BETA_VERSION // undef to remove warning dialog!
#define FRUITZ_OF_DOJO_URL @"http://www.fruitz-of-dojo.de/"
#define DEFAULT_BASE_PATH @"Quake ID1 Path"
#define DEFAULT_GL_DISPLAY @"TenebraeQuake Display"
#define DEFAULT_GL_DISPLAY_MODE @"TenebraeQuake Display Mode"
#define DEFAULT_GL_COLORS @"TenebraeQuake Display Depth"
#define DEFAULT_GL_SAMPLES @"TenebraeQuake Samples"
#define DEFAULT_GL_FADE_ALL @"TenebraeQuake Fade All Displays"
#define DEFAULT_GL_FULLSCREEN @"TenebraeQuake Fullscreen"
#define INITIAL_BASE_PATH @"id1"
#define INITIAL_GL_DISPLAY @"0"
#define INITIAL_GL_DISPLAY_MODE @"640x480 67Hz"
#define INITIAL_GL_COLORS @"0"
#define INITIAL_GL_SAMPLES @"0"
#define INITIAL_GL_FADE_ALL @"YES"
#define INITIAL_GL_FULLSCREEN @"YES"
#define ID1_VALIDATION_FILE @"/PAK0.PAK"
#define TENEBRAE_VALIDATION_FILE @"../tenebrae/PAK0.PAK"
#define GAME_COMMAND "-game"
#define QUAKE_BASE_PATH "."
#define QWSV_BASE_PATH "."
#define MOUSE_BUTTONS 5 // MacOS X supports up to 32 buttons[change in in_osx.m, too].
#define SYS_STRING_SIZE 1024 // string size for vsnprintf ().
#define MAX_DISPLAYS 100
#pragma mark -
//________________________________________________________________________________________________________mACROS
#pragma mark =Macros=
#define CHECK_MALLOC(MEM_PTR) if ((MEM_PTR) == NULL) \
{ \
Sys_Error ("Out of memory!"); \
}
#define CHECK_MOUSE_ENABLED() if ((gDisplayFullscreen == NO && _windowed_mouse.value == 0.0f) || \
gMouseEnabled == NO || gIsMinimized == YES || gIsDeactivated) \
{ \
return; \
}
#pragma mark -
//__________________________________________________________________________________________________________vARS
#pragma mark =Variables=
extern UInt gDisplay;
extern SInt gARBMultiSamples;
extern CGDirectDisplayID gDisplayList[MAX_DISPLAYS];
extern CGDisplayCount gDisplayCount;
extern NSDictionary *gDisplayMode;
extern Boolean gFadeAllDisplays;
extern cvar_t _windowed_mouse;
extern NSWindow *gWindow;
extern Boolean gIsMinimized;
extern qboolean gDisplayFullscreen,
gMouseEnabled;
Boolean gIsHidden = NO,
gIsDeactivated = NO;
qboolean isDedicated;
static char gRequestedServer[128];
static NSDate *gDistantPast;
static SInt gNoStdOut = 0,
gArgCount;
static char *gModDir = NULL,
**gArgValues = NULL;
static Boolean gDenyDrag = NO,
gWasDragged = NO,
gHostInitialized = NO;
static UInt8 gSpecialKey[] = {
K_UPARROW, K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW,
K_F1, K_F2, K_F3, K_F4,
K_F5, K_F6, K_F7, K_F8,
K_F9, K_F10, K_F11, K_F12,
K_F13, K_F14, K_F15, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, K_INS,
K_DEL, K_HOME, 0, K_END,
K_PGUP, K_PGDN, 0, 0,
K_PAUSE, 0, 0, 0,
0, 0, 0, 0,
0, K_NUMLOCK, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, K_INS, 0
};
static UInt8 gNumPadKey[] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, K_PERIOD_PAD, 0, K_ASTERISK_PAD,
0, K_PLUS_PAD, 0, 0,
0, 0, 0, 0,
K_ENTER_PAD, K_SLASH_PAD, K_MINUS_PAD, 0,
0, K_EQUAL_PAD, K_0_PAD, K_1_PAD,
K_2_PAD, K_3_PAD, K_4_PAD, K_5_PAD,
K_6_PAD, K_7_PAD, 0, K_8_PAD,
K_9_PAD, 0, 0, 0
};
#pragma mark -
//____________________________________________________________________________________________________iNTERFACES
#pragma mark =ObjC Interfaces=
@interface Quake : NSObject
{
IBOutlet NSPopUpButton *displayPopUp;
IBOutlet NSPopUpButton *modePopUp;
IBOutlet NSPopUpButton *colorsPopUp;
IBOutlet NSPopUpButton *samplesPopUp;
IBOutlet NSButton *fullscreenCheckBox;
IBOutlet NSButton *fadeAllCheckBox;
IBOutlet NSWindow *settingsWindow;
NSMutableArray *gModeList;
}
+ (void) initialize;
- (BOOL) application: (NSApplication *) theSender openFile: (NSString *) theFilePath;
- (void) applicationDidFinishLaunching: (NSNotification *) theNote;
- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) theSender;
- (void) applicationDidResignActive: (NSNotification *) theNote;
- (void) applicationDidBecomeActive: (NSNotification *) theNote;
- (void) applicationWillHide: (NSNotification *) theNote;
- (void) applicationWillUnhide: (NSNotification *) theNote;
- (void) buildDisplayList;
- (IBAction) buildResolutionList: (id) theSender;
- (void) newGame: (id) theSender;
- (void) setupDialog;
- (Boolean) isEqualTo: (NSString *) theString;
- (NSString *) displayModeToString: (NSDictionary *) theDisplayMode;
- (void) startQuake;
- (void) performOpenURL: (NSString *) theURL;
- (IBAction) pasteString: (id) theSender;
- (IBAction) visitFOD: (id) theSender;
- (void) connectToServer: (NSPasteboard *) thePasteboard userData:(NSString *)theData
error: (NSString **) theError;
@end
#pragma mark -
//___________________________________________________________________________________________fUNCTION_pROTOTYPES
#pragma mark =Function Prototypes=
extern Boolean GL_CheckARBMultisampleExtension (CGDirectDisplayID theDisplay);
extern void IN_ReceiveMouseMove (SInt, SInt);
extern void IN_ShowCursor (Boolean);
extern void M_Menu_Quit_f (void);
char * Sys_GetClipboardData (void);
void Sys_DoEvents (NSEvent *, NSEventType);
void Sys_SetKeyboardRepeatEnabled (Boolean);
void Sys_SwitchCase (char *, UInt16);
void Sys_FixFileNameCase (char *);
void Sys_CheckForIDDirectory (void);
SInt main (SInt, const char **);
#pragma mark -
//______________________________________________________________________________________________Sys_SwitchCase()
void Sys_SwitchCase (char *theString, UInt16 thePosition)
{
while (theString[thePosition] != 0x00)
{
if (theString[thePosition] >= 'a' && theString[thePosition] <= 'z')
{
theString[thePosition] += ('A' - 'a');
}
else
{
if (theString[thePosition] >= 'A' && theString[thePosition] <= 'Z')
theString[thePosition] += ('a' - 'A');
}
thePosition++;
}
}
//______________________________________________________________________________________Sys_FixForFileNameCase()
void Sys_FixFileNameCase (char *thePath)
{
FILE *myFile = NULL;
if (!(myFile = fopen (thePath, "rb")))
{
UInt16 myPathEnd = 0, i;
for (i = 0; i < strlen (thePath); i++)
if (thePath[i] == '/') myPathEnd = i;
if (myPathEnd)
{
char myBaseDir[MAXPATHLEN];
UInt16 myPathStart;
getcwd (myBaseDir, MAXPATHLEN);
i = 0;
while (thePath[i] != '/') i++;
myPathStart = i++;
while (i <= myPathEnd)
{
if (thePath[i] == '/')
{
thePath[i] = 0x00;
if (chdir (thePath))
{
Sys_SwitchCase (thePath, myPathStart);
chdir (myBaseDir);
if(chdir (thePath))
{
Sys_SwitchCase (thePath, myPathStart);
thePath[i] = '/';
chdir (myBaseDir);
return;
}
}
myPathStart = i + 1;
thePath[i] = '/';
chdir (myBaseDir);
}
i++;
}
chdir (myBaseDir);
}
if (!(myFile = fopen (thePath, "rb")))
{
if(!(i = strlen (thePath))) return;
i--;
while (i)
{
if(thePath[i - 1] == '/') break;
i--;
}
Sys_SwitchCase (thePath, i);
if (!(myFile = fopen (thePath, "rb")))
Sys_SwitchCase (thePath, i);
}
}
if (myFile != NULL)
fclose (myFile);
}
//____________________________________________________________________________________________Sys_FileOpenRead()
SInt Sys_FileOpenRead (char *thePath, SInt *theHandle)
{
SInt myHandle;
struct stat myFileInfo;
Sys_FixFileNameCase (thePath);
myHandle = open (thePath, O_RDONLY, 0666);
*theHandle = myHandle;
if (myHandle == -1)
return -1;
if (fstat (myHandle, &myFileInfo) == -1)
Sys_Error ("Can\'t open file \"%s\", reason: \"%s\".", thePath, strerror (errno));
return (myFileInfo.st_size);
}
//___________________________________________________________________________________________Sys_FileOpenWrite()
SInt Sys_FileOpenWrite (char *thePath)
{
SInt myHandle;
umask (0);
myHandle = open (thePath, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (myHandle == -1)
Sys_Error ("Can\'t open file \"%s\" for writing, reason: \"%s\".", thePath, strerror(errno));
return (myHandle);
}
//_______________________________________________________________________________________________Sys_FileClose()
void Sys_FileClose (SInt theHandle)
{
close (theHandle);
}
//________________________________________________________________________________________________Sys_FileSeek()
void Sys_FileSeek (SInt theHandle, SInt thePosition)
{
lseek (theHandle, thePosition, SEEK_SET);
}
//________________________________________________________________________________________________Sys_FileRead()
SInt Sys_FileRead (SInt theHandle, void *theDest, SInt theCount)
{
return (read (theHandle, theDest, theCount));
}
//_______________________________________________________________________________________________Sys_FileWrite()
SInt Sys_FileWrite (SInt theHandle, void *theData, SInt theCount)
{
return (write (theHandle, theData, theCount));
}
//________________________________________________________________________________________________Sys_FileTime()
int Sys_FileTime (char *thePath)
{
struct stat myBuffer;
if (stat (thePath, &myBuffer) == -1)
return (-1);
return (myBuffer.st_mtime);
}
//___________________________________________________________________________________________________Sys_mkdir()
void Sys_mkdir (char *thePath)
{
if (mkdir (thePath, 0777) != -1) return;
if (errno != EEXIST)
Sys_Error ("\"mkdir %s\" failed, reason: \"%s\".", thePath, strerror(errno));
}
//___________________________________________________________________________________________________Sys_Strtime()
void Sys_Strtime (char *theTime)
{
// FIX-ME : get system time as int Hour,int Min, int Sec
//sprintf(theTime,"%02d:%02d:%02d",0,0,0);
theTime[0]='\0';
}
//_______________________________________________________________________________________Sys_MakeCodeWriteable()
void Sys_MakeCodeWriteable (UInt32 theStartAddress, UInt32 theLength)
{
unsigned long myAddress;
SInt myPageSize = getpagesize();
myAddress = (theStartAddress & ~(myPageSize - 1)) - myPageSize;
if (mprotect ((char*)myAddress, theLength + theStartAddress - myAddress + myPageSize, 7) < 0)
Sys_Error ("Memory protection change failed!\n");
}
//___________________________________________________________________________________________________Sys_Error()
void Sys_Error (char *theError, ...)
{
va_list myArgPtr;
char myString[SYS_STRING_SIZE];
fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
va_start (myArgPtr, theError);
vsnprintf (myString, SYS_STRING_SIZE, theError, myArgPtr);
va_end (myArgPtr);
Host_Shutdown();
IN_ShowCursor (YES);
Sys_SetKeyboardRepeatEnabled (YES);
NSRunCriticalAlertPanel (@"An error has occured:", [NSString stringWithCString: myString],
NULL, NULL, NULL);
NSLog (@"An error has occured: %@\n", [NSString stringWithCString: myString]);
exit (1);
}
//____________________________________________________________________________________________________Sys_Warn()
void Sys_Warn (char *theWarning, ...)
{
#if defined(DEBUG_BUILD)
va_list myArgPtr;
char myString[SYS_STRING_SIZE];
va_start (myArgPtr, theWarning);
vsnprintf (myString, SYS_STRING_SIZE, theWarning, myArgPtr);
va_end (myArgPtr);
fprintf (stderr, "Warning: %s", myString);
#endif /* DEBUG_BUILD */
}
//_________________________________________________________________________________________________Sys_sprintf()
char * Sys_sprintf (char *theFormat, ...)
{
va_list myArgPtr;
static char myString[SYS_STRING_SIZE];
va_start (myArgPtr, theFormat);
vsnprintf (myString, SYS_STRING_SIZE, theFormat, myArgPtr);
va_end (myArgPtr);
return(myString);
}
//__________________________________________________________________________________________________Sys_Printf()
void Sys_Printf (char *theFormat, ...)
{
return;
}
//____________________________________________________________________________________________________Sys_Quit()
void Sys_Quit (void)
{
extern cvar_t gl_fsaa;
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
NSString *myString;
SInt mySamples = gl_fsaa.value;
// shutdown host:
Host_Shutdown ();
fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
fflush (stdout);
// reenable keyboard repeat:
Sys_SetKeyboardRepeatEnabled (YES);
// save the FSAA setting again [for Radeon users]:
switch (mySamples)
{
case 2:
case 4:
mySamples <<= 1;
break;
default:
mySamples = 0;
break;
}
myString = [NSString stringWithFormat: @"%d", mySamples];
if ([myString isEqualToString: INITIAL_GL_SAMPLES])
{
[myDefaults removeObjectForKey: DEFAULT_GL_SAMPLES];
}
else
{
[myDefaults setObject: myString forKey: DEFAULT_GL_SAMPLES];
}
[myDefaults synchronize];
exit (0);
}
//_______________________________________________________________________________________________Sys_FloatTime()
double Sys_FloatTime (void)
{
struct timeval myTimeValue;
struct timezone myTimeZone;
static SInt myStartSeconds;
// return deltaT since the first call:
gettimeofday (&myTimeValue, &myTimeZone);
if (!myStartSeconds)
{
myStartSeconds = myTimeValue.tv_sec;
return (myTimeValue.tv_usec / 1000000.0);
}
return ((myTimeValue.tv_sec - myStartSeconds) + (myTimeValue.tv_usec / 1000000.0));
}
//____________________________________________________________________________________________Sys_ConsoleInput()
char * Sys_ConsoleInput (void)
{
return (NULL);
}
//___________________________________________________________________________________________________Sys_Sleep()
void Sys_Sleep (void)
{
NSEvent *myEvent;
NSAutoreleasePool *myPool = NULL;
sleep (1);
while (1)
{
myPool = [[NSAutoreleasePool alloc] init];
if (myPool == NULL)
{
return;
}
// get the current event:
myEvent = [NSApp nextEventMatchingMask: NSAnyEventMask
untilDate: gDistantPast
inMode: NSDefaultRunLoopMode
dequeue: YES];
// break out if all events flushed!
if (myEvent == NULL)
{
[myPool release];
break;
}
// process the event:
[NSApp sendEvent: myEvent];
[myPool release];
}
}
//___________________________________________________________________________________________Sys_SendKeyEvents()
void Sys_SendKeyEvents (void)
{
NSEvent *myEvent;
NSAutoreleasePool *myPool;
// required for [y]es/[n]o dialogs within Quake:
myPool = [[NSAutoreleasePool alloc] init];
myEvent = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:gDistantPast
inMode:NSDefaultRunLoopMode dequeue:YES];
Sys_DoEvents (myEvent, [myEvent type]);
[myPool release];
}
//_________________________________________________________________________________________Sys_HighFPPrecision()
void Sys_HighFPPrecision (void)
{
}
//__________________________________________________________________________________________Sys_LowFPPrecision()
void Sys_LowFPPrecision (void)
{
}
//______________________________________________________________________________________________Sys_DoubleTime()
double Sys_DoubleTime (void)
{
return (Sys_FloatTime ());
}
//________________________________________________________________________________________________Sys_DebugLog()
void Sys_DebugLog (char *theFile, char *theFormat, ...)
{
va_list myArgPtr;
static char myText[SYS_STRING_SIZE];
SInt myFileDescriptor;
// get the format:
va_start (myArgPtr, theFormat);
vsnprintf (myText, SYS_STRING_SIZE, theFormat, myArgPtr);
va_end (myArgPtr);
// append the message to the debug logfile:
myFileDescriptor = open (theFile, O_WRONLY | O_CREAT | O_APPEND, 0666);
write (myFileDescriptor, myText, strlen (myText));
close (myFileDescriptor);
}
//________________________________________________________________________________Sys_SetKeyboardRepeatEnabled()
void Sys_SetKeyboardRepeatEnabled (Boolean theState)
{
static Boolean myKeyboardRepeatEnabled = YES;
static double myOriginalKeyboardRepeatInterval;
static double myOriginalKeyboardRepeatThreshold;
NXEventHandle myEventStatus;
if (theState == myKeyboardRepeatEnabled)
return;
if (!(myEventStatus = NXOpenEventStatus ()))
return;
if (theState)
{
NXSetKeyRepeatInterval (myEventStatus, myOriginalKeyboardRepeatInterval);
NXSetKeyRepeatThreshold (myEventStatus, myOriginalKeyboardRepeatThreshold);
NXResetKeyboard (myEventStatus);
}
else
{
myOriginalKeyboardRepeatInterval = NXKeyRepeatInterval (myEventStatus);
myOriginalKeyboardRepeatThreshold = NXKeyRepeatThreshold (myEventStatus);
NXSetKeyRepeatInterval (myEventStatus, 3456000.0f);
NXSetKeyRepeatThreshold (myEventStatus, 3456000.0f);
}
NXCloseEventStatus (myEventStatus);
myKeyboardRepeatEnabled = theState;
}
//_______________________________________________________________________________________Sys_GetClipboardData ()
char * Sys_GetClipboardData (void)
{
NSPasteboard *myPasteboard = NULL;
NSArray *myPasteboardTypes = NULL;
myPasteboard = [NSPasteboard generalPasteboard];
myPasteboardTypes = [myPasteboard types];
if ([myPasteboardTypes containsObject: NSStringPboardType])
{
NSString *myClipboardString;
myClipboardString = [myPasteboard stringForType: NSStringPboardType];
if (myClipboardString != NULL && [myClipboardString length] > 0)
{
return (strdup ([myClipboardString cString]));
}
}
return (NULL);
}
//____________________________________________________________________________________Sys_CheckForIDDirectory ()
void Sys_CheckForIDDirectory (void)
{
char *myBaseDir = NULL;
SInt myResult,
myPathLength;
BOOL myFirstRun = YES,
myDefaultPath = YES,
myIDPath = NO,
myTenebraePath = NO,
myPathChanged = NO;
NSOpenPanel *myOpenPanel;
NSUserDefaults *myDefaults;
NSArray *myFolder;
NSString *myValidatePath = nil,
*myBasePath = nil;
// get the user defaults:
myDefaults = [NSUserDefaults standardUserDefaults];
// prepare the open panel for requesting the "id1" folder:
myOpenPanel = [NSOpenPanel openPanel];
[myOpenPanel setAllowsMultipleSelection: NO];
[myOpenPanel setCanChooseFiles: NO];
[myOpenPanel setCanChooseDirectories: YES];
[myOpenPanel setTitle: @"Please locate the \"id1\" folder:"];
// get the "id1" path from the prefs:
myBasePath = [myDefaults stringForKey: DEFAULT_BASE_PATH];
while (1)
{
if (myBasePath)
{
// check if the path exists:
myValidatePath = [myBasePath stringByAppendingPathComponent: ID1_VALIDATION_FILE];
myIDPath = [[NSFileManager defaultManager] fileExistsAtPath: myValidatePath];
if (myIDPath == YES)
{
// check for the "tenebrae" folder:
myValidatePath = [myBasePath stringByAppendingPathComponent: TENEBRAE_VALIDATION_FILE];
myTenebraePath = [[NSFileManager defaultManager] fileExistsAtPath: myValidatePath];
if (myTenebraePath == YES)
{
// get a POSIX version of the path:
myBaseDir = (char *) [myBasePath fileSystemRepresentation];
myPathLength = strlen (myBaseDir);
// check if the last component was "id1":
if (myPathLength >= 3)
{
if ((myBaseDir[myPathLength - 3] == 'i' || myBaseDir[myPathLength - 3] == 'I') &&
(myBaseDir[myPathLength - 2] == 'd' || myBaseDir[myPathLength - 2] == 'D') &&
myBaseDir[myPathLength - 1] == '1')
{
// remove "id1":
myBaseDir[myPathLength - 3] = 0x00;
// change working directory to the selected path:
if (!chdir (myBaseDir))
{
if (myPathChanged)
{
[myDefaults setObject: myBasePath forKey: DEFAULT_BASE_PATH];
[myDefaults synchronize];
}
break;
}
else
{
NSRunCriticalAlertPanel (@"Can\'t change to the selected path!",
@"The selection was: \"%@\"", NULL, NULL, NULL,
myBasePath);
}
}
}
}
}
}
// if the path from the user defaults is bad, look if the id1 folder is located at the same
// folder as our Quake application:
if (myDefaultPath == YES)
{
myBasePath = [[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent]
stringByAppendingPathComponent: INITIAL_BASE_PATH];
myPathChanged = YES;
myDefaultPath = NO;
continue;
}
else
{
// if we run for the first time or the location of the "id1" folder changed, show a dialog:
if (myFirstRun == YES)
{
NSRunInformationalAlertPanel (@"You will now be asked to locate the \"id1\" folder.",
@"This folder is part of the standard installation of "
@"Quake. You will only be asked for it again, if you "
@"change the location of this folder.",
NULL, NULL, NULL);
myFirstRun = NO;
}
else
{
if (myIDPath == NO)
{
NSRunInformationalAlertPanel (@"You have not selected the \"id1\" folder.",
@"The \"id1\" folder comes with the shareware or retail "
@"version of Quake and has to contain at least the two "
@"files \"PAK0.PAK\" and \"PAK1.PAK\".",
NULL, NULL, NULL);
}
else
{
if (myTenebraePath == NO)
{
NSRunInformationalAlertPanel (@"Your Quake folder, which holds the \"id1\" folder, "
@"does not hold the \"tenebrae\" folder.",
@"The \"tenebrae\" folder comes on the same disc image "
@"as this application. Please copy this folder to your "
@"Quake folder and try agin.",
NULL, NULL, NULL);
}
}
}
}
// request the "id1" folder:
myResult = [myOpenPanel runModalForDirectory:nil file: nil types: nil];
// if the user selected "Cancel", quit Quake:
if (myResult == NSCancelButton)
{
[NSApp terminate: nil];
}
// get the selected path:
myFolder = [myOpenPanel filenames];
if (![myFolder count])
{
continue;
}
myBasePath = [myFolder objectAtIndex: 0];
myPathChanged = YES;
}
// just check if the mod is located at the same folder as the id1 folder:
if (gWasDragged && strcmp (gModDir, myBaseDir) != 0)
{
Sys_Error ("The modification has to be located within the same folder as the \"id1\" folder.");
}
}
//________________________________________________________________________________________Sys_CheckSpecialKeys()
int Sys_CheckSpecialKeys (int theKey)
{
extern qboolean keydown[];
int myKey;
// do a fast evaluation:
if (keydown[K_COMMAND] == false)
{
return (0);
}
myKey = toupper (theKey);
// check the keys:
switch (myKey)
{
case K_TAB:
case 'H':
if (gDisplayFullscreen == NO)
{
// hide application if windowed [CMD-TAB handled by system]:
if (myKey == 'H')
{
[NSApp hide: NULL];
return (1);
}
}
break;
case 'M':
// minimize window [CMD-M]:
if (gDisplayFullscreen == NO && gIsMinimized == NO && gWindow != NULL)
{
[gWindow miniaturize: NULL];
return (1);
}
break;
case 'Q':
// application quit [CMD-Q]:
M_Menu_Quit_f ();
return (1);
}
// paste [CMD-V] already checked inside "keys.c" and "menu.c"!
return (0);
}
//________________________________________________________________________________________________Sys_DoEvents()
void Sys_DoEvents (NSEvent *myEvent, NSEventType myType)
{
static NSString *myKeyboardBuffer;
static CGMouseDelta myMouseDeltaX, myMouseDeltaY, myMouseWheel;
static unichar myCharacter;
static UInt8 i;
static UInt16 myKeyPad;
static UInt32 myFilteredMouseButtons, myMouseButtons, myLastMouseButtons = 0,
myKeyboardBufferSize, myFilteredFlags, myFlags, myLastFlags = 0;
// we check here for events:
switch (myType)
{
// mouse buttons [max. number of supported buttons is defined via MOUSE_BUTTONS]:
case NSSystemDefined:
CHECK_MOUSE_ENABLED ();
if ([myEvent subtype] == 7)
{
myMouseButtons = [myEvent data2];
myFilteredMouseButtons = myLastMouseButtons ^ myMouseButtons;
for (i = 0; i < MOUSE_BUTTONS; i++)
{
if(myFilteredMouseButtons & (1 << i))
Key_Event (K_MOUSE1 + i, (myMouseButtons & (1 << i)) ? 1 : 0);
}
myLastMouseButtons = myMouseButtons;
}
break;
// scroll wheel:
case NSScrollWheel:
CHECK_MOUSE_ENABLED ();
myMouseWheel = [myEvent deltaY];
if(myMouseWheel > 0)
{
Key_Event (K_MWHEELUP, true);
Key_Event (K_MWHEELUP, false);
}
else
{
Key_Event (K_MWHEELDOWN, true);
Key_Event (K_MWHEELDOWN, false);
}
break;
// mouse movement:
case NSMouseMoved:
case NSLeftMouseDragged:
case NSRightMouseDragged:
case 27:
CHECK_MOUSE_ENABLED ();
CGGetLastMouseDelta (&myMouseDeltaX, &myMouseDeltaY);
IN_ReceiveMouseMove (myMouseDeltaX, myMouseDeltaY);
break;
// key up and down:
case NSKeyDown:
case NSKeyUp:
myKeyboardBuffer = [myEvent charactersIgnoringModifiers];
myKeyboardBufferSize = [myKeyboardBuffer length];
for (i = 0; i < myKeyboardBufferSize; i++)
{
myCharacter = [myKeyboardBuffer characterAtIndex: i];
if ((myCharacter & 0xFF00) == 0xF700)
{
myCharacter -= 0xF700;
if (myCharacter < 0x47)
{
Key_Event (gSpecialKey[myCharacter], (myType == NSKeyDown));
}
}
else
{
myFlags = [myEvent modifierFlags];
// v1.0.6: we will now check hardware codes for the keypad,
// otherwise we can not distinguish between between keypad numbers
// and normal number keys. We will only check for the keypad, if
// the flag for the keypad is set!
if (myFlags & NSNumericPadKeyMask)
{
myKeyPad = [myEvent keyCode];
if (myKeyPad < 0x5D && gNumPadKey[myKeyPad] != 0x00)
{
Key_Event (gNumPadKey[myKeyPad], (myType == NSKeyDown));
break;
}
}
if (myCharacter < 0x80)
{
if (myCharacter >= 'A' && myCharacter <= 'Z')
myCharacter += 'a' - 'A';
Key_Event (myCharacter, (myType == NSKeyDown));
}
}
}
break;
// special keys:
case NSFlagsChanged:
myFlags = [myEvent modifierFlags];
myFilteredFlags = myFlags ^ myLastFlags;
myLastFlags = myFlags;
if (myFilteredFlags & NSAlphaShiftKeyMask)
Key_Event (K_CAPSLOCK, (myFlags & NSAlphaShiftKeyMask) ? 1:0);
if (myFilteredFlags & NSShiftKeyMask)
Key_Event (K_SHIFT, (myFlags & NSShiftKeyMask) ? 1:0);
if (myFilteredFlags & NSControlKeyMask)
Key_Event (K_CTRL, (myFlags & NSControlKeyMask) ? 1:0);
if (myFilteredFlags & NSAlternateKeyMask)
Key_Event (K_ALT, (myFlags & NSAlternateKeyMask) ? 1:0);
if (myFilteredFlags & NSCommandKeyMask)
Key_Event (K_COMMAND, (myFlags & NSCommandKeyMask) ? 1:0);
if (myFilteredFlags & NSNumericPadKeyMask)
Key_Event (K_NUMLOCK, (myFlags & NSNumericPadKeyMask) ? 1:0);
break;
// ignore anything else, if not windowed:
default:
if (gDisplayFullscreen == NO)
{
[NSApp sendEvent: myEvent];
}
break;
}
}
#pragma mark -
//__________________________________________________________________________________________iMPLEMENTATION_Quake
@implementation Quake : NSObject
//____________________________________________________________________________________________________initialize
+ (void) initialize
{
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
NSString *myDefaultPath = [[[[NSBundle mainBundle]
bundlePath]
stringByDeletingLastPathComponent]
stringByAppendingPathComponent: INITIAL_BASE_PATH];
// required for event handling:
gDistantPast = [[NSDate distantPast] retain];
// register application defaults:
[myDefaults registerDefaults: [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects: myDefaultPath,
INITIAL_GL_DISPLAY,
INITIAL_GL_DISPLAY_MODE,
INITIAL_GL_COLORS,
INITIAL_GL_SAMPLES,
INITIAL_GL_FADE_ALL,
INITIAL_GL_FULLSCREEN,
nil]
forKeys:
[NSArray arrayWithObjects: DEFAULT_BASE_PATH,
DEFAULT_GL_DISPLAY,
DEFAULT_GL_DISPLAY_MODE,
DEFAULT_GL_COLORS,
DEFAULT_GL_SAMPLES,
DEFAULT_GL_FADE_ALL,
DEFAULT_GL_FULLSCREEN,
nil]
]];
}
//_______________________________________________________________________________________________________dealloc
- (void) dealloc
{
[gDistantPast release];
[super dealloc];
}
//_________________________________________________________________________________________application:openFile:
- (BOOL) application: (NSApplication *) theSender openFile: (NSString *) theFilePath
{
// allow only dragging one time:
if (gDenyDrag == YES)
{
return (NO);
}
gDenyDrag = YES;
if (gArgCount > 2)
{
return (NO);
}
// we have received a filepath:
if (theFilePath != NULL)
{
char *myMod = (char *) [[theFilePath lastPathComponent] fileSystemRepresentation];
char *myPath = (char *) [theFilePath fileSystemRepresentation];
char **myNewArgValues;
Boolean myDirectory;
SInt32 i;
// is the filepath a folder?
if (![[NSFileManager defaultManager] fileExistsAtPath:theFilePath isDirectory:&myDirectory])
{
Sys_Error ("The dragged item is not a valid file!");
}
if (myDirectory == NO)
{
Sys_Error ("The dragged item is not a folder!");
}
// prepare the new command line options:
myNewArgValues = malloc (sizeof(char *) * 3);
CHECK_MALLOC (myNewArgValues);
gArgCount = 3;
myNewArgValues[0] = gArgValues[0];
gArgValues = myNewArgValues;
gArgValues[1] = GAME_COMMAND;
gArgValues[2] = malloc (strlen (myPath) + 1);
CHECK_MALLOC (gArgValues[2]);
strcpy (gArgValues[2], myMod);
// get the path of the mod [compare it with the id1 path later]:
gModDir = malloc (strlen (myPath) + 1);
CHECK_MALLOC (gModDir);
strcpy (gModDir, myPath);
// dispose the foldername of the mod:
i = strlen (gModDir) - 1;
while (i > 1)
{
if (gModDir[i - 1] == '/')
{
gModDir[i] = 0x00;
break;
}
i--;
}
gWasDragged = YES;
return (YES);
}
return (NO);
}
//________________________________________________________________________________applicationDidFinishLaunching:
- (void) applicationDidFinishLaunching: (NSNotification *) theNote
{
// don't accept any drags from this point on!
gDenyDrag = YES;
// enable services:
[NSApp setServicesProvider: self];
// examine the "id1" folder and check if the MOD has the right location:
Sys_CheckForIDDirectory ();
NS_DURING
{
// show the settings dialog:
[self setupDialog];
}
NS_HANDLER
{
NSString *myException = [localException reason];
if (myException == NULL)
{
myException = @"Unknown exception!";
}
NSLog (@"An error has occured: %@\n", myException);
NSRunCriticalAlertPanel (@"An error has occured:", myException, NULL, NULL, NULL);
}
NS_ENDHANDLER;
}
//___________________________________________________________________________________applicationShouldTerminate:
- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) theSender
{
if (gHostInitialized == YES)
{
M_Menu_Quit_f ();
return (NSTerminateCancel);
}
return (NSTerminateNow);
}
//___________________________________________________________________________________applicationDidResignActive:
- (void) applicationDidResignActive: (NSNotification *) theNote
{
if (gHostInitialized == NO)
{
return;
}
IN_ShowCursor (YES);
Sys_SetKeyboardRepeatEnabled (YES);
gIsDeactivated = YES;
}
//___________________________________________________________________________________applicationDidBecomeActive:
- (void) applicationDidBecomeActive: (NSNotification *) theNote
{
if (gHostInitialized == NO)
{
return;
}
if (gDisplayFullscreen == YES || (gMouseEnabled == YES && _windowed_mouse.value != 0.0f))
{
IN_ShowCursor (NO);
}
Sys_SetKeyboardRepeatEnabled (NO);
gIsDeactivated = NO;
}
//__________________________________________________________________________________________applicationWillHide:
- (void) applicationWillHide: (NSNotification *) theNote
{
if (gHostInitialized == NO)
{
return;
}
IN_ShowCursor (YES);
Sys_SetKeyboardRepeatEnabled (YES);
gIsHidden = YES;
}
//________________________________________________________________________________________applicationWillUnhide:
- (void) applicationWillUnhide: (NSNotification *) theNote
{
if (gHostInitialized == NO)
{
return;
}
if (gDisplayFullscreen == YES || (gMouseEnabled == YES && _windowed_mouse.value != 0.0f))
{
IN_ShowCursor (NO);
}
Sys_SetKeyboardRepeatEnabled (NO);
gIsHidden = NO;
gIsDeactivated = NO;
}
//_____________________________________________________________________________________________buildDisplayList:
- (void) buildDisplayList
{
UInt32 i;
// retrieve displays list
if (CGGetActiveDisplayList (MAX_DISPLAYS, gDisplayList, &gDisplayCount) != CGDisplayNoErr)
{
Sys_Error ("Can\'t build display list!");
}
// add the displays to the popup:
[displayPopUp removeAllItems];
for (i = 0; i < gDisplayCount; i++)
{
[displayPopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i]];
}
[displayPopUp selectItemAtIndex: 0];
}
//__________________________________________________________________________________________buildResolutionList:
- (IBAction) buildResolutionList: (id) theSender
{
CGDisplayCount myDisplayIndex;
CGDirectDisplayID mySelectedDisplay;
NSArray *myDisplayModes;
UInt16 myResolutionIndex = 0, myModeCount, i;
UInt32 myColors, myDepth;
NSDictionary *myCurMode;
NSString *myDescription, *myOldResolution = NULL;
// get the old resolution:
if (gModeList != NULL)
{
// modelist can have a count of zero...
if([gModeList count] > 0)
{
myOldResolution = [self displayModeToString: [gModeList objectAtIndex:
[modePopUp indexOfSelectedItem]]];
}
[gModeList release];
gModeList = NULL;
}
// figure out which display was selected by the user:
myDisplayIndex = [displayPopUp indexOfSelectedItem];
if (myDisplayIndex >= gDisplayCount)
{
myDisplayIndex = 0;
[displayPopUp selectItemAtIndex: myDisplayIndex];
}
mySelectedDisplay = gDisplayList[myDisplayIndex];
// get the list of display modes:
myDisplayModes = [(NSArray *) CGDisplayAvailableModes (mySelectedDisplay) retain];
CHECK_MALLOC (myDisplayModes);
gModeList = [[NSMutableArray alloc] init];
CHECK_MALLOC (gModeList);
// filter modes:
myModeCount = [myDisplayModes count];
myColors = ([colorsPopUp indexOfSelectedItem] == 0) ? 16 : 32;
for (i = 0; i < myModeCount; i++)
{
myCurMode = [myDisplayModes objectAtIndex: i];
myDepth = [[myCurMode objectForKey: (NSString *) kCGDisplayBitsPerPixel] intValue];
if (myColors == myDepth)
{
UInt16 j = 0;
// I got double entries while testing [OSX bug?], so we have to check:
while (j < [gModeList count])
{
if ([[self displayModeToString: [gModeList objectAtIndex: j]] isEqualToString:
[self displayModeToString: myCurMode]])
{
break;
}
j++;
}
if (j == [gModeList count])
{
[gModeList addObject: myCurMode];
}
}
}
// Fill the popup with the resulting modes:
[modePopUp removeAllItems];
myModeCount = [gModeList count];
if (myModeCount == 0)
{
[modePopUp addItemWithTitle: @"not available!"];
}
else
{
for (i = 0; i < myModeCount; i++)
{
myDescription = [self displayModeToString: [gModeList objectAtIndex: i]];
if(myOldResolution != nil)
{
if([myDescription isEqualToString: myOldResolution]) myResolutionIndex = i;
}
[modePopUp addItemWithTitle: myDescription];
}
}
[modePopUp selectItemAtIndex: myResolutionIndex];
// last not least check for multisample buffers:
if (GL_CheckARBMultisampleExtension (mySelectedDisplay) == YES)
{
[samplesPopUp setEnabled: YES];
}
else
{
[samplesPopUp setEnabled: NO];
[samplesPopUp selectItemAtIndex: 0];
}
// clean up:
if (myDisplayModes != NULL)
{
[myDisplayModes release];
}
}
//__________________________________________________________________________________________________setupDialog:
- (void) setupDialog
{
NSUserDefaults *myDefaults = NULL;
NSString *myDescriptionStr = NULL,
*myDefaultModeStr = NULL;
UInt i = 0,
myDefaultDisplay,
myDefaultColors,
myDefaultSamples;
// build the display list:
[self buildDisplayList];
myDefaults = [NSUserDefaults standardUserDefaults];
// set up the displays popup:
myDefaultDisplay = [[myDefaults stringForKey: DEFAULT_GL_DISPLAY] intValue];
if (myDefaultDisplay > gDisplayCount)
{
myDefaultDisplay = 0;
}
[displayPopUp selectItemAtIndex: myDefaultDisplay];
// set up the colors popup:
myDefaultColors = [[myDefaults stringForKey: DEFAULT_GL_COLORS] intValue];
if (myDefaultColors > 1)
{
myDefaultDisplay = 1;
}
[colorsPopUp selectItemAtIndex: myDefaultColors];
// build the resolution list:
[self buildResolutionList: nil];
// setup the modes popup:
myDefaultModeStr = [myDefaults stringForKey: DEFAULT_GL_DISPLAY_MODE];
while (i < [gModeList count])
{
myDescriptionStr = [self displayModeToString: [gModeList objectAtIndex: i]];
if ([myDefaultModeStr isEqualToString: myDescriptionStr])
{
[modePopUp selectItemAtIndex: i];
break;
}
i++;
}
// setup the samples popup:
myDefaultSamples = ([[myDefaults stringForKey: DEFAULT_GL_SAMPLES] intValue]) >> 2;
if (myDefaultSamples > 2)
myDefaultSamples = 2;
if ([samplesPopUp isEnabled] == NO)
myDefaultSamples = 0;
[samplesPopUp selectItemAtIndex: myDefaultSamples];
// setup checkboxes:
[fadeAllCheckBox setState: [myDefaults boolForKey: DEFAULT_GL_FADE_ALL]];
[fullscreenCheckBox setState: [myDefaults boolForKey: DEFAULT_GL_FULLSCREEN]];
// setup the window:
[settingsWindow center];
[settingsWindow makeKeyAndOrderFront: nil];
#ifdef BETA_VERSION
NSBeginInformationalAlertSheet (@"This is a public beta version of TenebraeQuake. "
@"Since we are only able to test the game with an ATI Radeon/PCI, "
@"we need your feedback on compatibility with NVidia boards.",
NULL, NULL, NULL, settingsWindow, NULL, NULL, NULL, NULL,
@"Please send bug reports to \"awe@fruitz-of-dojo.de\"!");
#endif /* BETA_VERSION */
}
//_________________________________________________________________________________saveCheckBox:initial:default:
- (void) saveCheckBox: (NSButton *) theButton initial: (NSString *) theInitial
default: (NSString *) theDefault userDefaults: (NSUserDefaults *) theUserDefaults
{
// has our checkbox the initial value? if, delete from defaults::
if ([theButton state] == [self isEqualTo: theInitial])
{
[theUserDefaults removeObjectForKey: theDefault];
}
else
{
// write to defaults:
if ([theButton state] == YES)
{
[theUserDefaults setObject: @"YES" forKey: theDefault];
}
else
{
[theUserDefaults setObject: @"NO" forKey: theDefault];
}
}
}
//___________________________________________________________________________________saveString:initial:default:
- (void) saveString: (NSString *) theString initial: (NSString *) theInitial
default: (NSString *) theDefault userDefaults: (NSUserDefaults *) theUserDefaults
{
// has our popup menu the initial value? if, delete from defaults:
if ([theString isEqualToString: theInitial])
{
[theUserDefaults removeObjectForKey: theDefault];
}
else
{
// write to defaults:
[theUserDefaults setObject: theString forKey: theDefault];
}
}
//______________________________________________________________________________________________________newGame:
- (void) newGame: (id) theSender
{
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
NSString *myModeStr;
UInt myMode, myColors;
// check if display modes are available:
if ([gModeList count] == 0)
{
NSBeginAlertSheet (@"No display modes available!", NULL, NULL, NULL, settingsWindow, NULL,
NULL, NULL, NULL, @"Please try other displays and/or color settings.");
return;
}
// save the display:
gDisplay = [displayPopUp indexOfSelectedItem];
[self saveString: [NSString stringWithFormat: @"%d", gDisplay] initial: INITIAL_GL_DISPLAY
default: DEFAULT_GL_DISPLAY
userDefaults: myDefaults];
// save the display mode:
myMode = [modePopUp indexOfSelectedItem];
myModeStr = [self displayModeToString: [gModeList objectAtIndex: myMode]];
[self saveString: myModeStr initial: INITIAL_GL_DISPLAY_MODE default: DEFAULT_GL_DISPLAY_MODE
userDefaults: myDefaults];
// save the colors:
myColors = [colorsPopUp indexOfSelectedItem];
[self saveString: [NSString stringWithFormat: @"%d", myColors] initial: INITIAL_GL_COLORS
default: DEFAULT_GL_COLORS
userDefaults: myDefaults];
// save the samples:
if ([samplesPopUp isEnabled] == YES)
gARBMultiSamples = [samplesPopUp indexOfSelectedItem] << 2;
else
gARBMultiSamples = 0;
[self saveString: [NSString stringWithFormat: @"%d", gARBMultiSamples] initial: INITIAL_GL_SAMPLES
default: DEFAULT_GL_SAMPLES
userDefaults: myDefaults];
// save the state of the "fade all" checkbox:
[self saveCheckBox: fadeAllCheckBox initial: INITIAL_GL_FADE_ALL default: DEFAULT_GL_FADE_ALL
userDefaults: myDefaults];
// save the state of the "fullscreen" checkbox:
[self saveCheckBox: fullscreenCheckBox initial: INITIAL_GL_FULLSCREEN
default: DEFAULT_GL_FULLSCREEN
userDefaults: myDefaults];
// synchronize prefs and start Quake:
[myDefaults synchronize];
[settingsWindow close];
gDisplayMode = [gModeList objectAtIndex: myMode];
gDisplayFullscreen = [fullscreenCheckBox state];
gFadeAllDisplays = [fadeAllCheckBox state];
[self startQuake];
}
//____________________________________________________________________________________________________isEqualTo:
- (Boolean) isEqualTo: (NSString *) theString
{
// just some boolean str compare:
if([theString isEqualToString:@"YES"]) return(YES);
return(NO);
}
//__________________________________________________________________________________________displayModeToString:
- (NSString *) displayModeToString: (NSDictionary *) theDisplayMode
{
// generate a display mode string with the format: "(width)x(height) (frequency)Hz":
return ([NSString stringWithFormat: @"%dx%d %dHz",
[[theDisplayMode objectForKey: (NSString *)kCGDisplayWidth] intValue],
[[theDisplayMode objectForKey: (NSString *)kCGDisplayHeight] intValue],
[[theDisplayMode objectForKey: (NSString *)kCGDisplayRefreshRate] intValue]]);
}
//_______________________________________________________________________________connectToServer:userData:error:
- (void) connectToServer: (NSPasteboard *) thePasteboard userData:(NSString *)theData
error: (NSString **) theError
{
NSArray *myPasteboardTypes;
myPasteboardTypes = [thePasteboard types];
if ([myPasteboardTypes containsObject: NSStringPboardType])
{
NSString *myRequestedServer;
myRequestedServer = [thePasteboard stringForType: NSStringPboardType];
if (myRequestedServer != NULL)
{
snprintf (gRequestedServer, 128, "connect %s\n", [myRequestedServer cString]);
// required because of the GLQuakeWorld options dialog.
// we have to wait with the command until host_init() is finished!
if (gHostInitialized == YES)
{
Cbuf_AddText (gRequestedServer);
}
return;
}
}
*theError = @"Unable to connect to a server: could not find a string on the pasteboard!";
}
//__________________________________________________________________________________________________pasteString:
- (IBAction) pasteString: (id) theSender
{
extern qboolean keydown[];
qboolean myOldCommand,
myOldVKey;
// get the old state of the paste keys:
myOldCommand = keydown[K_COMMAND];
myOldVKey = keydown['v'];
// send the keys required for paste:
keydown[K_COMMAND] = true;
Key_Event ('v', true);
// set the old state of the paste keys:
Key_Event ('v', false);
keydown[K_COMMAND] = myOldCommand;
}
//_____________________________________________________________________________________________________visitFOD:
- (IBAction) visitFOD: (id) theSender
{
[self performOpenURL: FRUITZ_OF_DOJO_URL];
}
//_______________________________________________________________________________________________performOpenURL:
- (void) performOpenURL: (NSString *) theURL
{
NSTask *openURL;
if((openURL = [[NSTask alloc] init]))
{
[openURL setLaunchPath: @"/usr/bin/osascript"];
[openURL setCurrentDirectoryPath: [@"~/" stringByExpandingTildeInPath]];
[openURL setArguments: [NSArray arrayWithObjects:@"-e",
[NSString stringWithFormat:@"open location \"%@\"", theURL],
NULL]];
[openURL launch];
}
else
{
NSRunAlertPanel ([NSString stringWithFormat:@"Can\'t launch the URL:\n \"%@\".", theURL],
@"Reason: Task initialization failed.", NULL, NULL, NULL);
}
[openURL release];
}
//___________________________________________________________________________________________________startQuake:
- (void) startQuake
{
extern SInt32 vcrFile;
extern SInt32 recording;
quakeparms_t myParameters;
SInt32 j;
NSEvent *myEvent;
NSAutoreleasePool *myPool = NULL;
double myTime, myOldtime, myNewtime;
// prepare host init:
signal (SIGFPE, SIG_IGN);
memset (&myParameters, 0x00, sizeof (myParameters));
COM_InitArgv (gArgCount, gArgValues);
myParameters.argc = com_argc;
myParameters.argv = com_argv;
myParameters.memsize = 32*1024*1024;
j = COM_CheckParm ("-mem");
if (j)
{
myParameters.memsize = (int) (Q_atof (com_argv[j + 1]) * 1024 * 1024);
}
myParameters.membase = malloc (myParameters.memsize);
myParameters.basedir = QUAKE_BASE_PATH;
fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
Host_Init (&myParameters);
gHostInitialized = YES;
if(COM_CheckParm ("-nostdout"))
{
gNoStdOut = 1;
}
// disable keyboard repeat:
Sys_SetKeyboardRepeatEnabled (NO);
// some nice console output for credits:
Con_Printf ("\n<><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>\n");
Con_Printf (" TenebraeQuake for MacOS X - v%0.2f\n", MACOSX_VERSION);
Con_Printf (" Ported by: awe^fruitz of dojo\n");
Con_Printf ("Visit: http://www.fruitz-of-dojo.de\n");
Con_Printf ("\n tiger style kung fu is strong\n");
Con_Printf (" but our style is more effective!\n");
Con_Printf ("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>\n\n");
myOldtime = Sys_FloatTime ();
if (gRequestedServer[0] != 0x0)
{
Cbuf_AddText (gRequestedServer);
}
// our main loop:
while (1)
{
if (gIsHidden == YES)
{
S_StopAllSounds (YES);
while (gIsHidden == YES)
{
Sys_Sleep ();
}
Key_Event (K_COMMAND, 0);
}
myPool = [[NSAutoreleasePool alloc] init];
myEvent = [NSApp nextEventMatchingMask: NSAnyEventMask
untilDate: gDistantPast
inMode: NSDefaultRunLoopMode
dequeue: YES];
// do a frame if we didn't receive an event:
if (!myEvent)
{
[myPool release];
myNewtime = Sys_FloatTime();
myTime = myNewtime - myOldtime;
if (cls.state == ca_dedicated)
{
if (myTime < sys_ticrate.value && (vcrFile == -1 || recording))
{
usleep(1);
continue;
}
myTime = sys_ticrate.value;
}
if (myTime > sys_ticrate.value * 2)
{
myOldtime = myNewtime;
}
else
{
myOldtime += myTime;
}
// finally do the frame:
Host_Frame (myTime);
}
else
{
// do events:
Sys_DoEvents (myEvent, [myEvent type]);
[myPool release];
}
}
}
@end
#pragma mark -
//________________________________________________________________________________________________________main()
SInt main (SInt theArgCount, const char **theArgValues)
{
// just startup the application [if we have no commandline app]:
gArgCount = theArgCount;
gArgValues = (char **) theArgValues;
return (NSApplicationMain (theArgCount, theArgValues));
}
//___________________________________________________________________________________________________________eOF