macosx_sys.m (14297B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 #import "../client/client.h" 23 #import "macosx_local.h" 24 25 #import "dlfcn.h" 26 #import "Q3Controller.h" 27 28 #import <AppKit/AppKit.h> 29 #import <IOKit/IOKitLib.h> 30 #import <IOKit/IOBSD.h> 31 #import <IOKit/storage/IOCDMedia.h> 32 #import <mach/mach_error.h> 33 34 #import <sys/types.h> 35 #import <unistd.h> 36 #import <sys/param.h> 37 #import <sys/mount.h> 38 #import <sys/sysctl.h> 39 40 #ifdef OMNI_TIMER 41 #import "macosx_timers.h" 42 #endif 43 44 qboolean stdin_active = qfalse; 45 46 //=========================================================================== 47 48 int main(int argc, const char *argv[]) { 49 #ifdef DEDICATED 50 Q3Controller *controller; 51 52 stdin_active = qtrue; 53 controller = [[Q3Controller alloc] init]; 54 [controller quakeMain]; 55 return 0; 56 #else 57 return NSApplicationMain(argc, argv); 58 #endif 59 } 60 61 //=========================================================================== 62 63 /* 64 ================= 65 Sys_UnloadDll 66 67 ================= 68 */ 69 void Sys_UnloadDll( void *dllHandle ) { 70 if ( !dllHandle ) { 71 return; 72 } 73 dlclose( dllHandle ); 74 } 75 76 /* 77 ================= 78 Sys_LoadDll 79 80 Used to load a development dll instead of a virtual machine 81 ================= 82 */ 83 extern char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); 84 85 void * QDECL Sys_LoadDll( const char *name, char *fqpath , int (QDECL **entryPoint)(int, ...), 86 int (QDECL *systemcalls)(int, ...) ) { 87 void *libHandle; 88 void (*dllEntry)( int (*syscallptr)(int, ...) ); 89 NSString *bundlePath, *libraryPath; 90 const char *path; 91 92 // TTimo 93 // I don't understand the search strategy here. How can the Quake3 bundle know about the location 94 // of the other bundles? is that configured somewhere in XCode? 95 /* 96 bundlePath = [[NSBundle mainBundle] pathForResource: [NSString stringWithCString: name] ofType: @"bundle"]; 97 libraryPath = [NSString stringWithFormat: @"%@/Contents/MacOS/%s", bundlePath, name]; 98 */ 99 libraryPath = [NSString stringWithFormat: @"%s.bundle/Contents/MacOS/%s", name, name]; 100 if (!libraryPath) 101 return NULL; 102 103 path = [libraryPath cString]; 104 Com_Printf("Loading '%s'.\n", path); 105 libHandle = dlopen( [libraryPath cString], RTLD_LAZY ); 106 if (!libHandle) { 107 libHandle = dlopen( name, RTLD_LAZY ); 108 if (!libHandle) { 109 Com_Printf("Error loading dll: %s\n", dlerror()); 110 return NULL; 111 } 112 } 113 114 dllEntry = dlsym( libHandle, "_dllEntry" ); 115 if (!dllEntry) { 116 Com_Printf("Error loading dll: No dllEntry symbol.\n"); 117 dlclose(libHandle); 118 return NULL; 119 } 120 121 *entryPoint = dlsym( libHandle, "_vmMain" ); 122 if (!*entryPoint) { 123 Com_Printf("Error loading dll: No vmMain symbol.\n"); 124 dlclose(libHandle); 125 return NULL; 126 } 127 128 dllEntry(systemcalls); 129 return libHandle; 130 } 131 132 //=========================================================================== 133 134 char *Sys_GetClipboardData(void) // FIXME 135 { 136 NSPasteboard *pasteboard; 137 NSArray *pasteboardTypes; 138 139 pasteboard = [NSPasteboard generalPasteboard]; 140 pasteboardTypes = [pasteboard types]; 141 if ([pasteboardTypes containsObject:NSStringPboardType]) { 142 NSString *clipboardString; 143 144 clipboardString = [pasteboard stringForType:NSStringPboardType]; 145 if (clipboardString && [clipboardString length] > 0) { 146 return strdup([clipboardString cString]); 147 } 148 } 149 return NULL; 150 } 151 152 char *Sys_GetWholeClipboard ( void ) 153 { 154 return NULL; 155 } 156 157 void Sys_SetClipboard (const char *contents) 158 { 159 } 160 161 162 /* 163 ================== 164 Sys_FunctionCheckSum 165 ================== 166 */ 167 int Sys_FunctionCheckSum(void *f1) { 168 return 0; 169 } 170 171 /* 172 ================== 173 Sys_MonkeyShouldBeSpanked 174 ================== 175 */ 176 int Sys_MonkeyShouldBeSpanked( void ) { 177 return 0; 178 } 179 180 //=========================================================================== 181 182 void Sys_BeginProfiling(void) 183 { 184 } 185 186 void Sys_EndProfiling(void) 187 { 188 } 189 190 //=========================================================================== 191 192 /* 193 ================ 194 Sys_Init 195 196 The cvar and file system has been setup, so configurations are loaded 197 ================ 198 */ 199 void Sys_Init(void) 200 { 201 #ifdef OMNI_TIMER 202 InitializeTimers(); 203 OTStackPushRoot(rootNode); 204 #endif 205 206 NET_Init(); 207 Sys_InitInput(); 208 } 209 210 /* 211 ================= 212 Sys_Shutdown 213 ================= 214 */ 215 void Sys_Shutdown(void) 216 { 217 Com_Printf( "----- Sys_Shutdown -----\n" ); 218 Sys_EndProfiling(); 219 Sys_ShutdownInput(); 220 Com_Printf( "------------------------\n" ); 221 } 222 223 void Sys_Error(const char *error, ...) 224 { 225 va_list argptr; 226 NSString *formattedString; 227 228 Sys_Shutdown(); 229 230 va_start(argptr,error); 231 formattedString = [[NSString alloc] initWithFormat:[NSString stringWithCString:error] arguments:argptr]; 232 va_end(argptr); 233 234 NSLog(@"Sys_Error: %@", formattedString); 235 NSRunAlertPanel(@"Quake 3 Error", formattedString, nil, nil, nil); 236 237 Sys_Quit(); 238 } 239 240 void Sys_Quit(void) 241 { 242 Sys_Shutdown(); 243 [NSApp terminate:nil]; 244 } 245 246 /* 247 ================ 248 Sys_Print 249 250 This is called for all console output, even if the game is running 251 full screen and the dedicated console window is hidden. 252 ================ 253 */ 254 255 char *ansiColors[8] = 256 { "\033[30m" , /* ANSI Black */ 257 "\033[31m" , /* ANSI Red */ 258 "\033[32m" , /* ANSI Green */ 259 "\033[33m" , /* ANSI Yellow */ 260 "\033[34m" , /* ANSI Blue */ 261 "\033[36m" , /* ANSI Cyan */ 262 "\033[35m" , /* ANSI Magenta */ 263 "\033[37m" }; /* ANSI White */ 264 265 void Sys_Print(const char *text) 266 { 267 #if 0 268 /* Okay, this is a stupid hack, but what the hell, I was bored. ;) */ 269 const char *scan = text; 270 int index; 271 272 /* Make sure terminal mode is reset at the start of the line... */ 273 fputs("\033[0m", stdout); 274 275 while(*scan) { 276 /* See if we have a color control code. If so, snarf the character, 277 print what we have so far, print the ANSI Terminal color code, 278 skip over the color control code and continue */ 279 if(Q_IsColorString(scan)) { 280 index = ColorIndex(scan[1]); 281 282 /* Flush current message */ 283 if(scan != text) { 284 fwrite(text, scan - text, 1, stdout); 285 } 286 287 /* Write ANSI color code */ 288 fputs(ansiColors[index], stdout); 289 290 /* Reset search */ 291 text = scan+2; 292 scan = text; 293 continue; 294 } 295 scan++; 296 } 297 298 /* Flush whatever's left */ 299 fputs(text, stdout); 300 301 /* Make sure terminal mode is reset at the end of the line too... */ 302 fputs("\033[0m", stdout); 303 304 #else 305 fputs(text, stdout); 306 #endif 307 } 308 309 310 311 /* 312 ================ 313 Sys_CheckCD 314 315 Return true if the proper CD is in the drive 316 ================ 317 */ 318 319 qboolean Sys_ObjectIsCDRomDevice(io_object_t object) 320 { 321 CFStringRef value; 322 kern_return_t krc; 323 CFDictionaryRef properties; 324 qboolean isCDROM = qfalse; 325 io_iterator_t parentIterator; 326 io_object_t parent; 327 328 krc = IORegistryEntryCreateCFProperties(object, &properties, kCFAllocatorDefault, (IOOptionBits)0); 329 if (krc != KERN_SUCCESS) { 330 fprintf(stderr, "IORegistryEntryCreateCFProperties returned 0x%08x -- %s\n", krc, mach_error_string(krc)); 331 return qfalse; 332 } 333 334 //NSLog(@"properties = %@", properties); 335 336 // See if this is a CD-ROM 337 value = CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTypeKey)); 338 if (value && CFStringCompare(value, CFSTR("CD-ROM"), 0) == kCFCompareEqualTo) 339 isCDROM = qtrue; 340 CFRelease(properties); 341 342 // If it isn't check each of its parents. It seems that the parent enumerator only returns the immediate parent. Maybe the plural indicates that an object can have multiple direct parents. So, we'll call ourselves recursively for each parent. 343 if (!isCDROM) { 344 krc = IORegistryEntryGetParentIterator(object, kIOServicePlane, &parentIterator); 345 if (krc != KERN_SUCCESS) { 346 fprintf(stderr, "IOServiceGetMatchingServices returned 0x%08x -- %s\n", 347 krc, mach_error_string(krc)); 348 } else { 349 while (!isCDROM && (parent = IOIteratorNext(parentIterator))) { 350 if (Sys_ObjectIsCDRomDevice(parent)) 351 isCDROM = qtrue; 352 IOObjectRelease(parent); 353 } 354 355 IOObjectRelease(parentIterator); 356 } 357 } 358 359 //NSLog(@"Sys_ObjectIsCDRomDevice -> %d", isCDROM); 360 return isCDROM; 361 } 362 363 qboolean Sys_IsCDROMDevice(const char *deviceName) 364 { 365 kern_return_t krc; 366 io_iterator_t deviceIterator; 367 mach_port_t masterPort; 368 io_object_t object; 369 qboolean isCDROM = qfalse; 370 371 krc = IOMasterPort(bootstrap_port, &masterPort); 372 if (krc != KERN_SUCCESS) { 373 fprintf(stderr, "IOMasterPort returned 0x%08x -- %s\n", krc, mach_error_string(krc)); 374 return qfalse; 375 } 376 377 // Get an iterator for this BSD device. If it is a CD, it will likely only be one partition of the larger CD-ROM device. 378 krc = IOServiceGetMatchingServices(masterPort, 379 IOBSDNameMatching(masterPort, 0, deviceName), 380 &deviceIterator); 381 if (krc != KERN_SUCCESS) { 382 fprintf(stderr, "IOServiceGetMatchingServices returned 0x%08x -- %s\n", 383 krc, mach_error_string(krc)); 384 return qfalse; 385 } 386 387 while (!isCDROM && (object = IOIteratorNext(deviceIterator))) { 388 if (Sys_ObjectIsCDRomDevice(object)) { 389 isCDROM = qtrue; 390 } 391 IOObjectRelease(object); 392 } 393 394 IOObjectRelease(deviceIterator); 395 396 //NSLog(@"Sys_IsCDROMDevice -> %d", isCDROM); 397 return isCDROM; 398 } 399 400 qboolean Sys_CheckCD( void ) 401 { 402 // DO NOT just return success here if we have a library directory. 403 // Actually look for the CD. 404 405 // We'll look through the actual mount points rather than just looking 406 // for a particular directory since (a) the mount point may change 407 // between OS version (/foo in Public Beta, /Volumes/foo after Public Beta) 408 // and (b) this way someone can't just create a directory and warez the files. 409 410 unsigned int mountCount; 411 struct statfs *mounts; 412 413 mountCount = getmntinfo(&mounts, MNT_NOWAIT); 414 if (mountCount <= 0) { 415 perror("getmntinfo"); 416 #if 1 // Q3:TA doesn't need a CD, but we still need to locate it to allow for partial installs 417 return qtrue; 418 #else 419 return qfalse; 420 #endif 421 } 422 423 while (mountCount--) { 424 const char *lastComponent; 425 426 if ((mounts[mountCount].f_flags & MNT_RDONLY) != MNT_RDONLY) { 427 // Should have been a read only CD... this isn't it 428 continue; 429 } 430 431 if ((mounts[mountCount].f_flags & MNT_LOCAL) != MNT_LOCAL) { 432 // Should have been a local filesystem 433 continue; 434 } 435 436 lastComponent = strrchr(mounts[mountCount].f_mntonname, '/'); 437 if (!lastComponent) { 438 // No slash in the mount point! How is that possible? 439 continue; 440 } 441 442 // Skip the slash and look for the game name 443 lastComponent++; 444 if ((strcasecmp(lastComponent, "Quake3") != 0)) { 445 continue; 446 } 447 448 449 #if 0 450 fprintf(stderr, "f_bsize: %d\n", mounts[mountCount].f_bsize); 451 fprintf(stderr, "f_blocks: %d\n", mounts[mountCount].f_blocks); 452 fprintf(stderr, "type: %d\n", mounts[mountCount].f_type); 453 fprintf(stderr, "flags: %d\n", mounts[mountCount].f_flags); 454 fprintf(stderr, "fstype: %s\n", mounts[mountCount].f_fstypename); 455 fprintf(stderr, "f_mntonname: %s\n", mounts[mountCount].f_mntonname); 456 fprintf(stderr, "f_mntfromname: %s\n", mounts[mountCount].f_mntfromname); 457 fprintf(stderr, "\n\n"); 458 #endif 459 460 lastComponent = strrchr(mounts[mountCount].f_mntfromname, '/'); 461 if (!lastComponent) { 462 // No slash in the device name! How is that possible? 463 continue; 464 } 465 lastComponent++; 466 if (!Sys_IsCDROMDevice(lastComponent)) 467 continue; 468 469 // This looks good 470 Sys_SetDefaultCDPath(mounts[mountCount].f_mntonname); 471 return qtrue; 472 } 473 474 #if 1 // Q3:TA doesn't need a CD, but we still need to locate it to allow for partial installs 475 return qtrue; 476 #else 477 return qfalse; 478 #endif 479 } 480 481 482 //=================================================================== 483 484 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) { 485 } 486 487 void Sys_EndStreamedFile( fileHandle_t f ) { 488 } 489 490 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) { 491 return FS_Read( buffer, size * count, f ); 492 } 493 494 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { 495 FS_Seek( f, offset, origin ); 496 } 497 498 499 void OutputDebugString(char * s) 500 { 501 #ifdef DEBUG 502 fprintf(stderr, "%s", s); 503 #endif 504 } 505 506 /* 507 ================== 508 Sys_LowPhysicalMemory() 509 ================== 510 */ 511 #define MEM_THRESHOLD 96*1024*1024 512 513 qboolean Sys_LowPhysicalMemory() 514 { 515 return NSRealMemoryAvailable() <= MEM_THRESHOLD; 516 } 517 518 static unsigned int _Sys_ProcessorCount = 0; 519 520 unsigned int Sys_ProcessorCount() 521 { 522 if (!_Sys_ProcessorCount) { 523 int name[] = {CTL_HW, HW_NCPU}; 524 size_t size; 525 526 size = sizeof(_Sys_ProcessorCount); 527 if (sysctl(name, 2, &_Sys_ProcessorCount, &size, NULL, 0) < 0) { 528 perror("sysctl"); 529 _Sys_ProcessorCount = 1; 530 } else { 531 Com_Printf("System processor count is %d\n", _Sys_ProcessorCount); 532 } 533 } 534 535 return _Sys_ProcessorCount; 536 } 537