// 20 april 2019 #import #import "uipriv_darwin.h" @interface uiprivApplication : NSApplication @end @implementation uiprivApplication @end @interface uiprivApplicationDelegate : NSObject @end static uiprivApplication *uiprivApp; static uiprivApplicationDelegate *uiprivAppDelegate; @implementation uiprivApplicationDelegate #if 0 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app { // for debugging NSLog(@"in applicationShouldTerminate:"); if (uiprivShouldQuit()) { canQuit = YES; // this will call terminate:, which is the same as uiQuit() return NSTerminateNow; } return NSTerminateCancel; } #endif - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app { return NO; } @end #define errNSAppAlreadyInitialized "NSApp is not of type uiprivApplication; was likely already initialized beforehand" static const char *initErrors[] = { errNSAppAlreadyInitialized, NULL, }; const char **uiprivSysInitErrors(void) { return initErrors; } int uiprivSysInit(void *options, uiInitError *err) { uiprivApp = [uiprivApplication sharedApplication]; if (![NSApp isKindOfClass:[uiprivApplication class]]) return uiprivInitReturnError(err, errNSAppAlreadyInitialized); // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! // see https://github.com/andlabs/ui/issues/6 [uiprivApp setActivationPolicy:NSApplicationActivationPolicyRegular]; uiprivAppDelegate = [uiprivApplicationDelegate new]; [uiprivApp setDelegate:uiprivAppDelegate]; return 1; } void uiMain(void) { [uiprivApp run]; } void uiQuit(void) { @autoreleasepool { NSEvent *e; [uiprivApp stop:uiprivApp]; // stop: won't register until another event has passed; let's synthesize one // TODO instead of using NSApplicationDefined, create a private event type for libui internal use only e = [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:[[NSProcessInfo processInfo] systemUptime] windowNumber:0 context:[NSGraphicsContext currentContext] subtype:0 data1:0 data2:0]; [uiprivApp postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO) } } // thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this void uiQueueMain(void (*f)(void *data), void *data) { // dispatch_get_main_queue() is a serial queue so it will not execute multiple uiQueueMain() functions concurrently // the signature of f matches dispatch_function_t dispatch_async_f(dispatch_get_main_queue(), data, f); } // Debugger() was deprecated in macOS 10.8 (as part of the larger CarbonCore deprecation), but they did not provide a replacement. // Though some people say inline asm, I'd rather make this work automatically everywhere. // Others say raise(SIGTRAP) and others still say __builtin_trap(), but I can't confirm these do what I want (some sources, including documentation, claim they either cause a core dump or send a SIGILL instead). // I've also heard of some new clang intrinsics, __builtin_debugtrap(), but this is totally undocumented and the original patch for this suggested it would be identical to __builtin_trap(), so... // Also I cannot figure out how to manually raise EXC_BREAKPOINT. // So that leaves us with one option: just use Debugger(), turning off the deprecation warnings. // Also, while we could turn off the deprecation warning temporarily in gcc >= 4.6 and any clang, I'm not sure what minimum version of gcc we need, and I'm not sure if "any clang" is even accurate. // So instead of juggling version macros, just turn off deprecation warnings at the bottom of this file. // References: // - https://stackoverflow.com/questions/37299/xcode-equivalent-of-asm-int-3-debugbreak-halt // - https://stackoverflow.com/questions/2622017/suppressing-deprecated-warnings-in-xcode // - https://stackoverflow.com/questions/28166565/detect-gcc-as-opposed-to-msvc-clang-with-macro // - https://stackoverflow.com/questions/16555585/why-pragma-gcc-diagnostic-push-pop-warning-in-gcc-c // - possibly others, all on stackoverflow.com (and maybe once on Apple's own forums?); I forget now static void debugBreak(void); void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal) { NSExceptionName exceptionName; NSLog(@"*** %s: %s. %s", prefix, msg, suffix); exceptionName = NSInternalInconsistencyException; if (!internal) // TODO either find an appropriate exception for each possible message or use a custom exception name exceptionName = NSInvalidArgumentException; [NSException raise:exceptionName format:@"%s: %s", prefix, msg]; debugBreak(); abort(); // we shouldn't reach here } #ifdef __clang__ #pragma clang diagnostic ignored "-Wdeprecated-declarations" #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif static void debugBreak(void) { Debugger(); } // DO NOT ADD NEW CODE HERE. IT WILL NOT BE SUBJECT TO DEPRECATION WARNINGS. // I am making an exception here with Debugger(); see the large comment above.