diff options
Diffstat (limited to 'src/platform/cocoa')
| -rw-r--r-- | src/platform/cocoa/desktop.m | 145 | ||||
| -rw-r--r-- | src/platform/cocoa/meson.build | 2 | ||||
| -rw-r--r-- | src/platform/cocoa/window.m | 10 |
3 files changed, 156 insertions, 1 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); +} diff --git a/src/platform/cocoa/meson.build b/src/platform/cocoa/meson.build index 8230376..5fc5f5e 100644 --- a/src/platform/cocoa/meson.build +++ b/src/platform/cocoa/meson.build @@ -1 +1 @@ -sources += files('window.m', 'keymap.c') +sources += files('window.m', 'desktop.m', 'keymap.c') diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m index 811a7c6..75dce54 100644 --- a/src/platform/cocoa/window.m +++ b/src/platform/cocoa/window.m @@ -1366,6 +1366,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya darwin_ctx.cbs = &darwin_ctx.base.callbacks; darwin_ctx.ud = spec->ud; if (spec->app_id) darwin_ctx.app_id = strdup(spec->app_id); + darwin_ctx.base.app_id = darwin_ctx.app_id; } return &darwin_ctx; } @@ -2191,6 +2192,14 @@ static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at); } +static bool impl_surface_bell(struct yasl_surface *s) +{ + assert([NSThread isMainThread]); + (void)s; + NSBeep(); + return true; +} + static void *impl_display_get_native_handle(struct yasl_display *d) { return d ? (void *)NSApp : NULL; @@ -2249,6 +2258,7 @@ const struct yasl_backend_ops yasl_cocoa_backend = { .surface_show_window_menu = impl_surface_show_window_menu, .surface_drag_carry = impl_surface_drag_carry, .surface_place_under_pointer = impl_surface_place_under_pointer, + .surface_bell = impl_surface_bell, .display_get_native_handle = impl_display_get_native_handle, .surface_get_native_handle = impl_surface_get_native_handle, .surface_get_native_frame = impl_surface_get_native_frame, |
