#import #import #include "common/desktop.h" #include "yasl/desktop.h" #include #include #include #include #include #include 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 @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); }