diff options
Diffstat (limited to 'src/platform/cocoa/desktop.m')
| -rw-r--r-- | src/platform/cocoa/desktop.m | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/src/platform/cocoa/desktop.m b/src/platform/cocoa/desktop.m new file mode 100644 index 0000000..526cbb9 --- /dev/null +++ b/src/platform/cocoa/desktop.m @@ -0,0 +1,145 @@ +#import <AppKit/AppKit.h> +#import <UserNotifications/UserNotifications.h> + +#include "common/desktop.h" +#include "yasl/desktop.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <limits.h> +#include <mach-o/dyld.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static size_t copy_str(char *buf, size_t cap, const char *s) +{ + size_t len = s ? strlen(s) : 0; + if (!len || len >= cap) return 0; + memcpy(buf, s, len + 1); + return len; +} + +bool desktop_open_uri(const char *uri) +{ + @autoreleasepool { + NSString *str = [NSString stringWithUTF8String:uri]; + NSURL *url = str ? [NSURL URLWithString:str] : nil; + return url && [[NSWorkspace sharedWorkspace] openURL:url]; + } +} + +/* Show banners while the application is in front, too. */ +@interface YaslNotificationDelegate : NSObject <UNUserNotificationCenterDelegate> +@end + +@implementation YaslNotificationDelegate +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + willPresentNotification:(UNNotification *)notification + withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler +{ + (void)center; + (void)notification; + completionHandler(UNNotificationPresentationOptionBanner); +} +@end + +static YaslNotificationDelegate *notification_delegate; +static bool authorization_requested; + +/* UNUserNotificationCenter raises an exception outside an app bundle. */ +static bool notifications_available(void) +{ + NSBundle *bundle = [NSBundle mainBundle]; + return bundle.bundleIdentifier && [bundle.bundlePath.pathExtension isEqualToString:@"app"]; +} + +bool desktop_notify(const char *app_id, const char *title, const char *body) +{ + (void)app_id; + if (!notifications_available()) return false; + @autoreleasepool { + NSString *heading = [NSString stringWithUTF8String:title]; + NSString *message = [NSString stringWithUTF8String:body]; + if (!heading || !message) return false; + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + if (!notification_delegate) { + notification_delegate = [[YaslNotificationDelegate alloc] init]; + center.delegate = notification_delegate; + } + if (!authorization_requested) { + authorization_requested = true; + [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert + completionHandler:^(BOOL granted, NSError *error) { + (void)granted; + (void)error; + }]; + } + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.title = heading; + content.body = message; + UNNotificationRequest *request = + [UNNotificationRequest requestWithIdentifier:[[NSUUID UUID] UUIDString] + content:content + trigger:nil]; + [content release]; + [center addNotificationRequest:request withCompletionHandler:nil]; + return true; + } +} + +uint32_t desktop_appearance(void) +{ + @autoreleasepool { + uint32_t flags = 0; + NSAppearanceName name = [NSApp.effectiveAppearance + bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]]; + if ([name isEqualToString:NSAppearanceNameDarkAqua]) + flags |= YASL_APPEARANCE_DARK; + else if (name) + flags |= YASL_APPEARANCE_LIGHT; + if ([NSWorkspace sharedWorkspace].accessibilityDisplayShouldIncreaseContrast) + flags |= YASL_APPEARANCE_HIGH_CONTRAST; + return flags; + } +} + +size_t desktop_executable_path(char *buf, size_t cap) +{ + char raw[PATH_MAX]; + uint32_t size = sizeof(raw); + char resolved[PATH_MAX]; + if (_NSGetExecutablePath(raw, &size) != 0 || !realpath(raw, resolved)) return 0; + return copy_str(buf, cap, resolved); +} + +size_t desktop_resource_dir(char *buf, size_t cap) +{ + @autoreleasepool { + NSBundle *bundle = [NSBundle mainBundle]; + if (!bundle.bundleIdentifier || ![bundle.bundlePath.pathExtension isEqualToString:@"app"]) + return 0; + NSString *path = bundle.resourcePath; + return path ? copy_str(buf, cap, path.fileSystemRepresentation) : 0; + } +} + +size_t desktop_default_locale(char *buf, size_t cap) +{ + /* Identifiers such as "zh-Hans_CN" are not POSIX names, so build the name + * from the language and region codes alone. */ + char name[64] = "en_US.UTF-8"; + CFLocaleRef locale = CFLocaleCopyCurrent(); + if (locale) { + CFStringRef language = CFLocaleGetValue(locale, kCFLocaleLanguageCode); + CFStringRef region = CFLocaleGetValue(locale, kCFLocaleCountryCode); + char lang[16], reg[16]; + if (language && CFStringGetCString(language, lang, sizeof(lang), kCFStringEncodingUTF8) && lang[0]) { + if (region && CFStringGetCString(region, reg, sizeof(reg), kCFStringEncodingUTF8) && reg[0]) + snprintf(name, sizeof(name), "%s_%s.UTF-8", lang, reg); + else + snprintf(name, sizeof(name), "%s.UTF-8", lang); + } + CFRelease(locale); + } + return copy_str(buf, cap, name); +} |
