144 lines
5.7 KiB
Objective-C
144 lines
5.7 KiB
Objective-C
// 20 april 2019
|
|
#import "uipriv_darwin.h"
|
|
|
|
@interface uiprivApplication : NSApplication
|
|
@end
|
|
|
|
@implementation uiprivApplication
|
|
@end
|
|
|
|
@interface uiprivApplicationDelegate : NSObject<NSApplicationDelegate>
|
|
@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
|
|
|
|
static pthread_t mainThread;
|
|
|
|
bool uiprivSysInit(void *options, uiInitError *err)
|
|
{
|
|
uiprivApp = [uiprivApplication sharedApplication];
|
|
if (![NSApp isKindOfClass:[uiprivApplication class]])
|
|
// TODO verify if Info.plist (both in a proper .app bundle and embedded into a standalone binary) can override this (or if that only happens if you use NSApplicationMain() instead), and, if so, add that tidbit to this message
|
|
return uiprivInitReturnErrorf(err, "NSApp is not of type uiprivApplication; was likely already initialized beforehand");
|
|
|
|
// 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];
|
|
|
|
mainThread = pthread_self();
|
|
return true;
|
|
}
|
|
|
|
void uiMain(void)
|
|
{
|
|
if (!uiprivCheckInitializedAndThread())
|
|
return;
|
|
[uiprivApp run];
|
|
}
|
|
|
|
void uiQuit(void)
|
|
{
|
|
if (!uiprivCheckInitializedAndThread())
|
|
return;
|
|
@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; if we can't, set up data1 and data2 so that we can intercept and discard this event if it was accidentally triggered
|
|
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 uiprivSysQueueMain(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);
|
|
}
|
|
|
|
bool uiprivSysCheckThread(void)
|
|
{
|
|
return pthread_equal(pthread_self(), mainThread);
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
#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.
|