aboutsummaryrefslogtreecommitdiffstats
path: root/code/macosx/Q3Controller.m
diff options
context:
space:
mode:
Diffstat (limited to 'code/macosx/Q3Controller.m')
-rwxr-xr-xcode/macosx/Q3Controller.m435
1 files changed, 435 insertions, 0 deletions
diff --git a/code/macosx/Q3Controller.m b/code/macosx/Q3Controller.m
new file mode 100755
index 0000000..7839d59
--- /dev/null
+++ b/code/macosx/Q3Controller.m
@@ -0,0 +1,435 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#import "Q3Controller.h"
+
+#import <Foundation/Foundation.h>
+#import <AppKit/AppKit.h>
+
+#include "client.h"
+#include "macosx_local.h"
+//#include "GameRanger SDK/gameranger.h"
+#ifdef OMNI_TIMER
+#import "macosx_timers.h"
+#endif
+
+#define MAX_ARGC 1024
+
+static qboolean Sys_IsProcessingTerminationRequest = qfalse;
+static void Sys_CreatePathToFile(NSString *path, NSDictionary *attributes);
+
+@interface Q3Controller (Private)
+- (void)quakeMain;
+@end
+
+@implementation Q3Controller
+
+#ifndef DEDICATED
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notification;
+{
+ NS_DURING {
+ [self quakeMain];
+ } NS_HANDLER {
+ Sys_Error("%@", [localException reason]);
+ } NS_ENDHANDLER;
+ Sys_Quit();
+}
+
+- (void)applicationDidUnhide:(NSNotification *)notification;
+{
+ // Don't reactivate the game if we are asking whether to quit
+ if (Sys_IsProcessingTerminationRequest)
+ return;
+
+ if (!Sys_Unhide())
+ // Didn't work -- hide again so we should get another chance to unhide later
+ [NSApp hide: nil];
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
+{
+ int choice;
+
+ if (!Sys_IsHidden) {
+ // We're terminating via -terminate:
+ return NSTerminateNow;
+ }
+
+ // Avoid reactivating GL when we unhide due to this panel
+ Sys_IsProcessingTerminationRequest = qtrue;
+ choice = NSRunAlertPanel(nil, @"Quit without saving?", @"Don't Quit", @"Quit", nil);
+ Sys_IsProcessingTerminationRequest = qfalse;
+
+ if (choice == NSAlertAlternateReturn)
+ return NSTerminateNow;
+
+ // Make sure we get re-hidden
+ [NSApp hide:nil];
+
+ return NSTerminateCancel;
+}
+
+// Actions
+
+- (IBAction)paste:(id)sender;
+{
+ int shiftWasDown, insertWasDown;
+ unsigned int currentTime;
+
+ currentTime = Sys_Milliseconds();
+ // Save the original keyboard state
+ shiftWasDown = keys[K_SHIFT].down;
+ insertWasDown = keys[K_INS].down;
+ // Fake a Shift-Insert keyboard event
+ keys[K_SHIFT].down = qtrue;
+ Sys_QueEvent(currentTime, SE_KEY, K_INS, qtrue, 0, NULL);
+ Sys_QueEvent(currentTime, SE_KEY, K_INS, qfalse, 0, NULL);
+ // Restore the original keyboard state
+ keys[K_SHIFT].down = shiftWasDown;
+ keys[K_INS].down = insertWasDown;
+}
+
+extern void CL_Quit_f(void);
+
+
+- (IBAction)requestTerminate:(id)sender;
+{
+ Com_Quit_f();
+ // UI_QuitMenu();
+}
+
+- (void)showBanner;
+{
+ static BOOL hasShownBanner = NO;
+
+ if (!hasShownBanner) {
+ cvar_t *showBanner;
+
+ hasShownBanner = YES;
+ showBanner = Cvar_Get("cl_showBanner", "1", 0);
+ if (showBanner->integer != 0) {
+ NSPanel *splashPanel;
+ NSImage *bannerImage;
+ NSRect bannerRect;
+ NSImageView *bannerImageView;
+
+ bannerImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"banner.jpg"]];
+ bannerRect = NSMakeRect(0.0, 0.0, [bannerImage size].width, [bannerImage size].height);
+
+ splashPanel = [[NSPanel alloc] initWithContentRect:bannerRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
+
+ bannerImageView = [[NSImageView alloc] initWithFrame:bannerRect];
+ [bannerImageView setImage:bannerImage];
+ [splashPanel setContentView:bannerImageView];
+ [bannerImageView release];
+
+ [splashPanel center];
+ [splashPanel setHasShadow:YES];
+ [splashPanel orderFront: nil];
+ [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.5]];
+ [splashPanel close];
+
+ [bannerImage release];
+ }
+ }
+}
+
+// Services
+
+- (void)connectToServer:(NSPasteboard *)pasteboard userData:(NSString *)data error:(NSString **)error;
+{
+ NSArray *pasteboardTypes;
+
+ pasteboardTypes = [pasteboard types];
+ if ([pasteboardTypes containsObject:NSStringPboardType]) {
+ NSString *requestedServer;
+
+ requestedServer = [pasteboard stringForType:NSStringPboardType];
+ if (requestedServer) {
+ Cbuf_AddText(va("connect %s\n", [requestedServer cString]));
+ return;
+ }
+ }
+ *error = @"Unable to connect to server: could not find string on pasteboard";
+}
+
+- (void)performCommand:(NSPasteboard *)pasteboard userData:(NSString *)data error:(NSString **)error;
+{
+ NSArray *pasteboardTypes;
+
+ pasteboardTypes = [pasteboard types];
+ if ([pasteboardTypes containsObject:NSStringPboardType]) {
+ NSString *requestedCommand;
+
+ requestedCommand = [pasteboard stringForType:NSStringPboardType];
+ if (requestedCommand) {
+ Cbuf_AddText(va("%s\n", [requestedCommand cString]));
+ return;
+ }
+ }
+ *error = @"Unable to perform command: could not find string on pasteboard";
+}
+
+#endif
+
+- (void)quakeMain;
+{
+ NSAutoreleasePool *pool;
+ int argc = 0;
+ const char *argv[MAX_ARGC];
+ NSProcessInfo *processInfo;
+ NSArray *arguments;
+ unsigned int argumentIndex, argumentCount;
+ NSFileManager *defaultManager;
+ unsigned int commandLineLength;
+ NSString *installationPathKey, *installationPath;
+ char *cmdline;
+ BOOL foundDirectory;
+ NSString *appName, *demoAppName, *selectButton;
+ int count = 0;
+ pool = [[NSAutoreleasePool alloc] init];
+
+ [NSApp setServicesProvider:self];
+
+ processInfo = [NSProcessInfo processInfo];
+ arguments = [processInfo arguments];
+ argumentCount = [arguments count];
+ for (argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) {
+ NSString *arg;
+
+ arg = [arguments objectAtIndex:argumentIndex];
+ // Don't pass the Process Serial Number command line arg that the Window Server/Finder invokes us with
+ if ([arg hasPrefix: @"-psn_"])
+ continue;
+
+ argv[argc++] = strdup([arg cString]);
+ }
+
+ // Figure out where the level data is stored.
+ installationPathKey = @"RetailInstallationPath";
+
+ installationPath = [[NSUserDefaults standardUserDefaults] objectForKey:installationPathKey];
+ if (!installationPath) {
+ // Default to the directory containing the executable (which is where most users will want to put it
+ installationPath = [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent];
+ }
+
+#if !defined(DEDICATED)
+ appName = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"CFBundleName"];
+#else
+ // We are hard coding the app name here since the dedicated server is a tool, not a app bundle and does not have access to the Info.plist that the client app does. Suck.
+ appName = @"Quake3";
+#endif
+ demoAppName = appName;
+
+ while (YES) {
+ NSString *dataPath;
+ NSOpenPanel *openPanel;
+ int result;
+
+ foundDirectory = NO;
+ defaultManager = [NSFileManager defaultManager];
+ //NSLog(@"Candidate installation path = %@", installationPath);
+ dataPath = [installationPath stringByAppendingPathComponent: @"baseq3"];
+
+ if ([defaultManager fileExistsAtPath: dataPath]) {
+ // Check that the data directory contains at least one .pk3 file. We don't know what it will be named, so don't hard code a name (for example it might be named 'french.pk3' for a French release
+ NSArray *files;
+ unsigned int fileIndex;
+
+ files = [defaultManager directoryContentsAtPath: dataPath];
+ fileIndex = [files count];
+ while (fileIndex--) {
+ if ([[files objectAtIndex: fileIndex] hasSuffix: @"pk3"]) {
+ //NSLog(@"Found %@.", [files objectAtIndex: fileIndex]);
+ foundDirectory = YES;
+ break;
+ }
+ }
+ }
+
+ if (foundDirectory)
+ break;
+
+#ifdef DEDICATED
+ break;
+#warning TJW: We are hard coding the app name and default domain here since the dedicated server is a tool, not a app bundle and does not have access to the Info.plist that the client app does. Suck.
+ NSLog(@"Unable to determine installation directory. Please move the executable into the '%@' installation directory or add a '%@' key in the 'Q3DedicatedServer' defaults domain.", appName, installationPathKey, [[NSBundle mainBundle] bundleIdentifier]);
+ Sys_Quit();
+ exit(1);
+#else
+ selectButton = @"Select Retail Installation...";
+
+ result = NSRunAlertPanel(demoAppName, @"You need to select the installation directory for %@ (not any directory inside of it -- the installation directory itself).", selectButton, @"Quit", nil, appName);
+ switch (result) {
+ case NSAlertDefaultReturn:
+ break;
+ default:
+ Sys_Quit();
+ break;
+ }
+
+ openPanel = [NSOpenPanel openPanel];
+ [openPanel setAllowsMultipleSelection:NO];
+ [openPanel setCanChooseDirectories:YES];
+ [openPanel setCanChooseFiles:NO];
+ result = [openPanel runModalForDirectory:nil file:nil];
+ if (result == NSOKButton) {
+ NSArray *filenames;
+
+ filenames = [openPanel filenames];
+ if ([filenames count] == 1) {
+ installationPath = [filenames objectAtIndex:0];
+ [[NSUserDefaults standardUserDefaults] setObject:installationPath forKey:installationPathKey];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ }
+ }
+#endif
+ }
+
+ // Create the application support directory if it doesn't exist already
+ do {
+ NSArray *results;
+ NSString *libraryPath, *homePath, *filePath;
+ NSDictionary *attributes;
+
+ results = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+ if (![results count])
+ break;
+
+ libraryPath = [results objectAtIndex: 0];
+ homePath = [libraryPath stringByAppendingPathComponent: @"Application Support"];
+ homePath = [homePath stringByAppendingPathComponent: appName];
+ filePath = [homePath stringByAppendingPathComponent: @"foo"];
+
+ attributes = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: 0750], NSFilePosixPermissions, nil];
+ NS_DURING {
+ Sys_CreatePathToFile(filePath, attributes);
+ Sys_SetDefaultHomePath([homePath fileSystemRepresentation]);
+ } NS_HANDLER {
+ NSLog(@"Exception: %@", localException);
+#ifndef DEDICATED
+ NSRunAlertPanel(nil, @"Unable to create '%@'. Please make sure that you have permission to write to this folder and re-run the game.", @"OK", nil, nil, homePath);
+#endif
+ Sys_Quit();
+ } NS_ENDHANDLER;
+ } while(0);
+
+ // Provoke the CD scanning code into looking up the CD.
+ Sys_CheckCD();
+
+ // Let the filesystem know where our local install is
+ Sys_SetDefaultInstallPath([installationPath cString]);
+
+ cmdline = NULL;
+#if 0
+ if (GRCheckFileForCmd()) {
+ GRGetWaitingCmd();
+ if (GRHasProperty( 'Exec' )) {
+ NSString *cfgPath, *grCfg;
+ cfgPath = [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent];
+ cfgPath = [cfgPath stringByAppendingPathComponent: [NSString stringWithCString: GRGetPropertyStr( 'Exec' )]];
+ grCfg = [NSString stringWithContentsOfFile: cfgPath];
+ cmdline = malloc(strlen([grCfg cString])+1);
+ [grCfg getCString: cmdline];
+ }
+ }
+#endif
+ if (!cmdline) {
+ // merge the command line, this is kinda silly
+ for (commandLineLength = 1, argumentIndex = 1; argumentIndex < argc; argumentIndex++)
+ commandLineLength += strlen(argv[argumentIndex]) + 1;
+ cmdline = malloc(commandLineLength);
+ *cmdline = '\0';
+ for (argumentIndex = 1; argumentIndex < argc; argumentIndex++) {
+ if (argumentIndex > 1)
+ strcat(cmdline, " ");
+ strcat(cmdline, argv[argumentIndex]);
+ }
+ }
+ Com_Printf("command line: %s\n", cmdline);
+
+ Com_Init(cmdline);
+
+#ifndef DEDICATED
+ [NSApp activateIgnoringOtherApps:YES];
+#endif
+
+ while (1) {
+ Com_Frame();
+
+ if ((count & 15)==0) {
+ // We should think about doing this less frequently than every frame
+ [pool release];
+ pool = [[NSAutoreleasePool alloc] init];
+ }
+ }
+
+ [pool release];
+}
+
+@end
+
+
+
+// Creates any directories needed to be able to create a file at the specified path. Raises an exception on failure.
+static void Sys_CreatePathToFile(NSString *path, NSDictionary *attributes)
+{
+ NSArray *pathComponents;
+ unsigned int dirIndex, dirCount;
+ unsigned int startingIndex;
+ NSFileManager *manager;
+
+ manager = [NSFileManager defaultManager];
+ pathComponents = [path pathComponents];
+ dirCount = [pathComponents count] - 1;
+
+ startingIndex = 0;
+ for (dirIndex = startingIndex; dirIndex < dirCount; dirIndex++) {
+ NSString *partialPath;
+ BOOL fileExists;
+
+ partialPath = [NSString pathWithComponents:[pathComponents subarrayWithRange:NSMakeRange(0, dirIndex + 1)]];
+
+ // Don't use the 'fileExistsAtPath:isDirectory:' version since it doesn't traverse symlinks
+ fileExists = [manager fileExistsAtPath:partialPath];
+ if (!fileExists) {
+ if (![manager createDirectoryAtPath:partialPath attributes:attributes]) {
+ [NSException raise:NSGenericException format:@"Unable to create a directory at path: %@", partialPath];
+ }
+ } else {
+ NSDictionary *attributes;
+
+ attributes = [manager fileAttributesAtPath:partialPath traverseLink:YES];
+ if (![[attributes objectForKey:NSFileType] isEqualToString: NSFileTypeDirectory]) {
+ [NSException raise:NSGenericException format:@"Unable to write to path \"%@\" because \"%@\" is not a directory",
+ path, partialPath];
+ }
+ }
+ }
+}
+
+#ifdef DEDICATED
+void S_ClearSoundBuffer( void ) {
+}
+#endif