/* OSGLUCCO.m Copyright (C) 2012 Paul C. Pratt, SDL by Sam Lantinga and others You can redistribute this file and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. You should have received a copy of the license along with this file; see the file COPYING. This file 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 license for more details. */ /* Operating System GLUe for mac os CoCOa All operating system dependent code for the Mac OS Cocoa should go here. Originally derived from Cocoa port of SDL Library by Sam Lantinga (but little trace of that remains). */ #include "CNFGRAPI.h" #include "SYSDEPNS.h" #include "UTIL/ENDIANAC.h" #include "UI/MYOSGLUE.h" #include "STRCONST.h" /* --- adapting to API/ABI version differences --- */ #ifndef MAC_OS_X_VERSION_10_5 #define MAC_OS_X_VERSION_10_5 1050 #endif #ifndef MAC_OS_X_VERSION_10_6 #define MAC_OS_X_VERSION_10_6 1060 #endif #ifndef WantGraphicsSwitching #define WantGraphicsSwitching 0 #endif #if MAC_OS_X_VERSION_10_5 > MAC_OS_X_VERSION_MAX_ALLOWED typedef unsigned long NSUInteger; typedef long NSInteger; typedef struct __CFError * CFErrorRef; #if WantGraphicsSwitching #define NSOpenGLPFAAllowOfflineRenderers \ (NSOpenGLPixelFormatAttribute)96 #endif #endif #if MAC_OS_X_VERSION_10_6 > MAC_OS_X_VERSION_MAX_ALLOWED @protocol NSWindowDelegate @end @protocol NSApplicationDelegate @end #endif LOCALVAR CFBundleRef AppServBunRef; LOCALVAR bool DidApplicationServicesBun = false; LOCALFUNC bool HaveApplicationServicesBun(void) { if (! DidApplicationServicesBun) { AppServBunRef = CFBundleGetBundleWithIdentifier( CFSTR("com.apple.ApplicationServices")); DidApplicationServicesBun = true; } return (AppServBunRef != NULL); } #if MayFullScreen LOCALVAR CFBundleRef HIToolboxBunRef; LOCALVAR bool DidHIToolboxBunRef = false; LOCALFUNC bool HaveHIToolboxBunRef(void) { if (! DidHIToolboxBunRef) { HIToolboxBunRef = CFBundleGetBundleWithIdentifier( CFSTR("com.apple.HIToolbox")); DidHIToolboxBunRef = true; } return (HIToolboxBunRef != NULL); } #endif #if MayFullScreen /* SetSystemUIModeProcPtr API always not available */ typedef UInt32 SystemUIMode; typedef OptionBits SystemUIOptions; enum { kUIModeNormal = 0, kUIModeAllHidden = 3 }; enum { kUIOptionAutoShowMenuBar = 1 << 0, kUIOptionDisableAppleMenu = 1 << 2, kUIOptionDisableProcessSwitch = 1 << 3, kUIOptionDisableForceQuit = 1 << 4, kUIOptionDisableSessionTerminate = 1 << 5, kUIOptionDisableHide = 1 << 6 }; typedef OSStatus (*SetSystemUIModeProcPtr) (SystemUIMode inMode, SystemUIOptions inOptions); LOCALVAR SetSystemUIModeProcPtr SetSystemUIMode = NULL; LOCALVAR bool DidSetSystemUIMode = false; LOCALFUNC bool HaveSetSystemUIMode(void) { if (! DidSetSystemUIMode) { if (HaveHIToolboxBunRef()) { SetSystemUIMode = (SetSystemUIModeProcPtr) CFBundleGetFunctionPointerForName( HIToolboxBunRef, CFSTR("SetSystemUIMode")); } DidSetSystemUIMode = true; } return (SetSystemUIMode != NULL); } #endif typedef Boolean (*CFURLCopyResourcePropertyForKeyProcPtr) ( CFURLRef url, CFStringRef key, void *propertyValueTypeRefPtr, CFErrorRef *error ); LOCALVAR CFURLCopyResourcePropertyForKeyProcPtr CFURLCopyResourcePropertyForKey = NULL; LOCALVAR bool DidCFURLCopyResourcePropertyForKey = false; LOCALFUNC bool HaveCFURLCopyResourcePropertyForKey(void) { if (! DidCFURLCopyResourcePropertyForKey) { if (HaveApplicationServicesBun()) { CFURLCopyResourcePropertyForKey = (CFURLCopyResourcePropertyForKeyProcPtr) CFBundleGetFunctionPointerForName( AppServBunRef, CFSTR("CFURLCopyResourcePropertyForKey")); } DidCFURLCopyResourcePropertyForKey = true; } return (CFURLCopyResourcePropertyForKey != NULL); } LOCALVAR const CFStringRef *kCFURLIsAliasFileKey = NULL; LOCALVAR bool DidkCFURLIsAliasFileKey = false; LOCALFUNC bool HavekCFURLIsAliasFileKey(void) { if (! DidkCFURLIsAliasFileKey) { if (HaveApplicationServicesBun()) { kCFURLIsAliasFileKey = (const CFStringRef *) CFBundleGetDataPointerForName( AppServBunRef, CFSTR("kCFURLIsAliasFileKey")); } DidkCFURLIsAliasFileKey = true; } return (kCFURLIsAliasFileKey != NULL); } LOCALVAR const CFStringRef *kCFURLIsSymbolicLinkKey = NULL; LOCALVAR bool DidkCFURLIsSymbolicLinkKey = false; LOCALFUNC bool HavekCFURLIsSymbolicLinkKey(void) { if (! DidkCFURLIsSymbolicLinkKey) { if (HaveApplicationServicesBun()) { kCFURLIsSymbolicLinkKey = (const CFStringRef *) CFBundleGetDataPointerForName( AppServBunRef, CFSTR("kCFURLIsSymbolicLinkKey")); } DidkCFURLIsSymbolicLinkKey = true; } return (kCFURLIsSymbolicLinkKey != NULL); } typedef CFDataRef (*CFURLCreateBookmarkDataFromFileProcPtr) ( CFAllocatorRef allocator, CFURLRef fileURL, CFErrorRef *errorRef); LOCALVAR CFURLCreateBookmarkDataFromFileProcPtr CFURLCreateBookmarkDataFromFile = NULL; LOCALVAR bool DidCFURLCreateBookmarkDataFromFile = false; LOCALFUNC bool HaveCFURLCreateBookmarkDataFromFile(void) { if (! DidCFURLCreateBookmarkDataFromFile) { if (HaveApplicationServicesBun()) { CFURLCreateBookmarkDataFromFile = (CFURLCreateBookmarkDataFromFileProcPtr) CFBundleGetFunctionPointerForName(AppServBunRef, CFSTR("CFURLCreateBookmarkDataFromFile")); } DidCFURLCreateBookmarkDataFromFile = true; } return (CFURLCreateBookmarkDataFromFile != NULL); } typedef CFOptionFlags CFURLBookmarkResolutionOptions; typedef CFURLRef (*CFURLCreateByResolvingBookmarkDataProcPtr) ( CFAllocatorRef allocator, CFDataRef bookmark, CFURLBookmarkResolutionOptions options, CFURLRef relativeToURL, CFArrayRef resourcePropertiesToInclude, Boolean* isStale, CFErrorRef* error); LOCALVAR CFURLCreateByResolvingBookmarkDataProcPtr CFURLCreateByResolvingBookmarkData = NULL; LOCALVAR bool DidCFURLCreateByResolvingBookmarkData = false; LOCALFUNC bool HaveCFURLCreateByResolvingBookmarkData(void) { if (! DidCFURLCreateByResolvingBookmarkData) { if (HaveApplicationServicesBun()) { CFURLCreateByResolvingBookmarkData = (CFURLCreateByResolvingBookmarkDataProcPtr) CFBundleGetFunctionPointerForName(AppServBunRef, CFSTR("CFURLCreateByResolvingBookmarkData")); } DidCFURLCreateByResolvingBookmarkData = true; } return (CFURLCreateByResolvingBookmarkData != NULL); } typedef boolean_t (*CGCursorIsVisibleProcPtr)(void); LOCALVAR CGCursorIsVisibleProcPtr CGCursorIsVisible = NULL; LOCALVAR bool DidCGCursorIsVisible = false; LOCALFUNC bool HaveCGCursorIsVisible(void) { if (! DidCGCursorIsVisible) { if (HaveApplicationServicesBun()) { CGCursorIsVisible = (CGCursorIsVisibleProcPtr) CFBundleGetFunctionPointerForName( AppServBunRef, CFSTR("CGCursorIsVisible")); } DidCGCursorIsVisible = true; } return (CGCursorIsVisible != NULL); } /* --- some simple utilities --- */ GLOBALOSGLUPROC MoveBytes(anyp srcPtr, anyp destPtr, int32_t byteCount) { (void) memcpy((char *)destPtr, (char *)srcPtr, byteCount); } /* --- control mode and internationalization --- */ #define NeedCell2UnicodeMap 1 #include "LANG/INTLCHAR.h" /* --- sending debugging info to file --- */ LOCALVAR NSString *myAppName = nil; LOCALVAR NSString *DataPath = nil; #if dbglog_HAVE #define dbglog_ToStdErr 0 #if ! dbglog_ToStdErr LOCALVAR FILE *dbglog_File = NULL; #endif LOCALFUNC bool dbglog_open0(void) { #if dbglog_ToStdErr return true; #else NSString *myLogPath = [DataPath stringByAppendingPathComponent: @"dbglog.txt"]; const char *path = [myLogPath fileSystemRepresentation]; dbglog_File = fopen(path, "w"); return (NULL != dbglog_File); #endif } LOCALPROC dbglog_write0(char *s, uimr L) { #if dbglog_ToStdErr (void) fwrite(s, 1, L, stderr); #else if (NULL != dbglog_File) { (void) fwrite(s, 1, L, dbglog_File); } #endif } LOCALPROC dbglog_close0(void) { #if ! dbglog_ToStdErr if (NULL != dbglog_File) { fclose(dbglog_File); dbglog_File = NULL; } #endif } #endif /* --- information about the environment --- */ #define WantColorTransValid 1 #include "UI/COMOSGLU.h" #define WantKeyboard_RemapMac 1 #include "UTILS/PBUFSTDC.h" #include "UI/CONTROLM.h" /* --- text translation --- */ LOCALPROC UniCharStrFromSubstCStr(int *L, unichar *x, char *s) { int i; int L0; uint8_t ps[ClStrMaxLength]; ClStrFromSubstCStr(&L0, ps, s); for (i = 0; i < L0; ++i) { x[i] = Cell2UnicodeMap[ps[i]]; } *L = L0; } LOCALFUNC NSString * NSStringCreateFromSubstCStr(char *s) { int L; unichar x[ClStrMaxLength]; UniCharStrFromSubstCStr(&L, x, s); return [NSString stringWithCharacters:x length:L]; } #if IncludeSonyNameNew LOCALFUNC bool MacRomanFileNameToNSString(tPbuf i, NSString **r) { uint8_t * p; void *Buffer = PbufDat[i]; uint32_t L = PbufSize[i]; p = (uint8_t *)malloc(L /* + 1 */); if (p != NULL) { NSData *d; uint8_t *p0 = (uint8_t *)Buffer; uint8_t *p1 = (uint8_t *)p; if (L > 0) { uint32_t j = L; do { uint8_t x = *p0++; if (x < 32) { x = '-'; } else if (x >= 128) { } else { switch (x) { case '/': case '<': case '>': case '|': case ':': x = '-'; default: break; } } *p1++ = x; } while (--j > 0); if ('.' == p[0]) { p[0] = '-'; } } #if 0 *p1 = 0; *r = [NSString stringWithCString:(char *)p encoding:NSMacOSRomanStringEncoding]; /* only as of OS X 10.4 */ free(p); #endif d = [[NSData alloc] initWithBytesNoCopy:p length:L]; *r = [[[NSString alloc] initWithData:d encoding:NSMacOSRomanStringEncoding] autorelease]; [d release]; return true; } return false; } #endif #if IncludeSonyGetName || IncludeHostTextClipExchange LOCALFUNC MacErr_t NSStringToRomanPbuf(NSString *string, tPbuf *r) { MacErr_t v = mnvm_miscErr; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; #if 0 const char *s = [s0 cStringUsingEncoding: NSMacOSRomanStringEncoding]; uint32_t L = strlen(s); /* only as of OS X 10.4 */ #endif #if 0 NSData *d0 = [string dataUsingEncoding: NSMacOSRomanStringEncoding]; #endif NSData *d0 = [string dataUsingEncoding: NSMacOSRomanStringEncoding allowLossyConversion: YES]; const void *s = [d0 bytes]; NSUInteger L = [d0 length]; if (NULL == s) { v = mnvm_miscErr; } else { uint8_t * p = (uint8_t *)malloc(L); if (NULL == p) { v = mnvm_miscErr; } else { /* memcpy((char *)p, s, L); */ uint8_t *p0 = (uint8_t *)s; uint8_t *p1 = (uint8_t *)p; int i; for (i = L; --i >= 0; ) { uint8_t v = *p0++; if (10 == v) { v = 13; } *p1++ = v; } v = PbufNewFromPtr(p, L, r); } } [pool release]; return v; } #endif /* --- drives --- */ LOCALFUNC bool FindNamedChildPath(NSString *parentPath, char *ChildName, NSString **childPath) { bool v = false; #if 0 NSString *ss = [NSString stringWithCString:s encoding:NSASCIIStringEncoding]; /* only as of OS X 10.4 */ #endif #if 0 NSData *d = [NSData dataWithBytes: ChildName length: strlen(ChildName)]; NSString *ss = [[[NSString alloc] initWithData:d encoding:NSASCIIStringEncoding] autorelease]; #endif NSString *ss = NSStringCreateFromSubstCStr(ChildName); if (nil != ss) { NSString *r = [parentPath stringByAppendingPathComponent: ss]; if (nil != r) { *childPath = r; v = true; } } return v; } LOCALFUNC NSString *ResolveAlias(NSString *filePath, Boolean *targetIsFolder) { NSString *resolvedPath = nil; CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filePath, kCFURLPOSIXPathStyle, NO); if (url != NULL) { if (HaveCFURLCopyResourcePropertyForKey() && HavekCFURLIsAliasFileKey() && HavekCFURLIsSymbolicLinkKey() && HaveCFURLCreateBookmarkDataFromFile() && HaveCFURLCreateByResolvingBookmarkData()) { BOOL isDir; Boolean isStale; CFBooleanRef is_alias_file = NULL; CFBooleanRef is_symbolic_link = NULL; CFDataRef bookmark = NULL; CFURLRef resolvedurl = NULL; if (CFURLCopyResourcePropertyForKey(url, *kCFURLIsAliasFileKey, &is_alias_file, NULL)) if (CFBooleanGetValue(is_alias_file)) if (CFURLCopyResourcePropertyForKey(url, *kCFURLIsSymbolicLinkKey, &is_symbolic_link, NULL)) if (! CFBooleanGetValue(is_symbolic_link)) if (NULL != (bookmark = CFURLCreateBookmarkDataFromFile( kCFAllocatorDefault, url, NULL))) if (NULL != (resolvedurl = CFURLCreateByResolvingBookmarkData( kCFAllocatorDefault, bookmark, 0 /* CFURLBookmarkResolutionOptions options */, NULL /* relativeToURL */, NULL /* resourcePropertiesToInclude */, &isStale, NULL /* error */))) if (nil != (resolvedPath = (NSString *)CFURLCopyFileSystemPath( resolvedurl, kCFURLPOSIXPathStyle))) { if ([[NSFileManager defaultManager] fileExistsAtPath: resolvedPath isDirectory: &isDir]) { *targetIsFolder = isDir; } else { *targetIsFolder = FALSE; } [resolvedPath autorelease]; } if (NULL != resolvedurl) { CFRelease(resolvedurl); } if (NULL != bookmark) { CFRelease(bookmark); } if (NULL != is_alias_file) { CFRelease(is_alias_file); } if (NULL != is_symbolic_link) { CFRelease(is_symbolic_link); } } else { FSRef fsRef; Boolean wasAliased; if (CFURLGetFSRef(url, &fsRef)) { /* FSResolveAliasFile deprecated in 10.8 */ if ((FSResolveAliasFile(&fsRef, TRUE /*resolveAliasChains*/, targetIsFolder, &wasAliased) == noErr) && wasAliased) { CFURLRef resolvedurl = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsRef); if (resolvedurl != NULL) { resolvedPath = (NSString *)CFURLCopyFileSystemPath( resolvedurl, kCFURLPOSIXPathStyle); [resolvedPath autorelease]; CFRelease(resolvedurl); } } } } CFRelease(url); } return resolvedPath; } LOCALFUNC bool FindNamedChildDirPath(NSString *parentPath, char *ChildName, NSString **childPath) { NSString *r; BOOL isDir; Boolean isDirectory; bool v = false; if (FindNamedChildPath(parentPath, ChildName, &r)) if ([[NSFileManager defaultManager] fileExistsAtPath:r isDirectory: &isDir]) { if (isDir) { *childPath = r; v = true; } else { NSString *RslvPath = ResolveAlias(r, &isDirectory); if (nil != RslvPath) { if (isDirectory) { *childPath = RslvPath; v = true; } } } } return v; } LOCALFUNC bool FindNamedChildFilePath(NSString *parentPath, char *ChildName, NSString **childPath) { NSString *r; BOOL isDir; Boolean isDirectory; bool v = false; if (FindNamedChildPath(parentPath, ChildName, &r)) if ([[NSFileManager defaultManager] fileExistsAtPath:r isDirectory: &isDir]) { if (! isDir) { NSString *RslvPath = ResolveAlias(r, &isDirectory); if (nil != RslvPath) { if (! isDirectory) { *childPath = RslvPath; v = true; } } else { *childPath = r; v = true; } } } return v; } #define NotAfileRef NULL LOCALVAR FILE *Drives[NumDrives]; /* open disk image files */ #if IncludeSonyGetName || IncludeSonyNew LOCALVAR NSString *DriveNames[NumDrives]; #endif LOCALPROC InitDrives(void) { /* This isn't really needed, Drives[i] and DriveNames[i] need not have valid values when not vSonyIsInserted[i]. */ tDrive i; for (i = 0; i < NumDrives; ++i) { Drives[i] = NotAfileRef; #if IncludeSonyGetName || IncludeSonyNew DriveNames[i] = nil; #endif } } GLOBALOSGLUFUNC MacErr_t vSonyTransfer(bool IsWrite, uint8_t * Buffer, tDrive Drive_No, uint32_t Sony_Start, uint32_t Sony_Count, uint32_t *Sony_ActCount) { MacErr_t err = mnvm_miscErr; FILE *refnum = Drives[Drive_No]; uint32_t NewSony_Count = 0; if (0 == fseek(refnum, Sony_Start, SEEK_SET)) { if (IsWrite) { NewSony_Count = fwrite(Buffer, 1, Sony_Count, refnum); } else { NewSony_Count = fread(Buffer, 1, Sony_Count, refnum); } if (NewSony_Count == Sony_Count) { err = mnvm_noErr; } } if (nullpr != Sony_ActCount) { *Sony_ActCount = NewSony_Count; } return err; /*& figure out what really to return &*/ } GLOBALOSGLUFUNC MacErr_t vSonyGetSize(tDrive Drive_No, uint32_t *Sony_Count) { MacErr_t err = mnvm_miscErr; FILE *refnum = Drives[Drive_No]; long v; if (0 == fseek(refnum, 0, SEEK_END)) { v = ftell(refnum); if (v >= 0) { *Sony_Count = v; err = mnvm_noErr; } } return err; /*& figure out what really to return &*/ } #ifndef HaveAdvisoryLocks #define HaveAdvisoryLocks 1 #endif /* What is the difference between fcntl(fd, F_SETLK ... and flock(fd ... ? */ #if HaveAdvisoryLocks LOCALFUNC bool LockFile(FILE *refnum) { bool IsOk = false; #if 0 struct flock fl; int fd = fileno(refnum); fl.l_start = 0; /* starting offset */ fl.l_len = 0; /* len = 0 means until end of file */ /* fl.pid_t l_pid; */ /* lock owner, don't need to set */ fl.l_type = F_WRLCK; /* lock type: read/write, etc. */ fl.l_whence = SEEK_SET; /* type of l_start */ if (-1 == fcntl(fd, F_SETLK, &fl)) { MacMsg(kStrImageInUseTitle, kStrImageInUseMessage, false); } else { IsOk = true; } #else int fd = fileno(refnum); if (-1 == flock(fd, LOCK_EX | LOCK_NB)) { if (EWOULDBLOCK == errno) { /* already locked */ MacMsg(kStrImageInUseTitle, kStrImageInUseMessage, false); } else { /* Failed for other reasons, such as unsupported for this volume. Don't prevent opening. */ IsOk = true; } } else { IsOk = true; } #endif return IsOk; } #endif #if HaveAdvisoryLocks LOCALPROC UnlockFile(FILE *refnum) { #if 0 struct flock fl; int fd = fileno(refnum); fl.l_start = 0; /* starting offset */ fl.l_len = 0; /* len = 0 means until end of file */ /* fl.pid_t l_pid; */ /* lock owner, don't need to set */ fl.l_type = F_UNLCK; /* lock type: read/write, etc. */ fl.l_whence = SEEK_SET; /* type of l_start */ if (-1 == fcntl(fd, F_SETLK, &fl)) { /* an error occurred */ } #else int fd = fileno(refnum); if (-1 == flock(fd, LOCK_UN)) { } #endif } #endif LOCALFUNC MacErr_t vSonyEject0(tDrive Drive_No, bool deleteit) { FILE *refnum = Drives[Drive_No]; DiskEjectedNotify(Drive_No); #if HaveAdvisoryLocks UnlockFile(refnum); #endif fclose(refnum); Drives[Drive_No] = NotAfileRef; /* not really needed */ #if IncludeSonyGetName || IncludeSonyNew { NSString *filePath = DriveNames[Drive_No]; if (NULL != filePath) { if (deleteit) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; const char *s = [filePath fileSystemRepresentation]; remove(s); [pool release]; } [filePath release]; DriveNames[Drive_No] = NULL; /* not really needed */ } } #endif return mnvm_noErr; } GLOBALOSGLUFUNC MacErr_t vSonyEject(tDrive Drive_No) { return vSonyEject0(Drive_No, false); } #if IncludeSonyNew GLOBALOSGLUFUNC MacErr_t vSonyEjectDelete(tDrive Drive_No) { return vSonyEject0(Drive_No, true); } #endif LOCALPROC UnInitDrives(void) { tDrive i; for (i = 0; i < NumDrives; ++i) { if (vSonyIsInserted(i)) { (void) vSonyEject(i); } } } #if IncludeSonyGetName GLOBALOSGLUFUNC MacErr_t vSonyGetName(tDrive Drive_No, tPbuf *r) { MacErr_t v = mnvm_miscErr; NSString *filePath = DriveNames[Drive_No]; if (NULL != filePath) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString *s0 = [filePath lastPathComponent]; v = NSStringToRomanPbuf(s0, r); [pool release]; } return v; } #endif LOCALFUNC bool Sony_Insert0(FILE *refnum, bool locked, NSString *filePath) { tDrive Drive_No; bool IsOk = false; if (! FirstFreeDisk(&Drive_No)) { MacMsg(kStrTooManyImagesTitle, kStrTooManyImagesMessage, false); } else { /* printf("Sony_Insert0 %d\n", (int)Drive_No); */ #if HaveAdvisoryLocks if (locked || LockFile(refnum)) #endif { Drives[Drive_No] = refnum; DiskInsertNotify(Drive_No, locked); #if IncludeSonyGetName || IncludeSonyNew DriveNames[Drive_No] = [filePath retain]; #endif IsOk = true; } } if (! IsOk) { fclose(refnum); } return IsOk; } LOCALFUNC bool Sony_Insert1(NSString *filePath, bool silentfail) { /* const char *drivepath = [filePath UTF8String]; */ const char *drivepath = [filePath fileSystemRepresentation]; bool locked = false; /* printf("Sony_Insert1 %s\n", drivepath); */ FILE *refnum = fopen(drivepath, "rb+"); if (NULL == refnum) { locked = true; refnum = fopen(drivepath, "rb"); } if (NULL == refnum) { if (! silentfail) { MacMsg(kStrOpenFailTitle, kStrOpenFailMessage, false); } } else { return Sony_Insert0(refnum, locked, filePath); } return false; } LOCALFUNC bool Sony_Insert2(char *s) { NSString *sPath; if (! FindNamedChildFilePath(DataPath, s, &sPath)) { return false; } else { return Sony_Insert1(sPath, true); } } LOCALFUNC MacErr_t LoadMacRomPath(NSString *RomPath) { FILE *ROM_File; int File_Size; MacErr_t err = mnvm_fnfErr; const char *path = [RomPath fileSystemRepresentation]; ROM_File = fopen(path, "rb"); if (NULL != ROM_File) { File_Size = fread(ROM, 1, kROM_Size, ROM_File); if (kROM_Size != File_Size) { if (feof(ROM_File)) { MacMsgOverride(kStrShortROMTitle, kStrShortROMMessage); err = mnvm_eofErr; } else { MacMsgOverride(kStrNoReadROMTitle, kStrNoReadROMMessage); err = mnvm_miscErr; } } else { err = ROM_IsValid(); } fclose(ROM_File); } return err; } LOCALFUNC bool Sony_Insert1a(NSString *filePath) { bool v; if (! ROM_loaded) { v = (mnvm_noErr == LoadMacRomPath(filePath)); } else { v = Sony_Insert1(filePath, false); } return v; } LOCALPROC Sony_ResolveInsert(NSString *filePath) { Boolean isDirectory; NSString *RslvPath = ResolveAlias(filePath, &isDirectory); if (nil != RslvPath) { if (! isDirectory) { (void) Sony_Insert1a(RslvPath); } } else { (void) Sony_Insert1a(filePath); } } LOCALFUNC bool Sony_InsertIth(int i) { bool v; if ((i > 9) || ! FirstFreeDisk(nullpr)) { v = false; } else { char s[] = "disk?.dsk"; s[4] = '0' + i; v = Sony_Insert2(s); } return v; } LOCALFUNC bool LoadInitialImages(void) { if (! AnyDiskInserted()) { int i; for (i = 1; Sony_InsertIth(i); ++i) { /* stop on first error (including file not found) */ } } return true; } #if IncludeSonyNew LOCALFUNC bool WriteZero(FILE *refnum, uint32_t L) { #define ZeroBufferSize 2048 uint32_t i; uint8_t buffer[ZeroBufferSize]; memset(&buffer, 0, ZeroBufferSize); while (L > 0) { i = (L > ZeroBufferSize) ? ZeroBufferSize : L; if (fwrite(buffer, 1, i, refnum) != i) { return false; } L -= i; } return true; } #endif #if IncludeSonyNew LOCALPROC MakeNewDisk0(uint32_t L, NSString *sPath) { bool IsOk = false; const char *drivepath = [sPath fileSystemRepresentation]; FILE *refnum = fopen(drivepath, "wb+"); if (NULL == refnum) { MacMsg(kStrOpenFailTitle, kStrOpenFailMessage, false); } else { if (WriteZero(refnum, L)) { IsOk = Sony_Insert0(refnum, false, sPath); refnum = NULL; } if (refnum != NULL) { fclose(refnum); } if (! IsOk) { (void) remove(drivepath); } } } #endif /* --- ROM --- */ LOCALFUNC MacErr_t LoadMacRomFrom(NSString *parentPath) { NSString *RomPath; MacErr_t err = mnvm_fnfErr; if (FindNamedChildFilePath(parentPath, RomFileName, &RomPath)) { err = LoadMacRomPath(RomPath); } return err; } LOCALFUNC MacErr_t LoadMacRomFromAppDir(void) { return LoadMacRomFrom(DataPath); } LOCALFUNC MacErr_t LoadMacRomFromPrefDir(void) { NSString *PrefsPath; NSString *GryphelPath; NSString *RomsPath; MacErr_t err = mnvm_fnfErr; NSArray *paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES); if ((nil != paths) && ([paths count] > 0)) { NSString *LibPath = [paths objectAtIndex:0]; if (FindNamedChildDirPath(LibPath, "Preferences", &PrefsPath)) if (FindNamedChildDirPath(PrefsPath, "Gryphel", &GryphelPath)) if (FindNamedChildDirPath(GryphelPath, "mnvm_rom", &RomsPath)) { err = LoadMacRomFrom(RomsPath); } } return err; } LOCALFUNC MacErr_t LoadMacRomFromGlobalDir(void) { NSString *GryphelPath; NSString *RomsPath; MacErr_t err = mnvm_fnfErr; NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSLocalDomainMask, NO); if ((nil != paths) && ([paths count] > 0)) { NSString *LibPath = [paths objectAtIndex:0]; if (FindNamedChildDirPath(LibPath, "Gryphel", &GryphelPath)) if (FindNamedChildDirPath(GryphelPath, "mnvm_rom", &RomsPath)) { err = LoadMacRomFrom(RomsPath); } } return err; } LOCALFUNC bool LoadMacRom(void) { MacErr_t err; if (mnvm_fnfErr == (err = LoadMacRomFromAppDir())) if (mnvm_fnfErr == (err = LoadMacRomFromPrefDir())) if (mnvm_fnfErr == (err = LoadMacRomFromGlobalDir())) { } return true; /* keep launching Mini vMac, regardless */ } #if IncludeHostTextClipExchange GLOBALOSGLUFUNC MacErr_t HTCEexport(tPbuf i) { void *Buffer; uint32_t L; MacErr_t err = mnvm_miscErr; PbufKillToPtr(&Buffer, &L, i); if (L > 0) { int j; uint8_t *p = (uint8_t *)Buffer; for (j = L; --j >= 0; ) { uint8_t v = *p; if (13 == v) { *p = 10; } ++p; } } { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSData *d = [[NSData alloc] initWithBytesNoCopy: Buffer length: L]; /* NSData *d = [NSData dataWithBytes: Buffer length: L]; */ NSString *ss = [[[NSString alloc] initWithData:d encoding:NSMacOSRomanStringEncoding] autorelease]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSArray *newTypes = [NSArray arrayWithObject: NSStringPboardType]; (void) [pasteboard declareTypes: newTypes owner: nil]; if ([pasteboard setString: ss forType: NSStringPboardType]) { err = mnvm_noErr; } [d release]; [pool release]; } return err; } #endif #if IncludeHostTextClipExchange GLOBALOSGLUFUNC MacErr_t HTCEimport(tPbuf *r) { MacErr_t err = mnvm_miscErr; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSArray *supportedTypes = [NSArray arrayWithObject: NSStringPboardType]; NSString *available = [pasteboard availableTypeFromArray: supportedTypes]; if (nil != available) { NSString *string = [pasteboard stringForType: NSStringPboardType]; if (nil != string) { err = NSStringToRomanPbuf(string, r); } } [pool release]; return err; } #endif #if EmLocalTalk #include "UTIL/BPFILTER.h" #endif #define UseCGContextDrawImage 0 LOCALVAR NSWindow *Window = nil; LOCALVAR NSView *NSview = nil; #if UseCGContextDrawImage LOCALVAR NSGraphicsContext *NSgfxContext = nil; LOCALVAR CGContextRef CGcontext = nil; LOCALVAR void *Pixels = NULL; LOCALVAR uint16_t Pitch; LOCALVAR uint8_t BytesPerPixel; #endif LOCALVAR NSOpenGLContext *NSOpnGLCntxt = nil; LOCALVAR short GLhOffset; LOCALVAR short GLvOffset; /* OpenGL coordinates of upper left point of drawing area */ LOCALPROC HideCursor(void) { [NSCursor hide]; } LOCALPROC ShowCursor(void) { if (nil != Window) { [Window invalidateCursorRectsForView: NSview]; } #if 0 [cursor->nscursor performSelectorOnMainThread: @selector(set) withObject: nil waitUntilDone: NO]; #endif #if 0 [[NSCursor arrowCursor] set]; #endif [NSCursor unhide]; } #if EnableMoveMouse LOCALFUNC CGPoint QZ_PrivateSDLToCG(NSPoint *p) { CGPoint cgp; *p = [NSview convertPoint: *p toView: nil]; p->y = [NSview frame].size.height - p->y; *p = [Window convertBaseToScreen: *p]; cgp.x = p->x; cgp.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - p->y; return cgp; } #endif LOCALPROC QZ_GetMouseLocation(NSPoint *p) { /* incorrect while window is being dragged */ *p = [NSEvent mouseLocation]; /* global coordinates */ if (nil != Window) { *p = [Window convertScreenToBase: *p]; } *p = [NSview convertPoint: *p fromView: nil]; p->y = [NSview frame].size.height - p->y; } /* --- keyboard --- */ LOCALVAR NSUInteger CurrentMods = 0; /* Apple documentation says: "The lower 16 bits of the modifier flags are reserved for device-dependent bits." observed to be: */ #define _NSLShiftKeyMask 0x0002 #define _NSRShiftKeyMask 0x0004 #define _NSLControlKeyMask 0x0001 #define _NSRControlKeyMask 0x2000 #define _NSLCommandKeyMask 0x0008 #define _NSRCommandKeyMask 0x0010 #define _NSLOptionKeyMask 0x0020 #define _NSROptionKeyMask 0x0040 /* Avoid using the above unless it is really needed. */ LOCALPROC UpdateKeyboardModifiers(NSUInteger newMods) { NSUInteger changeMask = CurrentMods ^ newMods; if (0 != changeMask) { if (0 != (changeMask & NSAlphaShiftKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_CapsLock, 0 != (newMods & NSAlphaShiftKeyMask)); } #if MKC_formac_RShift == MKC_formac_Shift if (0 != (changeMask & NSShiftKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Shift, 0 != (newMods & NSShiftKeyMask)); } #else if (0 != (changeMask & _NSLShiftKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Shift, 0 != (newMods & _NSLShiftKeyMask)); } if (0 != (changeMask & _NSRShiftKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_RShift, 0 != (newMods & _NSRShiftKeyMask)); } #endif #if MKC_formac_RControl == MKC_formac_Control if (0 != (changeMask & NSControlKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Control, 0 != (newMods & NSControlKeyMask)); } #else if (0 != (changeMask & _NSLControlKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Control, 0 != (newMods & _NSLControlKeyMask)); } if (0 != (changeMask & _NSRControlKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_RControl, 0 != (newMods & _NSRControlKeyMask)); } #endif #if MKC_formac_RCommand == MKC_formac_Command if (0 != (changeMask & NSCommandKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Command, 0 != (newMods & NSCommandKeyMask)); } #else if (0 != (changeMask & _NSLCommandKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Command, 0 != (newMods & _NSLCommandKeyMask)); } if (0 != (changeMask & _NSRCommandKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_RCommand, 0 != (newMods & _NSRCommandKeyMask)); } #endif #if MKC_formac_ROption == MKC_formac_Option if (0 != (changeMask & NSAlternateKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Option, 0 != (newMods & NSAlternateKeyMask)); } #else if (0 != (changeMask & _NSLOptionKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_Option, 0 != (newMods & _NSLOptionKeyMask)); } if (0 != (changeMask & _NSROptionKeyMask)) { Keyboard_UpdateKeyMap2(MKC_formac_ROption, 0 != (newMods & _NSROptionKeyMask)); } #endif CurrentMods = newMods; } } /* --- mouse --- */ /* cursor hiding */ LOCALVAR bool WantCursorHidden = false; #if MayFullScreen LOCALVAR short hOffset; /* number of pixels to left of drawing area in window */ LOCALVAR short vOffset; /* number of pixels above drawing area in window */ #endif #if MayFullScreen LOCALVAR bool GrabMachine = false; #endif #if 1 LOCALVAR bool UseFullScreen = (0 != WantInitFullScreen); #endif LOCALVAR bool UseMagnify = (0 != WantInitMagnify); LOCALVAR bool gBackgroundFlag = false; LOCALVAR bool CurSpeedStopped = true; #define MaxScale WindowScale LOCALVAR bool HaveCursorHidden = false; LOCALPROC ForceShowCursor(void) { if (HaveCursorHidden) { HaveCursorHidden = false; ShowCursor(); } } /* cursor moving */ #if EnableMoveMouse LOCALFUNC bool MoveMouse(int16_t h, int16_t v) { NSPoint p; CGPoint cgp; #if 1 if (UseFullScreen) #endif #if MayFullScreen { h -= ViewHStart; v -= ViewVStart; } #endif if (UseMagnify) { h *= WindowScale; v *= WindowScale; } #if 1 if (UseFullScreen) #endif #if MayFullScreen { h += hOffset; v += vOffset; } #endif p = NSMakePoint(h, v); cgp = QZ_PrivateSDLToCG(&p); /* this is the magic call that fixes cursor "freezing" after warp */ CGAssociateMouseAndMouseCursorPosition(0); CGWarpMouseCursorPosition(cgp); CGAssociateMouseAndMouseCursorPosition(1); #if 0 if (noErr != CGSetLocalEventsSuppressionInterval(0.0)) { /* don't use MacMsg which can call MoveMouse */ } if (noErr != CGWarpMouseCursorPosition(cgp)) { /* don't use MacMsg which can call MoveMouse */ } #endif return true; } #endif #if EnableFSMouseMotion LOCALPROC AdjustMouseMotionGrab(void) { #if MayFullScreen if (GrabMachine) { /* if magnification changes, need to reset, even if HaveMouseMotion already true */ if (MoveMouse(ViewHStart + (ViewHSize / 2), ViewVStart + (ViewVSize / 2))) { SavedMouseH = ViewHStart + (ViewHSize / 2); SavedMouseV = ViewVStart + (ViewVSize / 2); HaveMouseMotion = true; } } else #endif { if (HaveMouseMotion) { (void) MoveMouse(CurMouseH, CurMouseV); HaveMouseMotion = false; } } } #endif #if EnableFSMouseMotion LOCALPROC MouseConstrain(void) { int16_t shiftdh; int16_t shiftdv; if (SavedMouseH < ViewHStart + (ViewHSize / 4)) { shiftdh = ViewHSize / 2; } else if (SavedMouseH > ViewHStart + ViewHSize - (ViewHSize / 4)) { shiftdh = - ViewHSize / 2; } else { shiftdh = 0; } if (SavedMouseV < ViewVStart + (ViewVSize / 4)) { shiftdv = ViewVSize / 2; } else if (SavedMouseV > ViewVStart + ViewVSize - (ViewVSize / 4)) { shiftdv = - ViewVSize / 2; } else { shiftdv = 0; } if ((shiftdh != 0) || (shiftdv != 0)) { SavedMouseH += shiftdh; SavedMouseV += shiftdv; if (! MoveMouse(SavedMouseH, SavedMouseV)) { HaveMouseMotion = false; } } } #endif /* cursor state */ LOCALPROC MousePositionNotify(int NewMousePosh, int NewMousePosv) { bool ShouldHaveCursorHidden = true; #if 1 if (UseFullScreen) #endif #if MayFullScreen { NewMousePosh -= hOffset; NewMousePosv -= vOffset; } #endif if (UseMagnify) { NewMousePosh /= WindowScale; NewMousePosv /= WindowScale; } #if 1 if (UseFullScreen) #endif #if MayFullScreen { NewMousePosh += ViewHStart; NewMousePosv += ViewVStart; } #endif #if EnableFSMouseMotion if (HaveMouseMotion) { MousePositionSetDelta(NewMousePosh - SavedMouseH, NewMousePosv - SavedMouseV); SavedMouseH = NewMousePosh; SavedMouseV = NewMousePosv; } else #endif { if (NewMousePosh < 0) { NewMousePosh = 0; ShouldHaveCursorHidden = false; } else if (NewMousePosh >= vMacScreenWidth) { NewMousePosh = vMacScreenWidth - 1; ShouldHaveCursorHidden = false; } if (NewMousePosv < 0) { NewMousePosv = 0; ShouldHaveCursorHidden = false; } else if (NewMousePosv >= vMacScreenHeight) { NewMousePosv = vMacScreenHeight - 1; ShouldHaveCursorHidden = false; } #if 1 if (UseFullScreen) #endif #if MayFullScreen { ShouldHaveCursorHidden = true; } #endif /* if (ShouldHaveCursorHidden || CurMouseButton) */ /* for a game like arkanoid, would like mouse to still move even when outside window in one direction */ MousePositionSet(NewMousePosh, NewMousePosv); } WantCursorHidden = ShouldHaveCursorHidden; } LOCALPROC CheckMouseState(void) { /* incorrect while window is being dragged so only call when needed. */ NSPoint p; QZ_GetMouseLocation(&p); MousePositionNotify((int) p.x, (int) p.y); } LOCALVAR bool gTrueBackgroundFlag = false; LOCALVAR uint8_t * ScalingBuff = nullpr; LOCALVAR uint8_t * CLUT_final; #define CLUT_finalsz1 (256 * 8) #define CLUT_finalClrSz (256 << (5 - vMacScreenDepth)) #define CLUT_finalsz ((CLUT_finalClrSz > CLUT_finalsz1) \ ? CLUT_finalClrSz : CLUT_finalsz1) #define ScrnMapr_DoMap UpdateBWLuminanceCopy #define ScrnMapr_Src GetCurDrawBuff() #define ScrnMapr_Dst ScalingBuff #define ScrnMapr_SrcDepth 0 #define ScrnMapr_DstDepth 3 #define ScrnMapr_Map CLUT_final #include "HW/SCREEN/SCRNMAPR.h" #define ScrnMapr_DoMap UpdateMappedColorCopy #define ScrnMapr_Src GetCurDrawBuff() #define ScrnMapr_Dst ScalingBuff #define ScrnMapr_SrcDepth vMacScreenDepth #define ScrnMapr_DstDepth 5 #define ScrnMapr_Map CLUT_final #include "HW/SCREEN/SCRNMAPR.h" #define ScrnTrns_DoTrans UpdateTransColorCopy #define ScrnTrns_Src GetCurDrawBuff() #define ScrnTrns_Dst ScalingBuff #define ScrnTrns_SrcDepth vMacScreenDepth #define ScrnTrns_DstDepth 5 #define ScrnTrns_DstZLo 1 #include "HW/SCREEN/SCRNTRNS.h" LOCALPROC UpdateLuminanceCopy(int16_t top, int16_t left, int16_t bottom, int16_t right) { int i; if (vMacScreenDepth > 0 && UseColorMode) { if (vMacScreenDepth < 4) { if (! ColorTransValid) { int j; int k; uint32_t * p4; p4 = (uint32_t *)CLUT_final; for (i = 0; i < 256; ++i) { for (k = 1 << (3 - vMacScreenDepth); --k >= 0; ) { j = (i >> (k << vMacScreenDepth)) & (CLUT_size - 1); *p4++ = (((long)CLUT_reds[j] & 0xFF00) << 16) | (((long)CLUT_greens[j] & 0xFF00) << 8) | ((long)CLUT_blues[j] & 0xFF00); } } ColorTransValid = true; } UpdateMappedColorCopy(top, left, bottom, right); } else { UpdateTransColorCopy(top, left, bottom, right); } } else { if (! ColorTransValid) { int k; uint8_t * p4 = (uint8_t *)CLUT_final; for (i = 0; i < 256; ++i) { for (k = 8; --k >= 0; ) { *p4++ = ((i >> k) & 0x01) - 1; } } ColorTransValid = true; } UpdateBWLuminanceCopy(top, left, bottom, right); } } LOCALPROC DrawWithOpenGL(uint16_t top, uint16_t left, uint16_t bottom, uint16_t right) { if (nil == NSOpnGLCntxt) { /* oops */ } else { int16_t top2; int16_t left2; #if 1 if (UseFullScreen) #endif #if MayFullScreen { if (top < ViewVStart) { top = ViewVStart; } if (left < ViewHStart) { left = ViewHStart; } if (bottom > ViewVStart + ViewVSize) { bottom = ViewVStart + ViewVSize; } if (right > ViewHStart + ViewHSize) { right = ViewHStart + ViewHSize; } if ((top >= bottom) || (left >= right)) { goto label_exit; } } #endif top2 = top; left2 = left; #if 1 if (UseFullScreen) #endif #if MayFullScreen { left2 -= ViewHStart; top2 -= ViewVStart; } #endif if (UseMagnify) { top2 *= WindowScale; left2 *= WindowScale; } [NSOpnGLCntxt makeCurrentContext]; UpdateLuminanceCopy(top, left, bottom, right); glRasterPos2i(GLhOffset + left2, GLvOffset - top2); if (vMacScreenDepth > 0 && UseColorMode) { glDrawPixels(right - left, bottom - top, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ScalingBuff + (left + top * vMacScreenWidth) * 4 ); } else { glDrawPixels(right - left, bottom - top, GL_LUMINANCE, GL_UNSIGNED_BYTE, ScalingBuff + (left + top * vMacScreenWidth) ); } #if 0 /* a very quick and dirty check of where drawing */ glDrawPixels(right - left, 1, GL_RED, GL_UNSIGNED_BYTE, ScalingBuff + (left + top * vMacScreenWidth) ); glDrawPixels(1, bottom - top, GL_RED, GL_UNSIGNED_BYTE, ScalingBuff + (left + top * vMacScreenWidth) ); #endif glFlush(); } #if MayFullScreen label_exit: ; #endif } #if UseCGContextDrawImage LOCALPROC SDL_UpdateRect(int32_t x, int32_t y, uint32_t w, uint32_t h) { if ([Window isMiniaturized]) { /* Do nothing if miniaturized */ } else { NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; if (ctx != NSgfxContext) { /* uhoh, you might be rendering from another thread... */ [NSGraphicsContext setCurrentContext: NSgfxContext]; ctx = NSgfxContext; } CGContextRef cgc = (CGContextRef) [ctx graphicsPort]; CGContextFlush(CGcontext); CGImageRef image = CGBitmapContextCreateImage( CGcontext); CGRect rectangle = CGRectMake(0, 0, [NSview frame].size.width, [NSview frame].size.height); CGContextDrawImage(cgc, rectangle, image); CGImageRelease(image); CGContextFlush(cgc); } } #endif /* --- time, date, location --- */ #define dbglog_TimeStuff (0 && dbglog_HAVE) LOCALVAR uint32_t TrueEmulatedTime = 0; LOCALVAR NSTimeInterval LatestTime; LOCALVAR NSTimeInterval NextTickChangeTime; #define TickDuration (1.0 / 60.14742) LOCALVAR uint32_t NewMacDateInSeconds; LOCALVAR bool EmulationWasInterrupted = false; LOCALPROC UpdateTrueEmulatedTime(void) { NSTimeInterval TimeDiff; LatestTime = [NSDate timeIntervalSinceReferenceDate]; TimeDiff = LatestTime - NextTickChangeTime; if (TimeDiff >= 0.0) { if (TimeDiff > 16 * TickDuration) { /* emulation interrupted, forget it */ ++TrueEmulatedTime; NextTickChangeTime = LatestTime + TickDuration; EmulationWasInterrupted = true; #if dbglog_TimeStuff dbglog_writelnNum("emulation interrupted", TrueEmulatedTime); #endif } else { do { #if 0 && dbglog_TimeStuff dbglog_writeln("got next tick"); #endif ++TrueEmulatedTime; TimeDiff -= TickDuration; NextTickChangeTime += TickDuration; } while (TimeDiff >= 0.0); } } else if (TimeDiff < (-16 * TickDuration)) { /* clock set back, reset */ #if dbglog_TimeStuff dbglog_writeln("clock set back"); #endif NextTickChangeTime = LatestTime + TickDuration; } } LOCALVAR uint32_t DateDelta; LOCALFUNC bool CheckDateTime(void) { NewMacDateInSeconds = ((uint32_t)LatestTime) + DateDelta; if (CurMacDateInSeconds != NewMacDateInSeconds) { CurMacDateInSeconds = NewMacDateInSeconds; return true; } else { return false; } } LOCALPROC StartUpTimeAdjust(void) { LatestTime = [NSDate timeIntervalSinceReferenceDate]; NextTickChangeTime = LatestTime; } LOCALFUNC bool InitLocationDat(void) { NSTimeZone *Zone = [NSTimeZone localTimeZone]; uint32_t TzOffSet = (uint32_t)[Zone secondsFromGMT]; #if AutoTimeZone BOOL isdst = [Zone isDaylightSavingTime]; #endif DateDelta = TzOffSet - 1233815296; LatestTime = [NSDate timeIntervalSinceReferenceDate]; NewMacDateInSeconds = ((uint32_t)LatestTime) + DateDelta; CurMacDateInSeconds = NewMacDateInSeconds; #if AutoTimeZone CurMacDelta = (TzOffSet & 0x00FFFFFF) | ((isdst ? 0x80 : 0) << 24); #endif return true; } /* --- sound --- */ #if SoundEnabled #define kLn2SoundBuffers 4 /* kSoundBuffers must be a power of two */ #define kSoundBuffers (1 << kLn2SoundBuffers) #define kSoundBuffMask (kSoundBuffers - 1) #define DesiredMinFilledSoundBuffs 3 /* if too big then sound lags behind emulation. if too small then sound will have pauses. */ #define kLnOneBuffLen 9 #define kLnAllBuffLen (kLn2SoundBuffers + kLnOneBuffLen) #define kOneBuffLen (1UL << kLnOneBuffLen) #define kAllBuffLen (1UL << kLnAllBuffLen) #define kLnOneBuffSz (kLnOneBuffLen + kLn2SoundSampSz - 3) #define kLnAllBuffSz (kLnAllBuffLen + kLn2SoundSampSz - 3) #define kOneBuffSz (1UL << kLnOneBuffSz) #define kAllBuffSz (1UL << kLnAllBuffSz) #define kOneBuffMask (kOneBuffLen - 1) #define kAllBuffMask (kAllBuffLen - 1) #define dbhBufferSize (kAllBuffSz + kOneBuffSz) #define dbglog_SoundStuff (0 && dbglog_HAVE) #define dbglog_SoundBuffStats (0 && dbglog_HAVE) LOCALVAR tpSoundSamp TheSoundBuffer = nullpr; static volatile uint16_t ThePlayOffset; static volatile uint16_t TheFillOffset; static volatile uint16_t MinFilledSoundBuffs; #if dbglog_SoundBuffStats LOCALVAR uint16_t MaxFilledSoundBuffs; #endif LOCALVAR uint16_t TheWriteOffset; LOCALPROC Sound_Start0(void) { /* Reset variables */ ThePlayOffset = 0; TheFillOffset = 0; TheWriteOffset = 0; MinFilledSoundBuffs = kSoundBuffers + 1; #if dbglog_SoundBuffStats MaxFilledSoundBuffs = 0; #endif } GLOBALOSGLUFUNC tpSoundSamp Sound_BeginWrite(uint16_t n, uint16_t *actL) { uint16_t ToFillLen = kAllBuffLen - (TheWriteOffset - ThePlayOffset); uint16_t WriteBuffContig = kOneBuffLen - (TheWriteOffset & kOneBuffMask); if (WriteBuffContig < n) { n = WriteBuffContig; } if (ToFillLen < n) { /* overwrite previous buffer */ #if dbglog_SoundStuff dbglog_writeln("sound buffer over flow"); #endif TheWriteOffset -= kOneBuffLen; } *actL = n; return TheSoundBuffer + (TheWriteOffset & kAllBuffMask); } #if 4 == kLn2SoundSampSz LOCALPROC ConvertSoundBlockToNative(tpSoundSamp p) { int i; for (i = kOneBuffLen; --i >= 0; ) { *p++ -= 0x8000; } } #else #define ConvertSoundBlockToNative(p) #endif LOCALPROC Sound_WroteABlock(void) { #if (4 == kLn2SoundSampSz) uint16_t PrevWriteOffset = TheWriteOffset - kOneBuffLen; tpSoundSamp p = TheSoundBuffer + (PrevWriteOffset & kAllBuffMask); #endif #if dbglog_SoundStuff dbglog_writeln("enter Sound_WroteABlock"); #endif ConvertSoundBlockToNative(p); TheFillOffset = TheWriteOffset; #if dbglog_SoundBuffStats { uint16_t ToPlayLen = TheFillOffset - ThePlayOffset; uint16_t ToPlayBuffs = ToPlayLen >> kLnOneBuffLen; if (ToPlayBuffs > MaxFilledSoundBuffs) { MaxFilledSoundBuffs = ToPlayBuffs; } } #endif } LOCALFUNC bool Sound_EndWrite0(uint16_t actL) { bool v; TheWriteOffset += actL; if (0 != (TheWriteOffset & kOneBuffMask)) { v = false; } else { /* just finished a block */ Sound_WroteABlock(); v = true; } return v; } LOCALPROC Sound_SecondNotify0(void) { if (MinFilledSoundBuffs <= kSoundBuffers) { if (MinFilledSoundBuffs > DesiredMinFilledSoundBuffs) { #if dbglog_SoundStuff dbglog_writeln("MinFilledSoundBuffs too high"); #endif NextTickChangeTime += TickDuration; } else if (MinFilledSoundBuffs < DesiredMinFilledSoundBuffs) { #if dbglog_SoundStuff dbglog_writeln("MinFilledSoundBuffs too low"); #endif ++TrueEmulatedTime; } #if dbglog_SoundBuffStats dbglog_writelnNum("MinFilledSoundBuffs", MinFilledSoundBuffs); dbglog_writelnNum("MaxFilledSoundBuffs", MaxFilledSoundBuffs); MaxFilledSoundBuffs = 0; #endif MinFilledSoundBuffs = kSoundBuffers + 1; } } typedef uint16_t trSoundTemp; #define kCenterTempSound 0x8000 #define AudioStepVal 0x0040 #if 3 == kLn2SoundSampSz #define ConvertTempSoundSampleFromNative(v) ((v) << 8) #elif 4 == kLn2SoundSampSz #define ConvertTempSoundSampleFromNative(v) ((v) + kCenterSound) #else #error "unsupported kLn2SoundSampSz" #endif #if 3 == kLn2SoundSampSz #define ConvertTempSoundSampleToNative(v) ((v) >> 8) #elif 4 == kLn2SoundSampSz #define ConvertTempSoundSampleToNative(v) ((v) - kCenterSound) #else #error "unsupported kLn2SoundSampSz" #endif LOCALPROC SoundRampTo(trSoundTemp *last_val, trSoundTemp dst_val, tpSoundSamp *stream, int *len) { trSoundTemp diff; tpSoundSamp p = *stream; int n = *len; trSoundTemp v1 = *last_val; while ((v1 != dst_val) && (0 != n)) { if (v1 > dst_val) { diff = v1 - dst_val; if (diff > AudioStepVal) { v1 -= AudioStepVal; } else { v1 = dst_val; } } else { diff = dst_val - v1; if (diff > AudioStepVal) { v1 += AudioStepVal; } else { v1 = dst_val; } } --n; *p++ = ConvertTempSoundSampleToNative(v1); } *stream = p; *len = n; *last_val = v1; } struct SoundR { tpSoundSamp fTheSoundBuffer; volatile uint16_t (*fPlayOffset); volatile uint16_t (*fFillOffset); volatile uint16_t (*fMinFilledSoundBuffs); volatile trSoundTemp lastv; bool enabled; bool wantplaying; bool HaveStartedPlaying; AudioUnit outputAudioUnit; }; typedef struct SoundR SoundR; LOCALPROC audio_callback(void *udata, void *stream, int len) { uint16_t ToPlayLen; uint16_t FilledSoundBuffs; int i; SoundR *datp = (SoundR *)udata; tpSoundSamp CurSoundBuffer = datp->fTheSoundBuffer; uint16_t CurPlayOffset = *datp->fPlayOffset; trSoundTemp v0 = datp->lastv; trSoundTemp v1 = v0; tpSoundSamp dst = (tpSoundSamp)stream; #if kLn2SoundSampSz > 3 len >>= (kLn2SoundSampSz - 3); #endif #if dbglog_SoundStuff dbglog_writeln("Enter audio_callback"); dbglog_writelnNum("len", len); #endif label_retry: ToPlayLen = *datp->fFillOffset - CurPlayOffset; FilledSoundBuffs = ToPlayLen >> kLnOneBuffLen; if (! datp->wantplaying) { #if dbglog_SoundStuff dbglog_writeln("playing end transistion"); #endif SoundRampTo(&v1, kCenterTempSound, &dst, &len); ToPlayLen = 0; } else if (! datp->HaveStartedPlaying) { #if dbglog_SoundStuff dbglog_writeln("playing start block"); #endif if ((ToPlayLen >> kLnOneBuffLen) < 8) { ToPlayLen = 0; } else { tpSoundSamp p = datp->fTheSoundBuffer + (CurPlayOffset & kAllBuffMask); trSoundTemp v2 = ConvertTempSoundSampleFromNative(*p); #if dbglog_SoundStuff dbglog_writeln("have enough samples to start"); #endif SoundRampTo(&v1, v2, &dst, &len); if (v1 == v2) { #if dbglog_SoundStuff dbglog_writeln("finished start transition"); #endif datp->HaveStartedPlaying = true; } } } if (0 == len) { /* done */ if (FilledSoundBuffs < *datp->fMinFilledSoundBuffs) { *datp->fMinFilledSoundBuffs = FilledSoundBuffs; } } else if (0 == ToPlayLen) { #if dbglog_SoundStuff dbglog_writeln("under run"); #endif for (i = 0; i < len; ++i) { *dst++ = ConvertTempSoundSampleToNative(v1); } *datp->fMinFilledSoundBuffs = 0; } else { uint16_t PlayBuffContig = kAllBuffLen - (CurPlayOffset & kAllBuffMask); tpSoundSamp p = CurSoundBuffer + (CurPlayOffset & kAllBuffMask); if (ToPlayLen > PlayBuffContig) { ToPlayLen = PlayBuffContig; } if (ToPlayLen > len) { ToPlayLen = len; } for (i = 0; i < ToPlayLen; ++i) { *dst++ = *p++; } v1 = ConvertTempSoundSampleFromNative(p[-1]); CurPlayOffset += ToPlayLen; len -= ToPlayLen; *datp->fPlayOffset = CurPlayOffset; goto label_retry; } datp->lastv = v1; } LOCALFUNC OSStatus audioCallback( void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { AudioBuffer *abuf; UInt32 i; UInt32 n = ioData->mNumberBuffers; #if dbglog_SoundStuff dbglog_writeln("Enter audioCallback"); dbglog_writelnNum("mNumberBuffers", n); #endif for (i = 0; i < n; i++) { abuf = &ioData->mBuffers[i]; audio_callback(inRefCon, abuf->mData, abuf->mDataByteSize); } return 0; } LOCALVAR SoundR cur_audio; LOCALPROC ZapAudioVars(void) { memset(&cur_audio, 0, sizeof(SoundR)); } LOCALPROC Sound_Stop(void) { #if dbglog_SoundStuff dbglog_writeln("enter Sound_Stop"); #endif if (cur_audio.wantplaying) { OSStatus result; uint16_t retry_limit = 50; /* half of a second */ cur_audio.wantplaying = false; label_retry: if (kCenterTempSound == cur_audio.lastv) { #if dbglog_SoundStuff dbglog_writeln("reached kCenterTempSound"); #endif /* done */ } else if (0 == --retry_limit) { #if dbglog_SoundStuff dbglog_writeln("retry limit reached"); #endif /* done */ } else { /* give time back, particularly important if got here on a suspend event. */ struct timespec rqt; struct timespec rmt; #if dbglog_SoundStuff dbglog_writeln("busy, so sleep"); #endif rqt.tv_sec = 0; rqt.tv_nsec = 10000000; (void) nanosleep(&rqt, &rmt); goto label_retry; } if (noErr != (result = AudioOutputUnitStop( cur_audio.outputAudioUnit))) { #if dbglog_HAVE dbglog_writeln("AudioOutputUnitStop fails"); #endif } } #if dbglog_SoundStuff dbglog_writeln("leave Sound_Stop"); #endif } LOCALPROC Sound_Start(void) { OSStatus result; if ((! cur_audio.wantplaying) && cur_audio.enabled) { #if dbglog_SoundStuff dbglog_writeln("enter Sound_Start"); #endif Sound_Start0(); cur_audio.lastv = kCenterTempSound; cur_audio.HaveStartedPlaying = false; cur_audio.wantplaying = true; if (noErr != (result = AudioOutputUnitStart( cur_audio.outputAudioUnit))) { #if dbglog_HAVE dbglog_writeln("AudioOutputUnitStart fails"); #endif cur_audio.wantplaying = false; } #if dbglog_SoundStuff dbglog_writeln("leave Sound_Start"); #endif } } LOCALPROC Sound_UnInit(void) { if (cur_audio.enabled) { OSStatus result; struct AURenderCallbackStruct callback; cur_audio.enabled = false; /* Remove the input callback */ callback.inputProc = 0; callback.inputProcRefCon = 0; if (noErr != (result = AudioUnitSetProperty( cur_audio.outputAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)))) { #if dbglog_HAVE dbglog_writeln("AudioUnitSetProperty fails" "(kAudioUnitProperty_SetRenderCallback)"); #endif } if (noErr != (result = CloseComponent( cur_audio.outputAudioUnit))) { #if dbglog_HAVE dbglog_writeln("CloseComponent fails in Sound_UnInit"); #endif } } } #define SOUND_SAMPLERATE 22255 /* = round(7833600 * 2 / 704) */ LOCALFUNC bool Sound_Init(void) { OSStatus result = noErr; Component comp; ComponentDescription desc; struct AURenderCallbackStruct callback; AudioStreamBasicDescription requestedDesc; cur_audio.fTheSoundBuffer = TheSoundBuffer; cur_audio.fPlayOffset = &ThePlayOffset; cur_audio.fFillOffset = &TheFillOffset; cur_audio.fMinFilledSoundBuffs = &MinFilledSoundBuffs; cur_audio.wantplaying = false; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; requestedDesc.mFormatID = kAudioFormatLinearPCM; requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked #if 3 != kLn2SoundSampSz | kLinearPCMFormatFlagIsSignedInteger #endif ; requestedDesc.mChannelsPerFrame = 1; requestedDesc.mSampleRate = SOUND_SAMPLERATE; requestedDesc.mBitsPerChannel = (1 << kLn2SoundSampSz); #if 0 requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; #endif #if 0 requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; #endif requestedDesc.mFramesPerPacket = 1; requestedDesc.mBytesPerFrame = (requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame) >> 3; requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket; callback.inputProc = audioCallback; callback.inputProcRefCon = &cur_audio; if (NULL == (comp = FindNextComponent(NULL, &desc))) { #if dbglog_HAVE dbglog_writeln("Failed to start CoreAudio: " "FindNextComponent returned NULL"); #endif } else if (noErr != (result = OpenAComponent( comp, &cur_audio.outputAudioUnit))) { #if dbglog_HAVE dbglog_writeln("Failed to start CoreAudio: OpenAComponent"); #endif } else if (noErr != (result = AudioUnitInitialize( cur_audio.outputAudioUnit))) { #if dbglog_HAVE dbglog_writeln( "Failed to start CoreAudio: AudioUnitInitialize"); #endif } else if (noErr != (result = AudioUnitSetProperty( cur_audio.outputAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &requestedDesc, sizeof(requestedDesc)))) { #if dbglog_HAVE dbglog_writeln("Failed to start CoreAudio: " "AudioUnitSetProperty(kAudioUnitProperty_StreamFormat)"); #endif } else if (noErr != (result = AudioUnitSetProperty( cur_audio.outputAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)))) { #if dbglog_HAVE dbglog_writeln("Failed to start CoreAudio: " "AudioUnitSetProperty(kAudioUnitProperty_SetInputCallback)" ); #endif } else { cur_audio.enabled = true; Sound_Start(); /* This should be taken care of by LeaveSpeedStopped, but since takes a while to get going properly, start early. */ } return true; /* keep going, even if no sound */ } GLOBALOSGLUPROC Sound_EndWrite(uint16_t actL) { if (Sound_EndWrite0(actL)) { } } LOCALPROC Sound_SecondNotify(void) { if (cur_audio.enabled) { Sound_SecondNotify0(); } } #endif LOCALPROC FinishSubMenu(NSMenu *theMenu, NSMenu *parentMenu, NSString *sTitle) { NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle: sTitle action: nil keyEquivalent: @""]; [menuItem setSubmenu: theMenu]; [parentMenu addItem: menuItem]; [menuItem release]; } LOCALFUNC NSMenu *setApplicationMenu(NSMenu *mainMenu) { NSMenuItem *menuItem; NSString *sAppName = NSStringCreateFromSubstCStr("^p"); /* doesn't matter though, OS X replaces this */ NSString *sAbout = NSStringCreateFromSubstCStr(kStrMenuItemAbout); NSString *sHide = NSStringCreateFromSubstCStr(kStrAppMenuItemHide); NSString *sHideOthers = NSStringCreateFromSubstCStr(kStrAppMenuItemHideOthers); NSString *sShowAll = NSStringCreateFromSubstCStr(kStrAppMenuItemShowAll); NSString *sQuit = NSStringCreateFromSubstCStr(kStrAppMenuItemQuit); NSMenu *appleMenu = [[NSMenu alloc] initWithTitle: sAppName]; /* Add menu items */ menuItem = [appleMenu addItemWithTitle: sAbout action: @selector(performApplicationAbout:) keyEquivalent: @"a"]; [menuItem setKeyEquivalentModifierMask: NSControlKeyMask]; [appleMenu addItem:[NSMenuItem separatorItem]]; [appleMenu addItemWithTitle: sHide action: @selector(hide:) keyEquivalent: @""]; [appleMenu addItemWithTitle: sHideOthers action: @selector(hideOtherApplications:) keyEquivalent: @""]; [appleMenu addItemWithTitle: sShowAll action: @selector(unhideAllApplications:) keyEquivalent: @""]; [appleMenu addItem: [NSMenuItem separatorItem]]; menuItem = [appleMenu addItemWithTitle: sQuit action: @selector(terminate:) keyEquivalent: @"q"]; [menuItem setKeyEquivalentModifierMask: NSControlKeyMask]; FinishSubMenu(appleMenu, mainMenu, sAppName); [appleMenu release]; return appleMenu; } /* Create File menu */ LOCALPROC setupFileMenu(NSMenu *mainMenu) { NSMenu *fileMenu; NSMenuItem *menuItem; NSString *sFile = NSStringCreateFromSubstCStr(kStrMenuFile); NSString *sOpen = NSStringCreateFromSubstCStr(kStrMenuItemOpen ";ll"); fileMenu = [[NSMenu alloc] initWithTitle: sFile]; menuItem = [fileMenu addItemWithTitle: sOpen action: @selector(performFileOpen:) keyEquivalent: @"o"]; [menuItem setKeyEquivalentModifierMask: NSControlKeyMask]; FinishSubMenu(fileMenu, mainMenu, sFile); [fileMenu release]; } /* Create Special menu */ LOCALPROC setupSpecialMenu(NSMenu *mainMenu) { NSMenu *specialMenu; NSString *sSpecial = NSStringCreateFromSubstCStr(kStrMenuSpecial); NSString *sMore = NSStringCreateFromSubstCStr(kStrMenuItemMore ";ll"); specialMenu = [[NSMenu alloc] initWithTitle: sSpecial]; [specialMenu addItemWithTitle: sMore action: @selector(performSpecialMoreCommands:) keyEquivalent: @""]; FinishSubMenu(specialMenu, mainMenu, sSpecial); [specialMenu release]; } LOCALPROC MenuSetup(void) { NSMenu *mainMenu = [[NSMenu alloc] init]; NSMenu *appleMenu = setApplicationMenu(mainMenu); setupFileMenu(mainMenu); setupSpecialMenu(mainMenu); [NSApp setMainMenu: mainMenu]; /* Tell the application object that this is now the application menu, if this unsupported call actually exists. Doesn't seem to be needed anyway, at least in OS X 10.7 */ if([NSApp respondsToSelector:@selector(setAppleMenu:)]) { /* [NSApp setAppleMenu: appleMenu]; */ [NSApp performSelector: @selector(setAppleMenu:) withObject:appleMenu]; } [mainMenu release]; } /* --- video out --- */ #if ! UseCGContextDrawImage LOCALPROC HaveChangedScreenBuff(uint16_t top, uint16_t left, uint16_t bottom, uint16_t right) { if ([NSview lockFocusIfCanDraw]) { DrawWithOpenGL(top, left, bottom, right); [NSview unlockFocus]; } } #else LOCALPROC HaveChangedScreenBuff(uint16_t top, uint16_t left, uint16_t bottom, uint16_t right) { int i; int j; uint8_t *the_data = (uint8_t *)GetCurDrawBuff(); uint8_t *p; uint32_t color; uint32_t black_color = 0; /* SDL_MapRGB(cur_video.format, 0, 0, 0) */ uint32_t white_color = 0; /* SDL_MapRGB(cur_video.format, 255, 255, 255) */ switch (BytesPerPixel) { case 2: /* (1)-5-5-5 RGB */ #if 0 rmask = 0x7C00; gmask = 0x03E0; bmask = 0x001F; #endif break; case 4: #if LittleEndianUnaligned #if 0 rmask = 0x0000FF00; gmask = 0x00FF0000; bmask = 0xFF000000; #endif black_color = 0x000000FF; white_color = 0xFFFFFFFF; #else #if 0 rmask = 0x00FF0000; gmask = 0x0000FF00; bmask = 0x000000FF; #endif black_color = 0xFF000000; white_color = 0xFFFFFFFF; #endif break; } if (UseMagnify) { for (i = top * WindowScale; i < bottom * WindowScale; ++i) { for (j = left * WindowScale; j < right * WindowScale; ++j) { p = the_data + (((i / WindowScale) * vMacScreenWidth + (j / WindowScale)) / 8); if (0 != (*p & (1 << ((~ (j / WindowScale)) & 0x7)))) { color = black_color; } else { color = white_color; } switch (BytesPerPixel) { case 2: { /* Probably 15-bpp or 16-bpp */ uint16_t *bufp; bufp = (uint16_t *)Pixels + i * Pitch / 2 + j; *bufp = color; } break; case 4: { /* Probably 32-bpp */ uint32_t *bufp; bufp = (uint32_t *)Pixels + i * Pitch / 4 + j; *bufp = color; } break; } } } } else { for (i = top; i < bottom; ++i) { for (j = left; j < right; ++j) { p = the_data + ((i * vMacScreenWidth + j) / 8); if (0 != (*p & (1 << ((~ j) & 0x7)))) { color = black_color; } else { color = white_color; } switch (BytesPerPixel) { case 2: { /* Probably 15-bpp or 16-bpp */ uint16_t *bufp; bufp = (uint16_t *)Pixels + i * Pitch / 2 + j; *bufp = color; } break; case 4: { /* Probably 32-bpp */ uint32_t *bufp; bufp = (uint32_t *)Pixels + i * Pitch / 4 + j; *bufp = color; } break; } } } } if (UseMagnify) { SDL_UpdateRect(left * WindowScale, top * WindowScale, (right - left) * WindowScale, (bottom - top) * WindowScale); } else { SDL_UpdateRect(left, top, right - left, bottom - top); } } #endif LOCALPROC DrawChangesAndClear(void) { if (ScreenChangedBottom > ScreenChangedTop) { HaveChangedScreenBuff(ScreenChangedTop, ScreenChangedLeft, ScreenChangedBottom, ScreenChangedRight); ScreenClearChanges(); } } GLOBALOSGLUPROC DoneWithDrawingForTick(void) { #if EnableFSMouseMotion if (HaveMouseMotion) { AutoScrollScreen(); } #endif DrawChangesAndClear(); } /* --- keyboard input --- */ LOCALPROC DisableKeyRepeat(void) { } LOCALPROC RestoreKeyRepeat(void) { } LOCALPROC ReconnectKeyCodes3(void) { } LOCALPROC DisconnectKeyCodes3(void) { DisconnectKeyCodes2(); MouseButtonSet(false); } /* --- basic dialogs --- */ LOCALPROC CheckSavedMacMsg(void) { /* called only on quit, if error saved but not yet reported */ if (nullpr != SavedBriefMsg) { NSString *briefMsg0 = NSStringCreateFromSubstCStr(SavedBriefMsg); NSString *longMsg0 = NSStringCreateFromSubstCStr(SavedLongMsg); NSString *quitMsg0 = NSStringCreateFromSubstCStr(kStrCmdQuit); (void) NSRunAlertPanel(briefMsg0, @"%@", quitMsg0, nil, nil, longMsg0); SavedBriefMsg = nullpr; } } /* --- hide/show menubar --- */ enum { NSApplicationPresentationDefault = 0, NSApplicationPresentationAutoHideDock = (1 << 0), NSApplicationPresentationHideDock = (1 << 1), NSApplicationPresentationAutoHideMenuBar = (1 << 2), NSApplicationPresentationHideMenuBar = (1 << 3), NSApplicationPresentationDisableAppleMenu = (1 << 4), NSApplicationPresentationDisableProcessSwitching = (1 << 5), NSApplicationPresentationDisableForceQuit = (1 << 6), NSApplicationPresentationDisableSessionTermination = (1 << 7), NSApplicationPresentationDisableHideApplication = (1 << 8), NSApplicationPresentationDisableMenuBarTransparency = (1 << 9), NSApplicationPresentationFullScreen = (1 << 10), NSApplicationPresentationAutoHideToolbar = (1 << 11) }; typedef NSUInteger NSApplicationPresentationOptions; @interface NSApplication : NSObject - (void)setPresentationOptions: (NSApplicationPresentationOptions)newOptions; @end #if MayFullScreen LOCALPROC _HideMenuBar(void) { if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) { [((NSApplication *)NSApp) setPresentationOptions: NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar #if GrabKeysFullScreen | NSApplicationPresentationDisableProcessSwitching #if GrabKeysMaxFullScreen /* dangerous !! */ | NSApplicationPresentationDisableForceQuit | NSApplicationPresentationDisableSessionTermination #endif #endif ]; } else if (HaveSetSystemUIMode()) { (void) SetSystemUIMode(kUIModeAllHidden, kUIOptionDisableAppleMenu #if GrabKeysFullScreen | kUIOptionDisableProcessSwitch #if GrabKeysMaxFullScreen /* dangerous !! */ | kUIOptionDisableForceQuit | kUIOptionDisableSessionTerminate #endif #endif ); } else { } } #endif #if MayFullScreen LOCALPROC _ShowMenuBar(void) { if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) { [((NSApplication *)NSApp) setPresentationOptions: NSApplicationPresentationDefault]; } else if (HaveSetSystemUIMode()) { (void) SetSystemUIMode(kUIModeNormal, 0); } else { } } #endif /* --- event handling for main window --- */ LOCALPROC BeginDialog(void) { DisconnectKeyCodes3(); ForceShowCursor(); } LOCALPROC EndDialog(void) { [Window makeKeyWindow]; EmulationWasInterrupted = true; } LOCALPROC InsertADisk0(void) { NSOpenPanel *panel = [NSOpenPanel openPanel]; [panel setAllowsMultipleSelection: YES]; BeginDialog(); if (NSOKButton == [panel runModal]) { int i; NSArray *a = [panel URLs]; int n = [a count]; for (i = 0; i < n; ++i) { NSURL *fileURL = [a objectAtIndex: i]; NSString* filePath = [fileURL path]; (void) Sony_Insert1a(filePath); } } EndDialog(); } /* --- main window creation and disposal --- */ LOCALFUNC bool Screen_Init(void) { #if 0 if (noErr != Gestalt(gestaltSystemVersion, &cur_video.system_version)) { cur_video.system_version = 0; } #endif #if 0 #define CGMainDisplayID CGMainDisplayID CGDirectDisplayID CurMainDisplayID = CGMainDisplayID(); cur_video.width = (uint32_t) CGDisplayPixelsWide(CurMainDisplayID); cur_video.height = (uint32_t) CGDisplayPixelsHigh(CurMainDisplayID); #endif InitKeyCodes(); return true; } #if MayFullScreen LOCALPROC AdjustMachineGrab(void) { #if EnableFSMouseMotion AdjustMouseMotionGrab(); #endif } #endif #if MayFullScreen LOCALPROC UngrabMachine(void) { GrabMachine = false; AdjustMachineGrab(); } #endif LOCALPROC AdjustGLforSize(int h, int v) { [NSOpnGLCntxt makeCurrentContext]; glClearColor (0.0, 0.0, 0.0, 1.0); #if 1 glViewport(0, 0, h, v); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, h, 0, v, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); #endif glColor3f(0.0, 0.0, 0.0); if (UseMagnify) { glPixelZoom(WindowScale, - WindowScale); } else { glPixelZoom(1, -1); } glPixelStorei(GL_UNPACK_ROW_LENGTH, vMacScreenWidth); glClear(GL_COLOR_BUFFER_BIT); [NSOpenGLContext clearCurrentContext]; ScreenChangedAll(); } LOCALVAR bool WantScreensChangedCheck = false; LOCALPROC UpdateOpenGLContext(void) { if (nil != NSOpnGLCntxt) { [NSOpnGLCntxt makeCurrentContext]; [NSOpnGLCntxt update]; } } LOCALPROC CloseOpenGLContext(void) { if (nil != NSOpnGLCntxt) { [NSOpenGLContext clearCurrentContext]; /* Only because DrawWithOpenGL doesn't bother to do this. No one uses the current context without settting it first. */ } } LOCALFUNC bool GetOpnGLCntxt(void) { bool v = false; if (nil == NSOpnGLCntxt) { NSRect NewWinRect = [NSview frame]; NSOpenGLPixelFormat *fmt; #if WantGraphicsSwitching { NSOpenGLPixelFormatAttribute attr0[] = { NSOpenGLPFAAllowOfflineRenderers, 0}; fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr0]; } if (nil != fmt) { /* ok */ } else #endif { NSOpenGLPixelFormatAttribute attr[] = { 0}; fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; if (nil == fmt) { #if dbglog_HAVE dbglog_writeln("Could not create fmt"); #endif goto label_exit; } } NSOpnGLCntxt = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil]; [fmt release]; if (nil == NSOpnGLCntxt) { #if dbglog_HAVE dbglog_writeln("Could not create NSOpnGLCntxt"); #endif goto label_exit; } /* fprintf(stderr, "%s\n", "Got OpenGL context"); */ [NSOpnGLCntxt setView: NSview]; [NSOpnGLCntxt update]; AdjustGLforSize(NewWinRect.size.width, NewWinRect.size.height); if (0 != vMacScreenDepth) { ColorModeWorks = true; } } v = true; label_exit: return v; } typedef NSUInteger (*modifierFlagsProcPtr) (id self, SEL cmd); /* Subclass of NSWindow to fix genie effect and support resize events */ @interface ClassWindow : NSWindow @end @implementation ClassWindow #if MayFullScreen - (BOOL)canBecomeKeyWindow { return #if 1 (! UseFullScreen) ? [super canBecomeKeyWindow] : #endif YES; } #endif #if MayFullScreen - (BOOL)canBecomeMainWindow { return #if 1 (! UseFullScreen) ? [super canBecomeMainWindow] : #endif YES; } #endif #if MayFullScreen - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen { #if 1 if (! UseFullScreen) { return [super constrainFrameRect:frameRect toScreen:screen]; } else #endif { return frameRect; } } #endif - (NSDragOperation)draggingEntered:(id )sender { /* NSPasteboard *pboard = [sender draggingPasteboard]; */ NSDragOperation sourceDragMask = [sender draggingSourceOperationMask]; NSDragOperation v = NSDragOperationNone; if (0 != (sourceDragMask & NSDragOperationGeneric)) { return NSDragOperationGeneric; } return v; } - (void)draggingExited:(id )sender { /* remove hilighting */ } - (BOOL)prepareForDragOperation:(id )sender { return YES; } - (BOOL)performDragOperation:(id )sender { BOOL v = NO; NSPasteboard *pboard = [sender draggingPasteboard]; /* NSDragOperation sourceDragMask = [sender draggingSourceOperationMask]; */ if ([[pboard types] containsObject:NSFilenamesPboardType]) { int i; NSArray *file_names = [pboard propertyListForType: NSFilenamesPboardType]; int n = [file_names count]; for (i = 0; i < n; ++i) { NSString *filePath = [file_names objectAtIndex:i]; Sony_ResolveInsert(filePath); } v = YES; } else if ([[pboard types] containsObject: NSURLPboardType]) { NSURL *fileURL = [NSURL URLFromPasteboard: pboard]; NSString* filePath = [fileURL path]; Sony_ResolveInsert(filePath); v = YES; } if (v && gTrueBackgroundFlag) { { SEL sel = @selector(modifierFlags); if ([NSEvent respondsToSelector:sel]) { modifierFlagsProcPtr imp = (modifierFlagsProcPtr) [NSEvent methodForSelector:sel]; UpdateKeyboardModifiers(imp([NSEvent class], sel)); } } [NSApp activateIgnoringOtherApps: YES]; } return v; } - (void) concludeDragOperation:(id )the_sender { } @end @interface ClassWindowDelegate : NSObject @end @implementation ClassWindowDelegate - (BOOL)windowShouldClose:(id)sender { RequestMacOff = true; return NO; } - (void)windowDidBecomeKey:(NSNotification *)aNotification { gTrueBackgroundFlag = false; } - (void)windowDidResignKey:(NSNotification *)aNotification { gTrueBackgroundFlag = true; } @end @interface ClassView : NSView @end @implementation ClassView - (void)resetCursorRects { [self addCursorRect: [self visibleRect] cursor: [NSCursor arrowCursor]]; } - (BOOL)isOpaque { return YES; } - (void)drawRect:(NSRect)dirtyRect { /* Called upon makeKeyAndOrderFront. Create our OpenGL context here, because can't do so before makeKeyAndOrderFront. And if create after then our content won't be drawn initially, resulting in flicker. */ if (GetOpnGLCntxt()) { DrawWithOpenGL(0, 0, vMacScreenHeight, vMacScreenWidth); } } @end #if UseCGContextDrawImage /* absent in 10.3.9. */ CG_EXTERN CGImageRef CGBitmapContextCreateImage(CGContextRef); #endif LOCALVAR ClassWindowDelegate *WinDelegate = nil; LOCALPROC CloseMainWindow(void) { if (nil != WinDelegate) { [WinDelegate release]; WinDelegate = nil; } if (nil != Window) { [Window close]; Window = nil; } if (nil != NSview) { [NSview release]; NSview = nil; } #if UseCGContextDrawImage if (nil != CGcontext) { CGContextFlush(CGcontext); CGContextRelease(CGcontext); CGcontext = nil; } if (NULL != Pixels) { free(Pixels); Pixels = NULL; } #endif if (nil != NSOpnGLCntxt) { [NSOpnGLCntxt release]; NSOpnGLCntxt = nil; } } LOCALPROC QZ_SetCaption(void) { #if 0 NSString *string = [[NSString alloc] initWithUTF8String: kStrAppName]; #endif [Window setTitle: myAppName /* string */]; #if 0 [string release]; #endif } enum { kMagStateNormal, kMagStateMagnifgy, kNumMagStates }; #define kMagStateAuto kNumMagStates #if MayNotFullScreen LOCALVAR int CurWinIndx; LOCALVAR bool HavePositionWins[kNumMagStates]; LOCALVAR NSPoint WinPositionWins[kNumMagStates]; #endif LOCALVAR NSRect SavedScrnBounds; LOCALFUNC bool CreateMainWindow(void) { #if UseCGContextDrawImage CGColorSpaceRef cgColorspace; #endif unsigned int style; NSRect MainScrnBounds; NSRect AllScrnBounds; NSRect NewWinRect; NSPoint botleftPos; int NewWindowHeight = vMacScreenHeight; int NewWindowWidth = vMacScreenWidth; bool v = false; #if 1 if (UseFullScreen) { _HideMenuBar(); } else { _ShowMenuBar(); } #else #if MayFullScreen _HideMenuBar(); #endif #endif MainScrnBounds = [[NSScreen mainScreen] frame]; SavedScrnBounds = MainScrnBounds; { int i; NSArray *screens = [NSScreen screens]; int n = [screens count]; AllScrnBounds = MainScrnBounds; for (i = 0; i < n; ++i) { AllScrnBounds = NSUnionRect(AllScrnBounds, [[screens objectAtIndex:i] frame]); } } if (UseMagnify) { NewWindowHeight *= WindowScale; NewWindowWidth *= WindowScale; } botleftPos.x = MainScrnBounds.origin.x + floor((MainScrnBounds.size.width - NewWindowWidth) / 2); botleftPos.y = MainScrnBounds.origin.y + floor((MainScrnBounds.size.height - NewWindowHeight) / 2); if (botleftPos.x < MainScrnBounds.origin.x) { botleftPos.x = MainScrnBounds.origin.x; } if (botleftPos.y < MainScrnBounds.origin.y) { botleftPos.y = MainScrnBounds.origin.y; } #if 1 if (UseFullScreen) #endif #if MayFullScreen { ViewHSize = MainScrnBounds.size.width; ViewVSize = MainScrnBounds.size.height; if (UseMagnify) { ViewHSize /= WindowScale; ViewVSize /= WindowScale; } if (ViewHSize >= vMacScreenWidth) { ViewHStart = 0; ViewHSize = vMacScreenWidth; } else { ViewHSize &= ~ 1; } if (ViewVSize >= vMacScreenHeight) { ViewVStart = 0; ViewVSize = vMacScreenHeight; } else { ViewVSize &= ~ 1; } } #endif #if 1 if (UseFullScreen) #endif #if MayFullScreen { NewWinRect = AllScrnBounds; GLhOffset = botleftPos.x - AllScrnBounds.origin.x; GLvOffset = (botleftPos.y - AllScrnBounds.origin.y) + ((NewWindowHeight < MainScrnBounds.size.height) ? NewWindowHeight : MainScrnBounds.size.height); hOffset = GLhOffset; vOffset = AllScrnBounds.size.height - GLvOffset; style = NSBorderlessWindowMask; } #endif #if 1 else #endif #if MayNotFullScreen { int WinIndx; if (UseMagnify) { WinIndx = kMagStateMagnifgy; } else { WinIndx = kMagStateNormal; } if (! HavePositionWins[WinIndx]) { WinPositionWins[WinIndx].x = botleftPos.x; WinPositionWins[WinIndx].y = botleftPos.y; HavePositionWins[WinIndx] = true; NewWinRect = NSMakeRect(botleftPos.x, botleftPos.y, NewWindowWidth, NewWindowHeight); } else { NewWinRect = NSMakeRect(WinPositionWins[WinIndx].x, WinPositionWins[WinIndx].y, NewWindowWidth, NewWindowHeight); } GLhOffset = 0; GLvOffset = NewWindowHeight; style = NSTitledWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask; CurWinIndx = WinIndx; } #endif /* Manually create a window, avoids having a nib file resource */ Window = [[ClassWindow alloc] initWithContentRect: NewWinRect styleMask: style backing: NSBackingStoreBuffered defer: YES]; if (nil == Window) { #if dbglog_HAVE dbglog_writeln("Could not create the Cocoa window"); #endif goto label_exit; } /* [Window setReleasedWhenClosed: YES]; */ /* no need to set current_video as it's the default for NSWindows */ QZ_SetCaption(); [Window setAcceptsMouseMovedEvents: YES]; [Window setViewsNeedDisplay: NO]; [Window registerForDraggedTypes: [NSArray arrayWithObjects: NSURLPboardType, NSFilenamesPboardType, nil]]; WinDelegate = [[ClassWindowDelegate alloc] init]; if (nil == WinDelegate) { #if dbglog_HAVE dbglog_writeln("Could not create NSview"); #endif goto label_exit; } [Window setDelegate: WinDelegate]; NSview = [[ClassView alloc] init]; if (nil == NSview) { #if dbglog_HAVE dbglog_writeln("Could not create NSview"); #endif goto label_exit; } [Window setContentView: NSview]; [Window makeKeyAndOrderFront: nil]; /* just in case drawRect didn't get called */ if (! GetOpnGLCntxt()) { #if dbglog_HAVE dbglog_writeln("Could not GetOpnGLCntxt"); #endif goto label_exit; } #if UseCGContextDrawImage Pitch = 4 * NewWindowWidth; Pixels = malloc(NewWindowHeight * Pitch); cgColorspace = CGColorSpaceCreateDeviceRGB(); CGcontext = CGBitmapContextCreate(Pixels, NewWindowWidth, NewWindowHeight, 8, Pitch, cgColorspace, kCGImageAlphaNoneSkipFirst); CGColorSpaceRelease(cgColorspace); NSgfxContext = [NSGraphicsContext graphicsContextWithWindow: Window]; [NSGraphicsContext setCurrentContext: NSgfxContext]; BytesPerPixel = 4; #endif v = true; label_exit: return v; } #if EnableRecreateW LOCALPROC ZapWState(void) { Window = nil; NSview = nil; WinDelegate = nil; #if UseCGContextDrawImage NSgfxContext = nil; CGcontext = nil; Pixels = NULL; #endif NSOpnGLCntxt = nil; } #endif #if EnableRecreateW struct WState { #if MayFullScreen uint16_t f_ViewHSize; uint16_t f_ViewVSize; uint16_t f_ViewHStart; uint16_t f_ViewVStart; short f_hOffset; short f_vOffset; #endif #if 1 bool f_UseFullScreen; #endif bool f_UseMagnify; #if MayNotFullScreen int f_CurWinIndx; #endif NSWindow *f_Window; NSView *f_NSview; ClassWindowDelegate *f_WinDelegate; #if UseCGContextDrawImage NSGraphicsContext *f_NSgfxContext; CGContextRef f_CGcontext; void *f_Pixels; uint16_t f_Pitch; uint8_t f_BytesPerPixel; #endif NSOpenGLContext *f_NSOpnGLCntxt; short f_GLhOffset; short f_GLvOffset; }; typedef struct WState WState; #endif #if EnableRecreateW LOCALPROC GetWState(WState *r) { #if MayFullScreen r->f_ViewHSize = ViewHSize; r->f_ViewVSize = ViewVSize; r->f_ViewHStart = ViewHStart; r->f_ViewVStart = ViewVStart; r->f_hOffset = hOffset; r->f_vOffset = vOffset; #endif #if 1 r->f_UseFullScreen = UseFullScreen; #endif r->f_UseMagnify = UseMagnify; #if MayNotFullScreen r->f_CurWinIndx = CurWinIndx; #endif r->f_Window = Window; r->f_NSview = NSview; r->f_WinDelegate = WinDelegate; #if UseCGContextDrawImage r->f_NSgfxContext = NSgfxContext; r->f_CGcontext = CGcontext; r->f_Pixels = Pixels; r->f_Pitch = Pitch; r->f_BytesPerPixel = BytesPerPixel; #endif r->f_NSOpnGLCntxt = NSOpnGLCntxt; r->f_GLhOffset = GLhOffset; r->f_GLvOffset = GLvOffset; } #endif #if EnableRecreateW LOCALPROC SetWState(WState *r) { #if MayFullScreen ViewHSize = r->f_ViewHSize; ViewVSize = r->f_ViewVSize; ViewHStart = r->f_ViewHStart; ViewVStart = r->f_ViewVStart; hOffset = r->f_hOffset; vOffset = r->f_vOffset; #endif #if 1 UseFullScreen = r->f_UseFullScreen; #endif UseMagnify = r->f_UseMagnify; #if MayNotFullScreen CurWinIndx = r->f_CurWinIndx; #endif Window = r->f_Window; NSview = r->f_NSview; WinDelegate = r->f_WinDelegate; #if UseCGContextDrawImage NSgfxContext = r->f_NSgfxContext; CGcontext = r->f_CGcontext; Pixels = r->f_Pixels; Pitch = r->f_Pitch; BytesPerPixel = r->f_BytesPerPixel; #endif NSOpnGLCntxt = r->f_NSOpnGLCntxt; GLhOffset = r->f_GLhOffset; GLvOffset = r->f_GLvOffset; } #endif #if EnableRecreateW LOCALPROC ReCreateMainWindow(void) { WState old_state; WState new_state; bool HadCursorHidden = HaveCursorHidden; #if 1 if (! UseFullScreen) #endif #if MayNotFullScreen { /* save old position */ NSRect r = [NSWindow contentRectForFrameRect: [Window frame] styleMask: [Window styleMask]]; WinPositionWins[CurWinIndx] = r.origin; } #endif #if MayFullScreen if (GrabMachine) { GrabMachine = false; UngrabMachine(); } #endif CloseOpenGLContext(); GetWState(&old_state); ZapWState(); UseMagnify = WantMagnify; #if 1 UseFullScreen = WantFullScreen; #endif if (! CreateMainWindow()) { CloseMainWindow(); SetWState(&old_state); #if 1 if (UseFullScreen) { _HideMenuBar(); } else { _ShowMenuBar(); } #endif /* avoid retry */ #if 1 WantFullScreen = UseFullScreen; #endif WantMagnify = UseMagnify; } else { GetWState(&new_state); SetWState(&old_state); CloseMainWindow(); SetWState(&new_state); if (HadCursorHidden) { (void) MoveMouse(CurMouseH, CurMouseV); } } } #endif enum { kWinStateWindowed, kWinStateFullScreen, kNumWinStates }; #if 1 LOCALVAR int WinMagStates[kNumWinStates]; #endif LOCALPROC ZapWinStateVars(void) { #if MayNotFullScreen { int i; for (i = 0; i < kNumMagStates; ++i) { HavePositionWins[i] = false; } } #endif #if 1 { int i; for (i = 0; i < kNumWinStates; ++i) { WinMagStates[i] = kMagStateAuto; } } #endif } #if 1 LOCALPROC ToggleWantFullScreen(void) { WantFullScreen = ! WantFullScreen; { int OldWinState = UseFullScreen ? kWinStateFullScreen : kWinStateWindowed; int OldMagState = UseMagnify ? kMagStateMagnifgy : kMagStateNormal; int NewWinState = WantFullScreen ? kWinStateFullScreen : kWinStateWindowed; int NewMagState = WinMagStates[NewWinState]; WinMagStates[OldWinState] = OldMagState; if (kMagStateAuto != NewMagState) { WantMagnify = (kMagStateMagnifgy == NewMagState); } else { WantMagnify = false; if (WantFullScreen) { NSRect MainScrnBounds = [[NSScreen mainScreen] frame]; if ((MainScrnBounds.size.width >= vMacScreenWidth * WindowScale) && (MainScrnBounds.size.height >= vMacScreenHeight * WindowScale) ) { WantMagnify = true; } } } } } #endif /* --- SavedTasks --- */ LOCALPROC LeaveBackground(void) { ReconnectKeyCodes3(); DisableKeyRepeat(); EmulationWasInterrupted = true; } LOCALPROC EnterBackground(void) { RestoreKeyRepeat(); DisconnectKeyCodes3(); ForceShowCursor(); } LOCALPROC LeaveSpeedStopped(void) { #if SoundEnabled Sound_Start(); #endif StartUpTimeAdjust(); } LOCALPROC EnterSpeedStopped(void) { #if SoundEnabled Sound_Stop(); #endif } #if IncludeSonyNew && ! SaveDialogEnable LOCALFUNC bool FindOrMakeNamedChildDirPath(NSString *parentPath, char *ChildName, NSString **childPath) { NSString *r; BOOL isDir; Boolean isDirectory; NSFileManager *fm = [NSFileManager defaultManager]; bool v = false; if (FindNamedChildPath(parentPath, ChildName, &r)) { if ([fm fileExistsAtPath:r isDirectory: &isDir]) { if (isDir) { *childPath = r; v = true; } else { NSString *RslvPath = ResolveAlias(r, &isDirectory); if (nil != RslvPath) { if (isDirectory) { *childPath = RslvPath; v = true; } } } } else { if ([fm respondsToSelector:@selector( createDirectoryAtPath:withIntermediateDirectories:attributes:error: )]) { if ([fm createDirectoryAtPath:r withIntermediateDirectories:NO attributes:nil error:nil]) { *childPath = r; v = true; } } else if ([fm respondsToSelector: @selector(createDirectoryAtPath:attributes:)]) { if ([fm createDirectoryAtPath:r attributes:nil]) { *childPath = r; v = true; } } else { /* fail */ } } } return v; } #endif @interface NSSavePanel : NSObject - (NSInteger)runModalForDirectory:(NSString *)path file:(NSString *)filename; - (void)setNameFieldStringValue:(NSString *)value; @end #if IncludeSonyNew LOCALPROC MakeNewDisk(uint32_t L, NSString *drivename) { #if SaveDialogEnable NSInteger result = NSCancelButton; NSSavePanel *panel = [NSSavePanel savePanel]; BeginDialog(); if ([panel respondsToSelector:@selector(setNameFieldStringValue:)]) { #if 0 [panel setNameFieldStringValue: drivename]; /* available as of OS X 10.6 */ #endif #if 0 [panel performSelector:@selector(setNameFieldStringValue:) withObject: drivename]; #endif [((NSSavePanel *)panel) setNameFieldStringValue: drivename]; result = [panel runModal]; } else if ([panel respondsToSelector: @selector(runModalForDirectory:file:)]) { #if 0 result = [panel runModalForDirectory: nil file: drivename]; /* compiler warns deprecated. To avoid warning, and to still work if removed from SDK, use NSInvocation. */ #endif #if 0 NSString *sDirName = nil; SEL sel = @selector(runModalForDirectory:file:); NSInvocation* invoc = [NSInvocation invocationWithMethodSignature: [panel methodSignatureForSelector: sel]]; [invoc setTarget:panel]; [invoc setSelector:sel]; [invoc setArgument:&sDirName atIndex:2]; [invoc setArgument:&drivename atIndex:3]; [invoc invoke]; [invoc getReturnValue: &result]; #endif /* an easier way ? seems to work */ result = [((NSSavePanel *)panel) runModalForDirectory: nil file: drivename]; } else { /* fail */ } EndDialog(); if (NSOKButton == result) { NSString* filePath = [[panel URL] path]; MakeNewDisk0(L, filePath); } #else /* SaveDialogEnable */ NSString *sPath; if (FindOrMakeNamedChildDirPath(DataPath, "out", &sPath)) { NSString *filePath = [sPath stringByAppendingPathComponent: drivename]; MakeNewDisk0(L, filePath); } #endif /* SaveDialogEnable */ } #endif #if IncludeSonyNew LOCALPROC MakeNewDiskAtDefault(uint32_t L) { MakeNewDisk(L, @"untitled.dsk"); } #endif LOCALPROC CheckForSavedTasks(void) { if (EvtQNeedRecover) { EvtQNeedRecover = false; /* attempt cleanup, EvtQNeedRecover may get set again */ EvtQTryRecoverFromFull(); } if (RequestMacOff) { RequestMacOff = false; if (AnyDiskInserted()) { MacMsgOverride(kStrQuitWarningTitle, kStrQuitWarningMessage); } else { ForceMacOff = true; } } if (ForceMacOff) { return; } if (gTrueBackgroundFlag != gBackgroundFlag) { gBackgroundFlag = gTrueBackgroundFlag; if (gTrueBackgroundFlag) { EnterBackground(); } else { LeaveBackground(); } } if (EmulationWasInterrupted) { EmulationWasInterrupted = false; if (! gTrueBackgroundFlag) { CheckMouseState(); } } #if EnableFSMouseMotion if (HaveMouseMotion) { MouseConstrain(); } #endif #if 1 if (gTrueBackgroundFlag && WantFullScreen) { ToggleWantFullScreen(); } #endif if (WantScreensChangedCheck) { WantScreensChangedCheck = false; UpdateOpenGLContext(); #if 1 /* triggered on enter full screen for some reason in OS X 10.11. so check against saved rect. */ if ((WantFullScreen) && (! NSEqualRects(SavedScrnBounds, [[NSScreen mainScreen] frame]))) { ToggleWantFullScreen(); } #endif } if (CurSpeedStopped != ( SpeedStopped || (gBackgroundFlag && ! RunInBackground) )) { CurSpeedStopped = ! CurSpeedStopped; if (CurSpeedStopped) { EnterSpeedStopped(); } else { LeaveSpeedStopped(); } } if ((nullpr != SavedBriefMsg) & ! MacMsgDisplayed) { MacMsgDisplayOn(); } #if EnableRecreateW if (0 || (UseMagnify != WantMagnify) #if 1 || (UseFullScreen != WantFullScreen) #endif ) { ReCreateMainWindow(); } #endif #if MayFullScreen if (GrabMachine != ( #if 1 UseFullScreen && #endif ! (gTrueBackgroundFlag || CurSpeedStopped))) { GrabMachine = ! GrabMachine; AdjustMachineGrab(); } #endif #if IncludeSonyNew if (vSonyNewDiskWanted) { #if IncludeSonyNameNew if (vSonyNewDiskName != NotAPbuf) { NSString *sNewDiskName; if (MacRomanFileNameToNSString(vSonyNewDiskName, &sNewDiskName)) { MakeNewDisk(vSonyNewDiskSize, sNewDiskName); } PbufDispose(vSonyNewDiskName); vSonyNewDiskName = NotAPbuf; } else #endif { MakeNewDiskAtDefault(vSonyNewDiskSize); } vSonyNewDiskWanted = false; /* must be done after may have gotten disk */ } #endif if (NeedWholeScreenDraw) { NeedWholeScreenDraw = false; ScreenChangedAll(); } if (! gTrueBackgroundFlag) { if (RequestInsertDisk) { RequestInsertDisk = false; InsertADisk0(); } } #if NeedRequestIthDisk if (0 != RequestIthDisk) { Sony_InsertIth(RequestIthDisk); RequestIthDisk = 0; } #endif if (HaveCursorHidden != ( #if MayNotFullScreen (WantCursorHidden #if 1 || UseFullScreen #endif ) && #endif ! (gTrueBackgroundFlag || CurSpeedStopped))) { HaveCursorHidden = ! HaveCursorHidden; if (HaveCursorHidden) { HideCursor(); } else { ShowCursor(); } } #if 1 /* Check if actual cursor visibility is what it should be. If move mouse to dock then cursor is made visible, but then if move directly to our window, cursor is not hidden again. */ if (HaveCGCursorIsVisible()) { /* but only in OS X 10.3 and later */ /* deprecated in cocoa, but no alternative (?) */ if (CGCursorIsVisible()) { if (HaveCursorHidden) { HideCursor(); if (CGCursorIsVisible()) { /* didn't work, attempt undo so that hide cursor count won't get large */ ShowCursor(); } } } else { if (! HaveCursorHidden) { ShowCursor(); /* don't check if worked, assume can't decrement hide cursor count below 0 */ } } } #endif } /* --- main program flow --- */ GLOBALOSGLUFUNC bool ExtraTimeNotOver(void) { UpdateTrueEmulatedTime(); return TrueEmulatedTime == OnTrueTime; } LOCALPROC ProcessEventModifiers(NSEvent *event) { NSUInteger newMods = [event modifierFlags]; UpdateKeyboardModifiers(newMods); } LOCALPROC ProcessEventLocation(NSEvent *event) { NSPoint p = [event locationInWindow]; NSWindow *w = [event window]; if (w != Window) { if (nil != w) { p = [w convertBaseToScreen: p]; } p = [Window convertScreenToBase: p]; } p = [NSview convertPoint: p fromView: nil]; p.y = [NSview frame].size.height - p.y; MousePositionNotify((int) p.x, (int) p.y); } LOCALPROC ProcessKeyEvent(bool down, NSEvent *event) { uint8_t scancode = [event keyCode]; ProcessEventModifiers(event); Keyboard_UpdateKeyMap2(Keyboard_RemapMac(scancode), down); } LOCALPROC ProcessOneSystemEvent(NSEvent *event) { switch ([event type]) { case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown: /* int button = QZ_OtherMouseButtonToSDL( [event buttonNumber]); */ ProcessEventLocation(event); ProcessEventModifiers(event); if (([event window] == Window) && (! gTrueBackgroundFlag) #if MayNotFullScreen && (WantCursorHidden #if 1 || UseFullScreen #endif ) #endif ) { MouseButtonSet(true); } else { /* doesn't belong to us */ [NSApp sendEvent: event]; } break; case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: /* int button = QZ_OtherMouseButtonToSDL( [event buttonNumber]); */ ProcessEventLocation(event); ProcessEventModifiers(event); if (! MouseButtonState) { /* doesn't belong to us */ [NSApp sendEvent: event]; } else { MouseButtonSet(false); } break; case NSMouseMoved: { ProcessEventLocation(event); ProcessEventModifiers(event); } break; case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged: if (! MouseButtonState) { /* doesn't belong to us ? */ [NSApp sendEvent: event]; } else { ProcessEventLocation(event); ProcessEventModifiers(event); } break; case NSKeyUp: ProcessKeyEvent(false, event); break; case NSKeyDown: ProcessKeyEvent(true, event); break; case NSFlagsChanged: ProcessEventModifiers(event); break; /* case NSScrollWheel: */ /* case NSSystemDefined: */ /* case NSAppKitDefined: */ /* case NSApplicationDefined: */ /* case NSPeriodic: */ /* case NSCursorUpdate: */ default: [NSApp sendEvent: event]; } } GLOBALOSGLUPROC WaitForNextTick(void) { NSDate *TheUntil; int i; NSEvent *event; NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; NSDate *TheDistantFuture = [NSDate distantFuture]; NSDate *TheDistantPast = [NSDate distantPast]; #if 0 NSDate *TheNextTick = [NSDate dateWithTimeIntervalSinceReferenceDate: NextTickChangeTime]; #endif TheUntil = TheDistantPast; label_retry: i = 32; while ((--i >= 0) && (nil != (event = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: TheUntil inMode: NSDefaultRunLoopMode dequeue: YES]))) { ProcessOneSystemEvent(event); TheUntil = TheDistantPast; } CheckForSavedTasks(); if (ForceMacOff) { goto label_exit; } if (CurSpeedStopped) { DoneWithDrawingForTick(); TheUntil = TheDistantFuture; goto label_retry; } if (ExtraTimeNotOver()) { NSTimeInterval inTimeout = NextTickChangeTime - LatestTime; if (inTimeout > 0.0) { struct timespec rqt; struct timespec rmt; rqt.tv_sec = 0; rqt.tv_nsec = inTimeout * 1000000000.0; (void) nanosleep(&rqt, &rmt); } TheUntil = TheDistantPast; goto label_retry; } if (CheckDateTime()) { #if SoundEnabled Sound_SecondNotify(); #endif } OnTrueTime = TrueEmulatedTime; #if dbglog_TimeStuff dbglog_writelnNum("WaitForNextTick, OnTrueTime", OnTrueTime); #endif label_exit: [pool release]; } typedef Boolean (*SecTranslocateIsTranslocatedURL_t)( CFURLRef path, bool *isTranslocated, CFErrorRef * error); typedef CFURLRef (*SecTranslocateCreateOriginalPathForURL_t)( CFURLRef translocatedPath, CFErrorRef * error); LOCALFUNC bool setupWorkingDirectory(void) { NSString *myAppDir; NSString *contentsPath; NSString *dataPath; NSBundle *myBundle = [NSBundle mainBundle]; NSString *myAppPath = [myBundle bundlePath]; #if WantUnTranslocate { bool isTranslocated; void *sec_handle = NULL; SecTranslocateIsTranslocatedURL_t mySecTranslocateIsTranslocatedURL = NULL; CFURLRef url = NULL; SecTranslocateCreateOriginalPathForURL_t mySecTranslocateCreateOriginalPathForURL = NULL; CFURLRef untranslocatedURL = NULL; NSString *realAppPath = NULL; if (NULL == (sec_handle = dlopen( "/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY))) { /* fail */ } else if (NULL == (mySecTranslocateIsTranslocatedURL = dlsym(sec_handle, "SecTranslocateIsTranslocatedURL"))) { /* fail */ } else if (NULL == (url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)myAppPath, kCFURLPOSIXPathStyle, NO))) { /* fail */ } else if (! mySecTranslocateIsTranslocatedURL(url, &isTranslocated, NULL)) { /* fail */ } else if (! isTranslocated) { /* done */ } else if (NULL == (mySecTranslocateCreateOriginalPathForURL = dlsym(sec_handle, "SecTranslocateCreateOriginalPathForURL"))) { /* fail */ } else if (NULL == (untranslocatedURL = mySecTranslocateCreateOriginalPathForURL(url, NULL))) { /* fail */ } else if (NULL == (realAppPath = (NSString *)CFURLCopyFileSystemPath( untranslocatedURL, kCFURLPOSIXPathStyle))) { /* fail */ } else { myAppPath = realAppPath; } if (NULL != realAppPath) { [realAppPath autorelease]; } if (NULL != untranslocatedURL) { CFRelease(untranslocatedURL); } if (NULL != url) { CFRelease(url); } if (NULL != sec_handle) { if (0 != dlclose(sec_handle)) { /* dbglog_writeln("dlclose failed"); */ } } } #endif /* WantUnTranslocate */ myAppDir = [myAppPath stringByDeletingLastPathComponent]; myAppName = [[[myAppPath lastPathComponent] stringByDeletingPathExtension] retain]; DataPath = myAppDir; if (FindNamedChildDirPath(myAppPath, "Contents", &contentsPath)) if (FindNamedChildDirPath(contentsPath, "mnvm_dat", &dataPath)) { DataPath = dataPath; } [DataPath retain]; return true; } @interface ClassApplicationDelegate : NSObject @end @implementation ClassApplicationDelegate - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { (void) Sony_Insert1a(filename); return TRUE; } - (void) applicationDidFinishLaunching: (NSNotification *) note { [NSApp stop: nil]; /* stop immediately */ { /* doesn't actually stop until an event, so make one. (As suggested by Michiel de Hoon in http://www.cocoabuilder.com/ post.) */ NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined location: NSMakePoint(0, 0) modifierFlags: 0 timestamp: 0.0 windowNumber: 0 context: nil subtype: 0 data1: 0 data2: 0]; [NSApp postEvent: event atStart: true]; } } - (void)applicationDidChangeScreenParameters: (NSNotification *)aNotification { WantScreensChangedCheck = true; } - (IBAction)performSpecialMoreCommands:(id)sender { DoMoreCommandsMsg(); } - (IBAction)performFileOpen:(id)sender { RequestInsertDisk = true; } - (IBAction)performApplicationAbout:(id)sender { DoAboutMsg(); } @end LOCALVAR ClassApplicationDelegate *ApplicationDelegate = nil; LOCALFUNC bool InitCocoaStuff(void) { NSApplication *NSApp = [NSApplication sharedApplication]; /* in Xcode 6.2, NSApp isn't the same as NSApp, breaks NSApp setDelegate */ MenuSetup(); ApplicationDelegate = [[ClassApplicationDelegate alloc] init]; [NSApp setDelegate: ApplicationDelegate]; #if 0 [NSApp finishLaunching]; #endif /* If use finishLaunching, after Hide Mini vMac command, activating from Dock doesn't bring our window forward. Using "run" instead fixes this. As suggested by Hugues De Keyzer in http://forums.libsdl.org/ post. SDL 2.0 doesn't use this technique. Was another solution found? */ [NSApp run]; /* our applicationDidFinishLaunching forces immediate halt. */ return true; } LOCALPROC UnInitCocoaStuff(void) { if (nil != ApplicationDelegate) { [ApplicationDelegate release]; } if (nil != myAppName) { [myAppName release]; } if (nil != DataPath) { [DataPath release]; } } /* --- platform independent code can be thought of as going here --- */ #include "PROGMAIN.h" LOCALPROC ZapOSGLUVars(void) { InitDrives(); ZapWinStateVars(); #if SoundEnabled ZapAudioVars(); #endif } LOCALPROC ReserveAllocAll(void) { #if dbglog_HAVE dbglog_ReserveAlloc(); #endif ReserveAllocOneBlock(&ROM, kROM_Size, 5, false); ReserveAllocOneBlock(&screencomparebuff, vMacScreenNumBytes, 5, true); #if UseControlKeys ReserveAllocOneBlock(&CntrlDisplayBuff, vMacScreenNumBytes, 5, false); #endif ReserveAllocOneBlock( &ScalingBuff, vMacScreenNumPixels * (vMacScreenDepth > 0) ? 4 : 1, 5, false ; ReserveAllocOneBlock(&CLUT_final, CLUT_finalsz, 5, false); #if SoundEnabled ReserveAllocOneBlock((uint8_t * *)&TheSoundBuffer, dbhBufferSize, 5, false); #endif EmulationReserveAlloc(); } LOCALFUNC bool AllocMemory(void) { #if 0 /* for testing start up error reporting */ MacMsg(kStrOutOfMemTitle, kStrOutOfMemMessage, true); return false; #else uimr n; bool IsOk = false; ReserveAllocOffset = 0; ReserveAllocBigBlock = nullpr; ReserveAllocAll(); n = ReserveAllocOffset; ReserveAllocBigBlock = (uint8_t *)calloc(1, n); if (NULL == ReserveAllocBigBlock) { MacMsg(kStrOutOfMemTitle, kStrOutOfMemMessage, true); } else { ReserveAllocOffset = 0; ReserveAllocAll(); if (n != ReserveAllocOffset) { /* oops, program error */ } else { IsOk = true; } } return IsOk; #endif } LOCALPROC UnallocMemory(void) { if (nullpr != ReserveAllocBigBlock) { free((char *) ReserveAllocBigBlock); } } LOCALFUNC bool InitOSGLU(void) { bool IsOk = false; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; if (AllocMemory()) if (setupWorkingDirectory()) #if dbglog_HAVE if (dbglog_open()) #endif #if SoundEnabled if (Sound_Init()) /* takes a while to stabilize, do as soon as possible */ #endif if (LoadMacRom()) if (LoadInitialImages()) if (InitCocoaStuff()) /* Can get openFile call backs here for initial files. So must load ROM, disk1.dsk, etc first. */ #if EmLocalTalk if (InitLocalTalk()) #endif if (InitLocationDat()) if (Screen_Init()) if (CreateMainWindow()) if (WaitForRom()) { IsOk = true; } [pool release]; return IsOk; } #if dbglog_HAVE && 0 IMPORTPROC DumpRTC(void); #endif LOCALPROC UnInitOSGLU(void) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; #if dbglog_HAVE && 0 DumpRTC(); #endif if (MacMsgDisplayed) { MacMsgDisplayOff(); } RestoreKeyRepeat(); #if MayFullScreen UngrabMachine(); #endif #if SoundEnabled Sound_Stop(); #endif #if SoundEnabled Sound_UnInit(); #endif #if IncludePbufs UnInitPbufs(); #endif UnInitDrives(); ForceShowCursor(); #if dbglog_HAVE dbglog_close(); #endif CheckSavedMacMsg(); CloseOpenGLContext(); CloseMainWindow(); #if MayFullScreen _ShowMenuBar(); #endif UnInitCocoaStuff(); UnallocMemory(); [pool release]; } int main(int argc, char **argv) { ZapOSGLUVars(); if (InitOSGLU()) { ProgramMain(); } UnInitOSGLU(); return 0; }